UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

613 lines (534 loc) 19.9 kB
/** * The `Matter.Common` module contains utility functions that are common to all modules. * * @class Common */ var Common = {}; module.exports = Common; (function() { Common._baseDelta = 1000 / 60; Common._nextId = 0; Common._seed = 0; Common._nowStartTime = +(new Date()); Common._warnedOnce = {}; Common._decomp = null; /** * Extends the object in the first argument using the object in the second argument. * @method extend * @param {} obj * @param {boolean} deep * @return {} obj extended */ Common.extend = function(obj, deep) { var argsStart, args, deepClone; if (typeof deep === 'boolean') { argsStart = 2; deepClone = deep; } else { argsStart = 1; deepClone = true; } for (var i = argsStart; i < arguments.length; i++) { var source = arguments[i]; if (source) { for (var prop in source) { if (deepClone && source[prop] && source[prop].constructor === Object) { if (!obj[prop] || obj[prop].constructor === Object) { obj[prop] = obj[prop] || {}; Common.extend(obj[prop], deepClone, source[prop]); } else { obj[prop] = source[prop]; } } else { obj[prop] = source[prop]; } } } } return obj; }; /** * Creates a new clone of the object, if deep is true references will also be cloned. * @method clone * @param {} obj * @param {bool} deep * @return {} obj cloned */ Common.clone = function(obj, deep) { return Common.extend({}, deep, obj); }; /** * Returns the list of keys for the given object. * @method keys * @param {} obj * @return {string[]} keys */ Common.keys = function(obj) { if (Object.keys) return Object.keys(obj); // avoid hasOwnProperty for performance var keys = []; for (var key in obj) keys.push(key); return keys; }; /** * Returns the list of values for the given object. * @method values * @param {} obj * @return {array} Array of the objects property values */ Common.values = function(obj) { var values = []; if (Object.keys) { var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { values.push(obj[keys[i]]); } return values; } // avoid hasOwnProperty for performance for (var key in obj) values.push(obj[key]); return values; }; /** * Gets a value from `base` relative to the `path` string. * @method get * @param {} obj The base object * @param {string} path The path relative to `base`, e.g. 'Foo.Bar.baz' * @param {number} [begin] Path slice begin * @param {number} [end] Path slice end * @return {} The object at the given path */ Common.get = function(obj, path, begin, end) { path = path.split('.').slice(begin, end); for (var i = 0; i < path.length; i += 1) { obj = obj[path[i]]; } return obj; }; /** * Sets a value on `base` relative to the given `path` string. * @method set * @param {} obj The base object * @param {string} path The path relative to `base`, e.g. 'Foo.Bar.baz' * @param {} val The value to set * @param {number} [begin] Path slice begin * @param {number} [end] Path slice end * @return {} Pass through `val` for chaining */ Common.set = function(obj, path, val, begin, end) { var parts = path.split('.').slice(begin, end); Common.get(obj, path, 0, -1)[parts[parts.length - 1]] = val; return val; }; /** * Shuffles the given array in-place. * The function uses a seeded random generator. * @method shuffle * @param {array} array * @return {array} array shuffled randomly */ Common.shuffle = function(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Common.random() * (i + 1)); var temp = array[i]; array[i] = array[j]; array[j] = temp; } return array; }; /** * Randomly chooses a value from a list with equal probability. * The function uses a seeded random generator. * @method choose * @param {array} choices * @return {object} A random choice object from the array */ Common.choose = function(choices) { return choices[Math.floor(Common.random() * choices.length)]; }; /** * Returns true if the object is a HTMLElement, otherwise false. * @method isElement * @param {object} obj * @return {boolean} True if the object is a HTMLElement, otherwise false */ Common.isElement = function(obj) { if (typeof HTMLElement !== 'undefined') { return obj instanceof HTMLElement; } return !!(obj && obj.nodeType && obj.nodeName); }; /** * Returns true if the object is an array. * @method isArray * @param {object} obj * @return {boolean} True if the object is an array, otherwise false */ Common.isArray = function(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; /** * Returns true if the object is a function. * @method isFunction * @param {object} obj * @return {boolean} True if the object is a function, otherwise false */ Common.isFunction = function(obj) { return typeof obj === "function"; }; /** * Returns true if the object is a plain object. * @method isPlainObject * @param {object} obj * @return {boolean} True if the object is a plain object, otherwise false */ Common.isPlainObject = function(obj) { return typeof obj === 'object' && obj.constructor === Object; }; /** * Returns true if the object is a string. * @method isString * @param {object} obj * @return {boolean} True if the object is a string, otherwise false */ Common.isString = function(obj) { return toString.call(obj) === '[object String]'; }; /** * Returns the given value clamped between a minimum and maximum value. * @method clamp * @param {number} value * @param {number} min * @param {number} max * @return {number} The value clamped between min and max inclusive */ Common.clamp = function(value, min, max) { if (value < min) return min; if (value > max) return max; return value; }; /** * Returns the sign of the given value. * @method sign * @param {number} value * @return {number} -1 if negative, +1 if 0 or positive */ Common.sign = function(value) { return value < 0 ? -1 : 1; }; /** * Returns the current timestamp since the time origin (e.g. from page load). * The result is in milliseconds and will use high-resolution timing if available. * @method now * @return {number} the current timestamp in milliseconds */ Common.now = function() { if (typeof window !== 'undefined' && window.performance) { if (window.performance.now) { return window.performance.now(); } else if (window.performance.webkitNow) { return window.performance.webkitNow(); } } if (Date.now) { return Date.now(); } return (new Date()) - Common._nowStartTime; }; /** * Returns a random value between a minimum and a maximum value inclusive. * The function uses a seeded random generator. * @method random * @param {number} min * @param {number} max * @return {number} A random number between min and max inclusive */ Common.random = function(min, max) { min = (typeof min !== "undefined") ? min : 0; max = (typeof max !== "undefined") ? max : 1; return min + _seededRandom() * (max - min); }; var _seededRandom = function() { // https://en.wikipedia.org/wiki/Linear_congruential_generator Common._seed = (Common._seed * 9301 + 49297) % 233280; return Common._seed / 233280; }; /** * Converts a CSS hex colour string into an integer. * @method colorToNumber * @param {string} colorString * @return {number} An integer representing the CSS hex string */ Common.colorToNumber = function(colorString) { colorString = colorString.replace('#',''); if (colorString.length == 3) { colorString = colorString.charAt(0) + colorString.charAt(0) + colorString.charAt(1) + colorString.charAt(1) + colorString.charAt(2) + colorString.charAt(2); } return parseInt(colorString, 16); }; /** * The console logging level to use, where each level includes all levels above and excludes the levels below. * The default level is 'debug' which shows all console messages. * * Possible level values are: * - 0 = None * - 1 = Debug * - 2 = Info * - 3 = Warn * - 4 = Error * @property Common.logLevel * @type {Number} * @default 1 */ Common.logLevel = 1; /** * Shows a `console.log` message only if the current `Common.logLevel` allows it. * The message will be prefixed with 'matter-js' to make it easily identifiable. * @method log * @param ...objs {} The objects to log. */ Common.log = function() { if (console && Common.logLevel > 0 && Common.logLevel <= 3) { console.log.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); } }; /** * Shows a `console.info` message only if the current `Common.logLevel` allows it. * The message will be prefixed with 'matter-js' to make it easily identifiable. * @method info * @param ...objs {} The objects to log. */ Common.info = function() { if (console && Common.logLevel > 0 && Common.logLevel <= 2) { console.info.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); } }; /** * Shows a `console.warn` message only if the current `Common.logLevel` allows it. * The message will be prefixed with 'matter-js' to make it easily identifiable. * @method warn * @param ...objs {} The objects to log. */ Common.warn = function() { if (console && Common.logLevel > 0 && Common.logLevel <= 3) { console.warn.apply(console, ['matter-js:'].concat(Array.prototype.slice.call(arguments))); } }; /** * Uses `Common.warn` to log the given message one time only. * @method warnOnce * @param ...objs {} The objects to log. */ Common.warnOnce = function() { var message = Array.prototype.slice.call(arguments).join(' '); if (!Common._warnedOnce[message]) { Common.warn(message); Common._warnedOnce[message] = true; } }; /** * Shows a deprecated console warning when the function on the given object is called. * The target function will be replaced with a new function that first shows the warning * and then calls the original function. * @method deprecated * @param {object} obj The object or module * @param {string} name The property name of the function on obj * @param {string} warning The one-time message to show if the function is called */ Common.deprecated = function(obj, prop, warning) { obj[prop] = Common.chain(function() { Common.warnOnce('🔅 deprecated 🔅', warning); }, obj[prop]); }; /** * Returns the next unique sequential ID. * @method nextId * @return {Number} Unique sequential ID */ Common.nextId = function() { return Common._nextId++; }; /** * A cross browser compatible indexOf implementation. * @method indexOf * @param {array} haystack * @param {object} needle * @return {number} The position of needle in haystack, otherwise -1. */ Common.indexOf = function(haystack, needle) { if (haystack.indexOf) return haystack.indexOf(needle); for (var i = 0; i < haystack.length; i++) { if (haystack[i] === needle) return i; } return -1; }; /** * A cross browser compatible array map implementation. * @method map * @param {array} list * @param {function} func * @return {array} Values from list transformed by func. */ Common.map = function(list, func) { if (list.map) { return list.map(func); } var mapped = []; for (var i = 0; i < list.length; i += 1) { mapped.push(func(list[i])); } return mapped; }; /** * Takes a directed graph and returns the partially ordered set of vertices in topological order. * Circular dependencies are allowed. * @method topologicalSort * @param {object} graph * @return {array} Partially ordered set of vertices in topological order. */ Common.topologicalSort = function(graph) { // https://github.com/mgechev/javascript-algorithms // Copyright (c) Minko Gechev (MIT license) // Modifications: tidy formatting and naming var result = [], visited = [], temp = []; for (var node in graph) { if (!visited[node] && !temp[node]) { Common._topologicalSort(node, visited, temp, graph, result); } } return result; }; Common._topologicalSort = function(node, visited, temp, graph, result) { var neighbors = graph[node] || []; temp[node] = true; for (var i = 0; i < neighbors.length; i += 1) { var neighbor = neighbors[i]; if (temp[neighbor]) { // skip circular dependencies continue; } if (!visited[neighbor]) { Common._topologicalSort(neighbor, visited, temp, graph, result); } } temp[node] = false; visited[node] = true; result.push(node); }; /** * Takes _n_ functions as arguments and returns a new function that calls them in order. * The arguments applied when calling the new function will also be applied to every function passed. * The value of `this` refers to the last value returned in the chain that was not `undefined`. * Therefore if a passed function does not return a value, the previously returned value is maintained. * After all passed functions have been called the new function returns the last returned value (if any). * If any of the passed functions are a chain, then the chain will be flattened. * @method chain * @param ...funcs {function} The functions to chain. * @return {function} A new function that calls the passed functions in order. */ Common.chain = function() { var funcs = []; for (var i = 0; i < arguments.length; i += 1) { var func = arguments[i]; if (func._chained) { // flatten already chained functions funcs.push.apply(funcs, func._chained); } else { funcs.push(func); } } var chain = function() { // https://github.com/GoogleChrome/devtools-docs/issues/53#issuecomment-51941358 var lastResult, args = new Array(arguments.length); for (var i = 0, l = arguments.length; i < l; i++) { args[i] = arguments[i]; } for (i = 0; i < funcs.length; i += 1) { var result = funcs[i].apply(lastResult, args); if (typeof result !== 'undefined') { lastResult = result; } } return lastResult; }; chain._chained = funcs; return chain; }; /** * Chains a function to excute before the original function on the given `path` relative to `base`. * See also docs for `Common.chain`. * @method chainPathBefore * @param {} base The base object * @param {string} path The path relative to `base` * @param {function} func The function to chain before the original * @return {function} The chained function that replaced the original */ Common.chainPathBefore = function(base, path, func) { return Common.set(base, path, Common.chain( func, Common.get(base, path) )); }; /** * Chains a function to excute after the original function on the given `path` relative to `base`. * See also docs for `Common.chain`. * @method chainPathAfter * @param {} base The base object * @param {string} path The path relative to `base` * @param {function} func The function to chain after the original * @return {function} The chained function that replaced the original */ Common.chainPathAfter = function(base, path, func) { return Common.set(base, path, Common.chain( Common.get(base, path), func )); }; /** * Provide the [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module to enable * concave vertex decomposition support when using `Bodies.fromVertices` e.g. `Common.setDecomp(require('poly-decomp'))`. * @method setDecomp * @param {} decomp The [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module. */ Common.setDecomp = function(decomp) { Common._decomp = decomp; }; /** * Returns the [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module provided through `Common.setDecomp`, * otherwise returns the global `decomp` if set. * @method getDecomp * @return {} The [poly-decomp](https://github.com/schteppe/poly-decomp.js) library module if provided. */ Common.getDecomp = function() { // get user provided decomp if set var decomp = Common._decomp; try { // otherwise from window global if (!decomp && typeof window !== 'undefined') { decomp = window.decomp; } // otherwise from node global if (!decomp && typeof global !== 'undefined') { decomp = global.decomp; } } catch (e) { // decomp not available decomp = null; } return decomp; }; })();