utils/Util.js

(function(Proton, undefined) {

    /**
     * @namespace
     * @memberof! Proton#
     * @alias Proton.Util
     */
    var Util = Util || {

        /**
         * Returns the default if the value is null or undefined
         *
         * @memberof Proton#Proton.Util
         * @method initValue
         *
         * @param {Mixed} value a specific value, could be everything but null or undefined
         * @param {Mixed} defaults the default if the value is null or undefined
         */
        initValue: function(value, defaults) {
            var value = (value != null && value != undefined) ? value : defaults;
            return value;
        },

        /**
         * Checks if the value is a valid array
         *
         * @memberof Proton#Proton.Util
         * @method isArray
         *
         * @param {Array} value Any array
         *
         * @returns {Boolean} 
         */
        isArray: function(value) {
            return typeof value === 'object' && value.hasOwnProperty('length');
        },

        /**
         * Destroyes the given array
         *
         * @memberof Proton#Proton.Util
         * @method destroyArray
         *
         * @param {Array} array Any array
         */
        destroyArray: function(array) {
            array.length = 0;
        },

        /**
         * Destroyes the given object
         *
         * @memberof Proton#Proton.Util
         * @method destroyObject
         *
         * @param {Object} obj Any object
         */
        destroyObject: function(obj) {
            for (var o in obj)
                delete obj[o];
        },

        /**
         * Returns the Vector2D - or creates a new one
         *
         * @memberof Proton#Proton.Util
         * @method getVector2D
         *
         * @param {Proton.Vector2D | Number} postionOrX
         * @param {Number} [y] just valid if 'postionOrX' is not an object
         *
         * @return {Proton.Vector2D}
         */
        getVector2D: function(postionOrX, y) {
            if (typeof(postionOrX) == 'object') {
                return postionOrX;
            } else {
                var vector2d = new Proton.Vector2D(postionOrX, y);
                return vector2d;
            }
        },

        /**
         * Makes an instance of a class and binds the given array
         *
         * @memberof Proton#Proton.Util
         * @method classApply
         *
         * @param {Function} constructor A class to make an instance from
         * @param {Array} [argArray] Any array to bind it to the constructor
         *
         * @return {Object} The instance of constructor, optionally bind with argArray
         */
        classApply: function(constructor, argArray) {
            if (!argArray) return new constructor;

            var args = [null].concat(argArray);
            var factoryFunction = constructor.bind.apply(constructor, args);
            return new factoryFunction();
        },

        /**
         * @memberof Proton#Proton.Util
         * @method judgeVector2D
         *
         * @todo add description for param `pOBJ`
         * @todo add description for function
         *
         * @param {Object} pOBJ
         *
         * @return {String} result
         */
        judgeVector2D: function(pOBJ) {
            var result = '';
            if (pOBJ.hasOwnProperty('x') || pOBJ.hasOwnProperty('y') || pOBJ.hasOwnProperty('p') || pOBJ.hasOwnProperty('position'))
                result += 'p';
            if (pOBJ.hasOwnProperty('vx') || pOBJ.hasOwnProperty('vx') || pOBJ.hasOwnProperty('v') || pOBJ.hasOwnProperty('velocity'))
                result += 'v';
            if (pOBJ.hasOwnProperty('ax') || pOBJ.hasOwnProperty('ax') || pOBJ.hasOwnProperty('a') || pOBJ.hasOwnProperty('accelerate'))
                result += 'a';

            return result;
        },

        /**
         * @memberof Proton#Proton.Util
         * @method setVector2DByObject
         *
         * @todo add description for param `target`
         * @todo add description for param `pOBJ`
         * @todo add description for function
         *
         * @param {Object} target
         * @param {Object} pOBJ
         */
        setVector2DByObject: function(target, pOBJ) {
            if (pOBJ.hasOwnProperty('x'))
                target.p.x = pOBJ['x'];

            if (pOBJ.hasOwnProperty('y'))
                target.p.y = pOBJ['y'];

            if (pOBJ.hasOwnProperty('vx'))
                target.v.x = pOBJ['vx'];

            if (pOBJ.hasOwnProperty('vy'))
                target.v.y = pOBJ['vy'];

            if (pOBJ.hasOwnProperty('ax'))
                target.a.x = pOBJ['ax'];

            if (pOBJ.hasOwnProperty('ay'))
                target.a.y = pOBJ['ay'];

            if (pOBJ.hasOwnProperty('p'))
                particle.p.copy(pOBJ['p']);

            if (pOBJ.hasOwnProperty('v'))
                particle.v.copy(pOBJ['v']);

            if (pOBJ.hasOwnProperty('a'))
                particle.a.copy(pOBJ['a']);

            if (pOBJ.hasOwnProperty('position'))
                particle.p.copy(pOBJ['position']);

            if (pOBJ.hasOwnProperty('velocity'))
                particle.v.copy(pOBJ['velocity']);

            if (pOBJ.hasOwnProperty('accelerate'))
                particle.a.copy(pOBJ['accelerate']);
        },

        /**
         * 强行添加属性
         *
         * @memberof Proton#Proton.Util
         * @method addPrototypeByObject
         *
         * @todo add description for param `target`
         * @todo add description for param `filters`
         * @todo translate desription from chinese to english
         *
         * @param {Object} target
         * @param {Object} prototypeObject An object of single prototypes
         * @param {Object} filters
         *
         * @return {Object} target
         */
        addPrototypeByObject: function(target, prototypeObject, filters) {
            for (var singlePrototype in prototypeObject) {
                if (filters) {
                    if (filters.indexOf(singlePrototype) < 0)
                        target[singlePrototype] = Proton.Util.getSpanValue(prototypeObject[singlePrototype]);
                } else {
                    target[singlePrototype] = Proton.Util.getSpanValue(prototypeObject[singlePrototype]);
                }
            }

            return target;
        },

        /**
         * set the prototype in a given prototypeObject
         *
         * @memberof Proton#Proton.Util
         * @method setPrototypeByObject
         *
         * @todo add description for param `target`
         * @todo add description for param `filters`
         * @todo translate desription from chinese to english
         *
         * @param {Object} target
         * @param {Object} prototypeObject An object of single prototypes
         * @param {Object} filters
         *
         * @return {Object} target
         */
        setPrototypeByObject: function(target, prototypeObject, filters) {
            for (var singlePrototype in prototypeObject) {
                if (target.hasOwnProperty(singlePrototype)) {
                    if (filters) {
                        if (filters.indexOf(singlePrototype) < 0)
                            target[singlePrototype] = Proton.Util.getSpanValue(prototypeObject[singlePrototype]);
                    } else {
                        target[singlePrototype] = Proton.Util.getSpanValue(prototypeObject[singlePrototype]);
                    }
                }
            }

            return target;
        },

        /**
         * Returns a new Proton.Span object
         *
         * @memberof Proton#Proton.Util
         * @method setSpanValue
         *
         * @todo a, b and c should be 'Mixed' or 'Number'?
         *
         * @param {Mixed | Proton.Span} a
         * @param {Mixed}               b
         * @param {Mixed}               c
         *
         * @return {Proton.Span}
         */
        setSpanValue: function(a, b, c) {
            if (a instanceof Proton.Span) {
                return a;
            } else {
                if (!b) {
                    return new Proton.Span(a);
                } else {
                    if (!c)
                        return new Proton.Span(a, b);
                    else
                        return new Proton.Span(a, b, c);
                }
            }
        },

        /**
         * Returns the value from a Proton.Span, if the param is not a Proton.Span it will return the given parameter
         *
         * @memberof Proton#Proton.Util
         * @method getSpanValue
         *
         * @param {Mixed | Proton.Span} pan
         *
         * @return {Mixed} the value of Proton.Span OR the parameter if it is not a Proton.Span
         */
        getSpanValue: function(pan) {
            if (pan instanceof Proton.Span)
                return pan.getValue();
            else
                return pan;
        },

        /**
         * Inherits any class from the superclass. Acts like 'extends' in Java
         *
         * @memberof Proton#Proton.Util
         * @method inherits
         *
         * @param {Object} subClass     the child class
         * @param {Object} superClass   the parent/super class
         */
        inherits: function(subClass, superClass) {
            subClass._super_ = superClass;
            if (Object['create']) {
                //console.log(subClass,superClass);
                subClass.prototype = Object.create(superClass.prototype, {
                    constructor: {
                        value: subClass
                    }
                });
            } else {
                var F = function() {};
                F.prototype = superClass.prototype;
                subClass.prototype = new F();
                subClass.prototype.constructor = subClass;
            }
        },

        /**
         * This will get the image data. It could be necessary to create a Proton.Zone.
         *
         * @memberof Proton#Proton.Util
         * @method getImageData
         *
         * @param {HTMLCanvasElement}   context any canvas, must be a 2dContext 'canvas.getContext('2d')'
         * @param {Object}              image   could be any dom image, e.g. document.getElementById('thisIsAnImgTag');
         * @param {Proton.Rectangle}    rect
         */
        getImageData: function(context, image, rect) {
            context.drawImage(image, rect.x, rect.y);
            var imagedata = context.getImageData(rect.x, rect.y, rect.width, rect.height);
            context.clearRect(rect.x, rect.y, rect.width, rect.height);
            return imagedata;
        },

        /**
         * @memberof Proton#Proton.Util
         * @method getImage
         *
         * @todo add description
         * @todo describe fun
         *
         * @param {Mixed}               img
         * @param {Proton.Particle}     particle
         * @param {Boolean}             drawCanvas  set to true if a canvas should be saved into particle.transform.canvas
         * @param {Boolean}             fun
         */
        getImage: function(img, particle, drawCanvas, fun) {
            if (typeof(img) == 'string') {
                this.loadAndSetImage(img, particle, drawCanvas, fun);
            } else if (typeof(img) == 'object') {
                this.loadAndSetImage(img.src, particle, drawCanvas, fun);
            } else if (img instanceof Image) {
                this.loadedImage(img.src, particle, drawCanvas, fun, img);
            }
        },

        /**
         * @memberof Proton#Proton.Util
         * @method loadedImage
         *
         * @todo add description
         * @todo describe fun
         * @todo describe target
         *
         * @param {String}              src         the src of an img-tag
         * @param {Proton.Particle}     particle
         * @param {Boolean}             drawCanvas  set to true if a canvas should be saved into particle.transform.canvas
         * @param {Boolean}             fun
         * @param {Object}              target
         */
        loadedImage: function(src, particle, drawCanvas, fun, target) {
            particle.target = target;
            particle.transform.src = src;
            if (!Proton.TextureBuffer[src])
                Proton.TextureBuffer[src] = particle.target;
            if (drawCanvas) {
                if (Proton.TextureCanvasBuffer[src]) {
                    particle.transform.canvas = Proton.TextureCanvasBuffer[src];
                } else {
                    var _width = Proton.WebGLUtil.nhpot(particle.target.width);
                    var _height = Proton.WebGLUtil.nhpot(particle.target.height);
                    particle.transform.canvas = Proton.DomUtil.createCanvas('canvas' + src, _width, _height);
                    var context = particle.transform.canvas.getContext('2d');
                    context.drawImage(particle.target, 0, 0, particle.target.width, particle.target.height);
                    Proton.TextureCanvasBuffer[src] = particle.transform.canvas;
                }
            }
            if (fun)
                fun(particle);
        },

        /**
         * @memberof Proton#Proton.Util
         * @method loadAndSetImage
         *
         * @todo add description
         * @todo describe fun
         *
         * @param {String}              src         the src of an img-tag
         * @param {Proton.Particle}     particle
         * @param {Boolean}             drawCanvas  set to true if a canvas should be saved into particle.transform.canvas
         * @param {Boolean}             fun
         */
        loadAndSetImage: function(src, particle, drawCanvas, fun) {
            if (Proton.TextureBuffer[src]) {
                this.loadedImage(src, particle, drawCanvas, fun, Proton.TextureBuffer[src]);
            } else {
                var self = this;
                var myImage = new Image();
                myImage.onload = function(e) {
                    self.loadedImage(src, particle, drawCanvas, fun, e.target);
                }
                myImage.src = src;
            }
        },

        /**
         * @typedef  {Object} rgbObject
         * @property {Number} r red value
         * @property {Number} g green value
         * @property {Number} b blue value
         */
        /**
         * converts a hex value to a rgb object
         *
         * @memberof Proton#Proton.Util
         * @method hexToRGB
         *
         * @param {String} h any hex value, e.g. #000000 or 000000 for black
         *
         * @return {rgbObject}
         */
        hexToRGB: function(h) {
            var hex16 = (h.charAt(0) == "#") ? h.substring(1, 7) : h;
            var r = parseInt(hex16.substring(0, 2), 16);
            var g = parseInt(hex16.substring(2, 4), 16);
            var b = parseInt(hex16.substring(4, 6), 16);

            return {
                r: r,
                g: g,
                b: b
            }
        },

        /**
         * converts a rgb value to a rgb string
         *
         * @memberof Proton#Proton.Util
         * @method rgbToHex
         *
         * @param {Object | Proton.hexToRGB} rgb a rgb object like in {@link Proton#Proton.Util.hexToRGB}
         *
         * @return {String} rgb()
         */
        rgbToHex: function(rbg) {
            return 'rgb(' + rbg.r + ', ' + rbg.g + ', ' + rbg.b + ')';
        }
    };

    Proton.Util = Util;
})(Proton);


///bind
if (!Function.prototype.bind) {
    Function.prototype.bind = function(oThis) {
        if (typeof this !== "function") {
            // closest thing possible to the ECMAScript 5
            // internal IsCallable function
            throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
        }

        var aArgs = Array.prototype.slice.call(arguments, 1),
            fToBind = this,
            fNOP = function() {},
            fBound = function() {
                return fToBind.apply(this instanceof fNOP ? this : oThis || this,
                    aArgs.concat(Array.prototype.slice.call(arguments)));
            };

        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();

        return fBound;
    };
}