Phaser Source: src/utils/Utils.js

# Source: src/utils/Utils.js

    /**
    * @author       Richard Davey <rich@photonstorm.com>
    * @copyright    2016 Photon Storm Ltd.
    * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
    */

    /**
    * @class Phaser.Utils
    * @static
    */
    Phaser.Utils = {

        /**
        * Takes the given string and reverses it, returning the reversed string.
        * For example if given the string `Atari 520ST` it would return `TS025 iratA`.
        *
        * @method Phaser.Utils.reverseString
        * @param {string} string - The string to be reversed.
        * @return {string} The reversed string.
        */
        reverseString: function (string) {

            return string.split('').reverse().join('');

        },

        /**
         * Gets an objects property by string.
         *
         * @method Phaser.Utils.getProperty
         * @param {object} obj - The object to traverse.
         * @param {string} prop - The property whose value will be returned.
         * @return {*} the value of the property or null if property isn't found .
         */
        getProperty: function(obj, prop) {

            var parts = prop.split('.'),
                last = parts.pop(),
                l = parts.length,
                i = 1,
                current = parts[0];

            while (i < l && (obj = obj[current]))
            {
                current = parts[i];
                i++;
            }

            if (obj)
            {
                return obj[last];
            }
            else
            {
                return null;
            }

        },

        /**
         * Sets an objects property by string.
         *
         * @method Phaser.Utils.setProperty
         * @param {object} obj - The object to traverse
         * @param {string} prop - The property whose value will be changed
         * @return {object} The object on which the property was set.
         */
        setProperty: function(obj, prop, value) {

            var parts = prop.split('.'),
                last = parts.pop(),
                l = parts.length,
                i = 1,
                current = parts[0];

            while (i < l && (obj = obj[current]))
            {
                current = parts[i];
                i++;
            }

            if (obj)
            {
                obj[last] = value;
            }

            return obj;

        },

        /**
        * Generate a random bool result based on the chance value.
        *
        * Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance
        * of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed.
        *
        * @method Phaser.Utils#chanceRoll
        * @param {number} chance - The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%).
        * @return {boolean} True if the roll passed, or false otherwise.
        */
        chanceRoll: function (chance) {
            if (chance === undefined) { chance = 50; }
            return chance > 0 && (Math.random() * 100 <= chance);
        },

        /**
        * Choose between one of two values randomly.
        *
        * @method Phaser.Utils#randomChoice
        * @param {any} choice1
        * @param {any} choice2
        * @return {any} The randomly selected choice
        */
        randomChoice: function (choice1, choice2) {
            return (Math.random() < 0.5) ? choice1 : choice2;
        },

        /**
        * Get a unit dimension from a string.
        *
        * @method Phaser.Utils.parseDimension
        * @param {string|number} size - The size to parse.
        * @param {number} dimension - The window dimension to check.
        * @return {number} The parsed dimension.
        */
        parseDimension: function (size, dimension) {

            var f = 0;
            var px = 0;

            if (typeof size === 'string')
            {
                //  %?
                if (size.substr(-1) === '%')
                {
                    f = parseInt(size, 10) / 100;

                    if (dimension === 0)
                    {
                        px = window.innerWidth * f;
                    }
                    else
                    {
                        px = window.innerHeight * f;
                    }
                }
                else
                {
                    px = parseInt(size, 10);
                }
            }
            else
            {
                px = size;
            }

            return px;

        },

        /**
        * Takes the given string and pads it out, to the length required, using the character
        * specified. For example if you need a string to be 6 characters long, you can call:
        *
        * `pad('bob', 6, '-', 2)`
        *
        * This would return: `bob---` as it has padded it out to 6 characters, using the `-` on the right.
        *
        * You can also use it to pad numbers (they are always returned as strings):
        *
        * `pad(512, 6, '0', 1)`
        *
        * Would return: `000512` with the string padded to the left.
        *
        * If you don't specify a direction it'll pad to both sides:
        *
        * `pad('c64', 7, '*')`
        *
        * Would return: `**c64**`
        *
        * @method Phaser.Utils.pad
        * @param {string} str - The target string. `toString()` will be called on the string, which means you can also pass in common data types like numbers.
        * @param {integer} [len=0] - The number of characters to be added.
        * @param {string} [pad=" "] - The string to pad it out with (defaults to a space).
        * @param {integer} [dir=3] - The direction dir = 1 (left), 2 (right), 3 (both).
        * @return {string} The padded string.
        */
        pad: function (str, len, pad, dir) {

            if (len === undefined) { var len = 0; }
            if (pad === undefined) { var pad = ' '; }
            if (dir === undefined) { var dir = 3; }

            str = str.toString();

            var padlen = 0;

            if (len + 1 >= str.length)
            {
                switch (dir)
                {
                    case 1:
                        str = new Array(len + 1 - str.length).join(pad) + str;
                        break;

                    case 3:
                        var right = Math.ceil((padlen = len - str.length) / 2);
                        var left = padlen - right;
                        str = new Array(left+1).join(pad) + str + new Array(right+1).join(pad);
                        break;

                    default:
                        str = str + new Array(len + 1 - str.length).join(pad);
                        break;
                }
            }

            return str;

        },

        /**
        * This is a slightly modified version of jQuery.isPlainObject.
        * A plain object is an object whose internal class property is [object Object].
        * @method Phaser.Utils.isPlainObject
        * @param {object} obj - The object to inspect.
        * @return {boolean} - true if the object is plain, otherwise false.
        */
        isPlainObject: function (obj) {

            // Not plain objects:
            // - Any object or value whose internal [[Class]] property is not "[object Object]"
            // - DOM nodes
            // - window
            if (typeof(obj) !== "object" || obj.nodeType || obj === obj.window)
            {
                return false;
            }

            // Support: Firefox <20
            // The try/catch suppresses exceptions thrown when attempting to access
            // the "constructor" property of certain host objects, ie. |window.location|
            // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
            try {
                if (obj.constructor && !({}).hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf"))
                {
                    return false;
                }
            } catch (e) {
                return false;
            }

            // If the function hasn't returned already, we're confident that
            // |obj| is a plain object, created by {} or constructed with new Object
            return true;
        },

        /**
        * This is a slightly modified version of http://api.jquery.com/jQuery.extend/
        *
        * @method Phaser.Utils.extend
        * @param {boolean} deep - Perform a deep copy?
        * @param {object} target - The target object to copy to.
        * @return {object} The extended object.
        */
        extend: function () {

            var options, name, src, copy, copyIsArray, clone,
                target = arguments[0] || {},
                i = 1,
                length = arguments.length,
                deep = false;

            // Handle a deep copy situation
            if (typeof target === "boolean")
            {
                deep = target;
                target = arguments[1] || {};
                // skip the boolean and the target
                i = 2;
            }

            // extend Phaser if only one argument is passed
            if (length === i)
            {
                target = this;
                --i;
            }

            for (; i < length; i++)
            {
                // Only deal with non-null/undefined values
                if ((options = arguments[i]) != null)
                {
                    // Extend the base object
                    for (name in options)
                    {
                        src = target[name];
                        copy = options[name];

                        // Prevent never-ending loop
                        if (target === copy)
                        {
                            continue;
                        }

                        // Recurse if we're merging plain objects or arrays
                        if (deep && copy && (Phaser.Utils.isPlainObject(copy) || (copyIsArray = Array.isArray(copy))))
                        {
                            if (copyIsArray)
                            {
                                copyIsArray = false;
                                clone = src && Array.isArray(src) ? src : [];
                            }
                            else
                            {
                                clone = src && Phaser.Utils.isPlainObject(src) ? src : {};
                            }

                            // Never move original objects, clone them
                            target[name] = Phaser.Utils.extend(deep, clone, copy);

                        // Don't bring in undefined values
                        }
                        else if (copy !== undefined)
                        {
                            target[name] = copy;
                        }
                    }
                }
            }

            // Return the modified object
            return target;

        },

        /**
        * Mixes in an existing mixin object with the target.
        *
        * Values in the mixin that have either `get` or `set` functions are created as properties via `defineProperty`
        * _except_ if they also define a `clone` method - if a clone method is defined that is called instead and
        * the result is assigned directly.
        *
        * @method Phaser.Utils.mixinPrototype
        * @param {object} target - The target object to receive the new functions.
        * @param {object} mixin - The object to copy the functions from.
        * @param {boolean} [replace=false] - If the target object already has a matching function should it be overwritten or not?
        */
        mixinPrototype: function (target, mixin, replace) {

            if (replace === undefined) { replace = false; }

            var mixinKeys = Object.keys(mixin);

            for (var i = 0; i < mixinKeys.length; i++)
            {
                var key = mixinKeys[i];
                var value = mixin[key];

                if (!replace && (key in target))
                {
                    //  Not overwriting existing property
                    continue;
                }
                else
                {
                    if (value &&
                        (typeof value.get === 'function' || typeof value.set === 'function'))
                    {
                        //  Special case for classes like Phaser.Point which has a 'set' function!
                        if (typeof value.clone === 'function')
                        {
                            target[key] = value.clone();
                        }
                        else
                        {
                            Object.defineProperty(target, key, value);
                        }
                    }
                    else
                    {
                        target[key] = value;
                    }
                }
            }

        },

        /**
        * Mixes the source object into the destination object, returning the newly modified destination object.
        * Based on original code by @mudcube
        *
        * @method Phaser.Utils.mixin
        * @param {object} from - The object to copy (the source object).
        * @param {object} to - The object to copy to (the destination object).
        * @return {object} The modified destination object.
        */
        mixin: function (from, to) {

            if (!from || typeof (from) !== "object")
            {
                return to;
            }

            for (var key in from)
            {
                var o = from[key];

                if (o.childNodes || o.cloneNode)
                {
                    continue;
                }

                var type = typeof (from[key]);

                if (!from[key] || type !== "object")
                {
                    to[key] = from[key];
                }
                else
                {
                    //  Clone sub-object
                    if (typeof (to[key]) === type)
                    {
                        to[key] = Phaser.Utils.mixin(from[key], to[key]);
                    }
                    else
                    {
                        to[key] = Phaser.Utils.mixin(from[key], new o.constructor());
                    }
                }
            }

            return to;

        }

    };