web手势库AlloyFinger运用( 控制CANVAS中图片移动、缩放、旋转)

web手势库AlloyFinger(alloy_finger.js),针对多点触控设备编程的Web手势组件,快速帮助你的web程序增加手势支持

非常灵活省去了自己写相应操作的代码,就能控制元素移动、缩放、旋转了

http://alloyteam.github.io/AlloyFinger/example/canvas/此网址的案例就是控制CANVAS中多张图片操作的,此案例实现不仅引用了alloy_finger.js,另外引用了alloy_paper.js

不过我想要将文件域中上传的图片画入CANVAS中,且控制上传的图片移动、缩放、旋转有就问题,一是上传图片传递的url问题,二是只有在页面加载完成时画入的图片手势行为有效,延时设置画入图片或将上传的图片画入画布的,手势行为都会报错,于是改了下alloy_paper.js的代码

修改一(针对上传图片处理的):

1299行加了一个方法

复制以下方法

"loadRes": function(arr) {
this.resCount = arr.length;
for (var i = 0; i < arr.length; i++) {
var type=this._getTypeByExtension(arr[i].src.match(this.FILE_PATTERN)[5]);
if (type === "audio") {
this.loadAudio(arr[i].id, arr[i].src);
} else if (type === "js") {
this.loadScript(arr[i].src);
} else if (type === "img") {
this.loadImage(arr[i].id, arr[i].src);
}
}
},

改为

"loadRes2": function(arr) {
this.resCount = arr.length;
for (var i = 0; i < arr.length; i++) {
this.loadImage(arr[i].id, arr[i].src);
}
},

修改二(针对手势行为报错处理):

原3988行(改后3994行)注释取消(//this.adjustLayout();
this.adjustLayout();


案例:代言海报下载


结合插件实现的例子如下:

HTML代码部分:




    
    Canvas+AlloyFinger —— test
    
    










上传
图片
生成图片

alloy_finger.js代码:

/* AlloyFinger v0.1.2
 * By dntzhang
 * Github: https://github.com/AlloyTeam/AlloyFinger
 */
;(function() {
    function getLen(v) {
        return Math.sqrt(v.x * v.x + v.y * v.y);
    }

    function dot(v1, v2) {
        return v1.x * v2.x + v1.y * v2.y;
    }

    function getAngle(v1, v2) {
        var mr = getLen(v1) * getLen(v2);
        if (mr === 0) return 0;
        var r = dot(v1, v2) / mr;
        if (r > 1) r = 1;
        return Math.acos(r);
    }

    function cross(v1, v2) {
        return v1.x * v2.y - v2.x * v1.y;
    }

    function getRotateAngle(v1, v2) {
        var angle = getAngle(v1, v2);
        if (cross(v1, v2) > 0) {
            angle *= -1;
        }

        return angle * 180 / Math.PI;
    }
    var AlloyFinger = function (el, option) {

        el.addEventListener("touchstart", this.start.bind(this), false);
        el.addEventListener("touchmove", this.move.bind(this), false);
        el.addEventListener("touchend", this.end.bind(this), false);
        el.addEventListener("touchcancel",this.cancel.bind(this),false);

        this.preV = { x: null, y: null };
        this.pinchStartLen = null;
        this.scale = 1;
        this.isDoubleTap = false;
        this.rotate = option.rotate || function () { };
        this.touchStart = option.touchStart || function () { };
        this.multipointStart = option.multipointStart || function () { };
        this.multipointEnd=option.multipointEnd||function(){};
        this.pinch = option.pinch || function () { };
        this.swipe = option.swipe || function () { };
        this.tap = option.tap || function () { };
        this.doubleTap = option.doubleTap || function () { };
        this.longTap = option.longTap || function () { };
        this.singleTap = option.singleTap || function () { };
        this.pressMove = option.pressMove || function () { };
        this.touchMove = option.touchMove || function () { };
        this.touchEnd = option.touchEnd || function () { };
        this.touchCancel = option.touchCancel || function () { };

        this.delta = null;
        this.last = null;
        this.now = null;
        this.tapTimeout = null;
        this.touchTimeout = null;
        this.longTapTimeout = null;
        this.swipeTimeout=null;
        this.x1 = this.x2 = this.y1 = this.y2 = null;
        this.preTapPosition={x:null,y:null};
    };

    AlloyFinger.prototype = {
        start: function (evt) {
            if(!evt.touches)return;
            this.now = Date.now();
            this.x1 = evt.touches[0].pageX;
            this.y1 = evt.touches[0].pageY;
            this.delta = this.now - (this.last || this.now);
            this.touchStart(evt);
            if(this.preTapPosition.x!==null){
                this.isDoubleTap = (this.delta > 0 && this.delta <= 250&&Math.abs(this.preTapPosition.x-this.x1)<30&&Math.abs(this.preTapPosition.y-this.y1)<30);
            }
            this.preTapPosition.x=this.x1;
            this.preTapPosition.y=this.y1;
            this.last = this.now;
            var preV = this.preV,
                len = evt.touches.length;
            if (len > 1) {
                this._cancelLongTap();
                var v = { x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 };
                preV.x = v.x;
                preV.y = v.y;
                this.pinchStartLen = getLen(preV);
                this.multipointStart(evt);
            }
            this.longTapTimeout = setTimeout(function(){
                this.longTap(evt);
            }.bind(this), 750);
        },
        move: function (evt) {
            if(!evt.touches)return;
            var preV = this.preV,
                len = evt.touches.length,
                currentX = evt.touches[0].pageX,
                currentY = evt.touches[0].pageY;
            this.isDoubleTap=false;
            if (len > 1) {
                var v = { x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY };

                if (preV.x !== null) {
                    if (this.pinchStartLen > 0) {
                        evt.scale = getLen(v) / this.pinchStartLen;
                        this.pinch(evt);
                    }

                    evt.angle = getRotateAngle(v, preV);
                    this.rotate(evt);
                }
                preV.x = v.x;
                preV.y = v.y;
            } else {
                if (this.x2 !== null) {
                    evt.deltaX = currentX - this.x2;
                    evt.deltaY = currentY - this.y2;

                }else{
                    evt.deltaX = 0;
                    evt.deltaY = 0;
                }
                this.pressMove(evt);
            }

            this.touchMove(evt);

            this._cancelLongTap();
            this.x2 = currentX;
            this.y2 = currentY;
            if (evt.touches.length > 1) {
                this._cancelLongTap();
                evt.preventDefault();
            }
        },
        end: function (evt) {
            if(!evt.changedTouches)return;
            this._cancelLongTap();
            var self = this;
            if( evt.touches.length<2){
                this.multipointEnd(evt);
            }
            this.touchEnd(evt);
            //swipe
            if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) ||
                (this.y2 && Math.abs(this.preV.y - this.y2) > 30)) {
                evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2);
                this.swipeTimeout = setTimeout(function () {
                    self.swipe(evt);

                }, 0)
            } else {
                this.tapTimeout = setTimeout(function () {
                    self.tap(evt);
                    // trigger double tap immediately
                    if (self.isDoubleTap) {
                        self.doubleTap(evt);
                        clearTimeout(self.touchTimeout);
                        self.isDoubleTap = false;
                    }else{
                        self.touchTimeout=setTimeout(function(){
                            self.singleTap(evt);
                        },250);
                    }
                }, 0)
            }

            this.preV.x = 0;
            this.preV.y = 0;
            this.scale = 1;
            this.pinchStartLen = null;
            this.x1 = this.x2 = this.y1 = this.y2 = null;
        },
        cancel:function(evt){
            clearTimeout(this.touchTimeout);
            clearTimeout(this.tapTimeout);
            clearTimeout(this.longTapTimeout);
            clearTimeout(this.swipeTimeout);
            this.touchCancel(evt);
        },
        _cancelLongTap: function () {
            clearTimeout(this.longTapTimeout);
        },
        _swipeDirection: function (x1, x2, y1, y2) {
            return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
        }


    };

    if (typeof module !== 'undefined' && typeof exports === 'object') {
        module.exports = AlloyFinger;
    }else {
        window.AlloyFinger = AlloyFinger;
    }
})();

修改后的alloy_paper.js代码:

/* Alloy Game Engine
 * By AlloyTeam http://www.alloyteam.com/
 * Github: https://github.com/AlloyTeam/AlloyGameEngine
 * MIT Licensed.
 */
;(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        root.AlloyPaper = factory();
    }
}(this, function () {
'use strict';

// The base Class implementation (does nothing)
var Class = function () { };

// Create a new Class that inherits from this class
Class.extend = function (prop) {
    var _super = this.prototype;
    var prototype = Object.create(_super);

    // Copy the properties over onto the new prototype
    for (var name in prop) {
        if (name != "statics") {
            // Check if we're overwriting an existing function
            prototype[name] = typeof prop[name] == "function" &&
            typeof _super[name] == "function"  ?
                (function (temp_name, fn) {
                    return function () {
                        var tmp = this._super;

                        // Add a new ._super() method that is the same method
                        // but on the super-class
                        this._super = _super[temp_name];

                        // The method only need to be bound temporarily, so we
                        // remove it when we're done executing
                        var ret = fn.apply(this, arguments);
                        this._super = tmp;

                        return ret;
                    };
                })(name, prop[name]) :
                prop[name];
        }
    }

    // The dummy class constructor
    function _Class() {
        // All construction is actually done in the init method

        this.ctor.apply(this, arguments);
    }

    //继承父类的静态属性
    for (var key in this) {
        if (this.hasOwnProperty(key) && key != "extend")
            _Class[key] = this[key];
    }

    // Populate our constructed prototype object
    _Class.prototype = prototype;

    _Class.prototype._super = Object.create(_super);
    //静态属性和方法
    if (prop.statics) {
        for (var key in prop.statics) {
            if (prop.statics.hasOwnProperty(key)) {
                _Class[key] = prop.statics[key];
                if (key == "ctor") {
                    //提前执行静态构造函数
                    _Class[key]();
                }
            }

        }
    }

    // Enforce the constructor to be what we expect
    _Class.prototype.constructor = _Class;

    // And make this class extendable
    _Class.extend = Class.extend;

    return _Class;
};

window.Class = Class;
//AlloyPaper
var AlloyPaper={};

AlloyPaper.DefaultCursor = "default";

AlloyPaper.Cache = {};
AlloyPaper.TWEEN = Class.extend({
    "statics": {
        "ctor": function() {
            if (Date.now === undefined) {
                Date.now = function() {
                    return new Date().valueOf();
                };
            }
            this._tweens = [];
        },
        "REVISION": "14",
        "getAll": function() {
            return this._tweens;
        },
        "removeAll": function() {
            this._tweens = [];
        },
        "add": function(tween) {
            this._tweens.push(tween);
        },
        "remove": function(tween) {
            var i = this._tweens.indexOf(tween);
            if (i !== -1) {
                this._tweens.splice(i, 1);
            }
        },
        "update": function(time) {
            if (this._tweens.length === 0) return false;
            var i = 0;
            time = time !== undefined ? time : typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
            while (i < this._tweens.length) {
                if (this._tweens[i].update(time)) {
                    i++;
                } else {
                    this._tweens.splice(i, 1);
                }
            }
            return true;
        },
        "Tween": function(object) {
            var _object = object;
            var _valuesStart = {};
            var _valuesEnd = {};
            var _valuesStartRepeat = {};
            var _duration = 1e3;
            var _repeat = 0;
            var _yoyo = false;
            var _isPlaying = false;
            var _reversed = false;
            var _delayTime = 0;
            var _startTime = null;
            var _easingFunction = AlloyPaper.TWEEN.Easing.Linear.None;
            var _interpolationFunction = AlloyPaper.TWEEN.Interpolation.Linear;
            var _chainedTweens = [];
            var _onStartCallback = null;
            var _onStartCallbackFired = false;
            var _onUpdateCallback = null;
            var _onCompleteCallback = null;
            var _onStopCallback = null;
            var _paused = false,
                _passTime = null;
            for (var field in object) {
                _valuesStart[field] = parseFloat(object[field], 10);
            }
            this.toggle = function() {
                if (_paused) {
                    this.play();
                } else {
                    this.pause();
                }
            };
            this.pause = function() {
                _paused = true;
                var pauseTime = typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
                _passTime = pauseTime - _startTime;
            };
            this.play = function() {
                _paused = false;
                var nowTime = typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
                _startTime = nowTime - _passTime;
            };
            this.to = function(properties, duration) {
                if (duration !== undefined) {
                    _duration = duration;
                }
                _valuesEnd = properties;
                return this;
            };
            this.start = function(time) {
                AlloyPaper.TWEEN.add(this);
                _isPlaying = true;
                _onStartCallbackFired = false;
                _startTime = time !== undefined ? time : typeof window !== "undefined" && window.performance !== undefined && window.performance.now !== undefined ? window.performance.now() : Date.now();
                _startTime += _delayTime;
                for (var property in _valuesEnd) {
                    if (_valuesEnd[property] instanceof Array) {
                        if (_valuesEnd[property].length === 0) {
                            continue;
                        }
                        _valuesEnd[property] = [_object[property]].concat(_valuesEnd[property]);
                    }
                    _valuesStart[property] = _object[property];
                    if (_valuesStart[property] instanceof Array === false) {
                        _valuesStart[property] *= 1;
                    }
                    _valuesStartRepeat[property] = _valuesStart[property] || 0;
                }
                return this;
            };
            this.stop = function() {
                if (!_isPlaying) {
                    return this;
                }
                AlloyPaper.TWEEN.remove(this);
                _isPlaying = false;
                if (_onStopCallback !== null) {
                    _onStopCallback.call(_object);
                }
                this.stopChainedTweens();
                return this;
            };
            this.stopChainedTweens = function() {
                for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
                    _chainedTweens[i].stop();
                }
            };
            this.delay = function(amount) {
                _delayTime = amount;
                return this;
            };
            this.repeat = function(times) {
                _repeat = times;
                return this;
            };
            this.yoyo = function(yoyo) {
                _yoyo = yoyo;
                return this;
            };
            this.easing = function(easing) {
                _easingFunction = easing;
                return this;
            };
            this.interpolation = function(interpolation) {
                _interpolationFunction = interpolation;
                return this;
            };
            this.chain = function() {
                _chainedTweens = arguments;
                return this;
            };
            this.onStart = function(callback) {
                _onStartCallback = callback;
                return this;
            };
            this.onUpdate = function(callback) {
                _onUpdateCallback = callback;
                return this;
            };
            this.onComplete = function(callback) {
                _onCompleteCallback = callback;
                return this;
            };
            this.onStop = function(callback) {
                _onStopCallback = callback;
                return this;
            };
            this.update = function(time) {
                if (_paused) return true;
                var property;
                if (time < _startTime) {
                    return true;
                }
                if (_onStartCallbackFired === false) {
                    if (_onStartCallback !== null) {
                        _onStartCallback.call(_object);
                    }
                    _onStartCallbackFired = true;
                }
                var elapsed = (time - _startTime) / _duration;
                elapsed = elapsed > 1 ? 1 : elapsed;
                var value = _easingFunction(elapsed);
                for (property in _valuesEnd) {
                    var start = _valuesStart[property] || 0;
                    var end = _valuesEnd[property];
                    if (end instanceof Array) {
                        _object[property] = _interpolationFunction(end, value);
                    } else {
                        if (typeof end === "string") {
                            end = start + parseFloat(end, 10);
                        }
                        if (typeof end === "number") {
                            _object[property] = start + (end - start) * value;
                        }
                    }
                }
                if (_onUpdateCallback !== null) {
                    _onUpdateCallback.call(_object, value);
                }
                if (elapsed == 1) {
                    if (_repeat > 0) {
                        if (isFinite(_repeat)) {
                            _repeat--;
                        }
                        for (property in _valuesStartRepeat) {
                            if (typeof _valuesEnd[property] === "string") {
                                _valuesStartRepeat[property] = _valuesStartRepeat[property] + parseFloat(_valuesEnd[property], 10);
                            }
                            if (_yoyo) {
                                var tmp = _valuesStartRepeat[property];
                                _valuesStartRepeat[property] = _valuesEnd[property];
                                _valuesEnd[property] = tmp;
                            }
                            _valuesStart[property] = _valuesStartRepeat[property];
                        }
                        if (_yoyo) {
                            _reversed = !_reversed;
                        }
                        _startTime = time + _delayTime;
                        return true;
                    } else {
                        if (_onCompleteCallback !== null) {
                            _onCompleteCallback.call(_object);
                        }
                        for (var i = 0, numChainedTweens = _chainedTweens.length; i < numChainedTweens; i++) {
                            _chainedTweens[i].start(time);
                        }
                        return false;
                    }
                }
                return true;
            };
        },
        "Easing": {
            "Linear": {
                "None": function(k) {
                    return k;
                }
            },
            "Quadratic": {
                "In": function(k) {
                    return k * k;
                },
                "Out": function(k) {
                    return k * (2 - k);
                },
                "InOut": function(k) {
                    if ((k *= 2) < 1) return.5 * k * k;
                    return -.5 * (--k * (k - 2) - 1);
                }
            },
            "Cubic": {
                "In": function(k) {
                    return k * k * k;
                },
                "Out": function(k) {
                    return --k * k * k + 1;
                },
                "InOut": function(k) {
                    if ((k *= 2) < 1) return.5 * k * k * k;
                    return.5 * ((k -= 2) * k * k + 2);
                }
            },
            "Quartic": {
                "In": function(k) {
                    return k * k * k * k;
                },
                "Out": function(k) {
                    return 1 - --k * k * k * k;
                },
                "InOut": function(k) {
                    if ((k *= 2) < 1) return.5 * k * k * k * k;
                    return -.5 * ((k -= 2) * k * k * k - 2);
                }
            },
            "Quintic": {
                "In": function(k) {
                    return k * k * k * k * k;
                },
                "Out": function(k) {
                    return --k * k * k * k * k + 1;
                },
                "InOut": function(k) {
                    if ((k *= 2) < 1) return.5 * k * k * k * k * k;
                    return.5 * ((k -= 2) * k * k * k * k + 2);
                }
            },
            "Sinusoidal": {
                "In": function(k) {
                    return 1 - Math.cos(k * Math.PI / 2);
                },
                "Out": function(k) {
                    return Math.sin(k * Math.PI / 2);
                },
                "InOut": function(k) {
                    return.5 * (1 - Math.cos(Math.PI * k));
                }
            },
            "Exponential": {
                "In": function(k) {
                    return k === 0 ? 0 : Math.pow(1024, k - 1);
                },
                "Out": function(k) {
                    return k === 1 ? 1 : 1 - Math.pow(2, -10 * k);
                },
                "InOut": function(k) {
                    if (k === 0) return 0;
                    if (k === 1) return 1;
                    if ((k *= 2) < 1) return.5 * Math.pow(1024, k - 1);
                    return.5 * (-Math.pow(2, -10 * (k - 1)) + 2);
                }
            },
            "Circular": {
                "In": function(k) {
                    return 1 - Math.sqrt(1 - k * k);
                },
                "Out": function(k) {
                    return Math.sqrt(1 - --k * k);
                },
                "InOut": function(k) {
                    if ((k *= 2) < 1) return -.5 * (Math.sqrt(1 - k * k) - 1);
                    return.5 * (Math.sqrt(1 - (k -= 2) * k) + 1);
                }
            },
            "Elastic": {
                "In": function(k) {
                    var s, a = .1,
                        p = .4;
                    if (k === 0) return 0;
                    if (k === 1) return 1;
                    if (!a || a < 1) {
                        a = 1;
                        s = p / 4;
                    } else s = p * Math.asin(1 / a) / (2 * Math.PI);
                    return -(a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
                },
                "Out": function(k) {
                    var s, a = .1,
                        p = .4;
                    if (k === 0) return 0;
                    if (k === 1) return 1;
                    if (!a || a < 1) {
                        a = 1;
                        s = p / 4;
                    } else s = p * Math.asin(1 / a) / (2 * Math.PI);
                    return a * Math.pow(2, -10 * k) * Math.sin((k - s) * (2 * Math.PI) / p) + 1;
                },
                "InOut": function(k) {
                    var s, a = .1,
                        p = .4;
                    if (k === 0) return 0;
                    if (k === 1) return 1;
                    if (!a || a < 1) {
                        a = 1;
                        s = p / 4;
                    } else s = p * Math.asin(1 / a) / (2 * Math.PI);
                    if ((k *= 2) < 1) return -.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p));
                    return a * Math.pow(2, -10 * (k -= 1)) * Math.sin((k - s) * (2 * Math.PI) / p) * .5 + 1;
                }
            },
            "Back": {
                "In": function(k) {
                    var s = 1.70158;
                    return k * k * ((s + 1) * k - s);
                },
                "Out": function(k) {
                    var s = 1.70158;
                    return --k * k * ((s + 1) * k + s) + 1;
                },
                "InOut": function(k) {
                    var s = 1.70158 * 1.525;
                    if ((k *= 2) < 1) return.5 * (k * k * ((s + 1) * k - s));
                    return.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2);
                }
            },
            "Bounce": {
                "In": function(k) {
                    return 1 - AlloyPaper.TWEEN.Easing.Bounce.Out(1 - k);
                },
                "Out": function(k) {
                    if (k < 1 / 2.75) {
                        return 7.5625 * k * k;
                    } else if (k < 2 / 2.75) {
                        return 7.5625 * (k -= 1.5 / 2.75) * k + .75;
                    } else if (k < 2.5 / 2.75) {
                        return 7.5625 * (k -= 2.25 / 2.75) * k + .9375;
                    } else {
                        return 7.5625 * (k -= 2.625 / 2.75) * k + .984375;
                    }
                },
                "InOut": function(k) {
                    if (k < .5) return AlloyPaper.TWEEN.Easing.Bounce.In(k * 2) * .5;
                    return AlloyPaper.TWEEN.Easing.Bounce.Out(k * 2 - 1) * .5 + .5;
                }
            }
        },
        "Interpolation": {
            "Linear": function(v, k) {
                var m = v.length - 1,
                    f = m * k,
                    i = Math.floor(f),
                    fn = AlloyPaper.TWEEN.Interpolation.Utils.Linear;
                if (k < 0) return fn(v[0], v[1], f);
                if (k > 1) return fn(v[m], v[m - 1], m - f);
                return fn(v[i], v[i + 1 > m ? m : i + 1], f - i);
            },
            "Bezier": function(v, k) {
                var b = 0,
                    n = v.length - 1,
                    pw = Math.pow,
                    bn = AlloyPaper.TWEEN.Interpolation.Utils.Bernstein,
                    i;
                for (i = 0; i <= n; i++) {
                    b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i);
                }
                return b;
            },
            "CatmullRom": function(v, k) {
                var m = v.length - 1,
                    f = m * k,
                    i = Math.floor(f),
                    fn = AlloyPaper.TWEEN.Interpolation.Utils.CatmullRom;
                if (v[0] === v[m]) {
                    if (k < 0) i = Math.floor(f = m * (1 + k));
                    return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i);
                } else {
                    if (k < 0) return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0]);
                    if (k > 1) return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m]);
                    return fn(v[i ? i - 1 : 0], v[i], v[m < i + 1 ? m : i + 1], v[m < i + 2 ? m : i + 2], f - i);
                }
            },
            "Utils": {
                "Linear": function(p0, p1, t) {
                    return (p1 - p0) * t + p0;
                },
                "Bernstein": function(n, i) {
                    var fc = AlloyPaper.TWEEN.Interpolation.Utils.getFactorial();
                    return fc(n) / fc(i) / fc(n - i);
                },
                "getFactorial": function() {
                    return function() {
                        var a = [1];
                        return function(n) {
                            var s = 1,
                                i;
                            if (a[n]) return a[n];
                            for (i = n; i > 1; i--) s *= i;
                            return a[n] = s;
                        };
                    }();
                },
                "CatmullRom": function(p0, p1, p2, p3, t) {
                    var v0 = (p2 - p0) * .5,
                        v1 = (p3 - p1) * .5,
                        t2 = t * t,
                        t3 = t * t2;
                    return (2 * p1 - 2 * p2 + v0 + v1) * t3 + (-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 + v0 * t + p1;
                }
            }
        }
    }
});


//begin-------------------AlloyPaper.Dom---------------------begin

AlloyPaper.Dom = Class.extend({
    "statics": {
        "get": function(selector) {
            this.element = document.querySelector(selector);
            return this;
        },
        "on": function(type, fn) {
            this.element.addEventListener(type, fn, false);
            return this;
        }
    }
});

//end-------------------AlloyPaper.Dom---------------------end


//begin-------------------AlloyPaper.FPS---------------------begin

AlloyPaper.FPS = Class.extend({
    "statics": {
        "get": function() {
            if (!this.instance) this.instance = new this();
            this.instance._computeFPS();
            return this.instance;
        }
    },
    "ctor": function() {
        this.last = new Date();
        this.current = null;
        this.lastMeasured=new Date();
        this.fpsList = [];
        this.totalValue = 0;
        this.value = 60;
      
    },
    "_computeFPS": function() {
        this.current = new Date();
        if (this.current - this.last > 0) {
            var fps = Math.ceil(1e3 / (this.current - this.last));
            this.fpsList.push(fps);
           
            this.totalValue += fps;
            this.last = this.current;
         
       
        }
        if (this.current - this.lastMeasured > 1000) {

            this.value =Math.ceil( this.totalValue / this.fpsList.length);
            this.totalValue = 0;
            this.fpsList.length = 0;
            this.lastMeasured = this.current;

        }
    }
});

//end-------------------AlloyPaper.FPS---------------------end

AlloyPaper.Keyboard = Class.extend({
    "statics": {
        "ctor": function() {
            var KeyboardJS = {},
                locales = {},
                locale, map, macros, activeKeys = [],
                bindings = [],
                activeBindings = [],
                activeMacros = [],
                aI, usLocale;
            usLocale = {
                map: {
                    "3": ["cancel"],
                    "8": ["backspace"],
                    "9": ["tab"],
                    "12": ["clear"],
                    "13": ["enter"],
                    "16": ["shift"],
                    "17": ["ctrl"],
                    "18": ["alt", "menu"],
                    "19": ["pause", "break"],
                    "20": ["capslock"],
                    "27": ["escape", "esc"],
                    "32": ["space", "spacebar"],
                    "33": ["pageup"],
                    "34": ["pagedown"],
                    "35": ["end"],
                    "36": ["home"],
                    "37": ["left"],
                    "38": ["up"],
                    "39": ["right"],
                    "40": ["down"],
                    "41": ["select"],
                    "42": ["printscreen"],
                    "43": ["execute"],
                    "44": ["snapshot"],
                    "45": ["insert", "ins"],
                    "46": ["delete", "del"],
                    "47": ["help"],
                    "91": ["command", "windows", "win", "super", "leftcommand", "leftwindows", "leftwin", "leftsuper"],
                    "92": ["command", "windows", "win", "super", "rightcommand", "rightwindows", "rightwin", "rightsuper"],
                    "145": ["scrolllock", "scroll"],
                    "186": ["semicolon", ";"],
                    "187": ["equal", "equalsign", "="],
                    "188": ["comma", ","],
                    "189": ["dash", "-"],
                    "190": ["period", "."],
                    "191": ["slash", "forwardslash", "/"],
                    "192": ["graveaccent", "`"],
                    "219": ["openbracket", "["],
                    "220": ["backslash", "\\"],
                    "221": ["closebracket", "]"],
                    "222": ["apostrophe", "'"],
                    "48": ["zero", "0"],
                    "49": ["one", "1"],
                    "50": ["two", "2"],
                    "51": ["three", "3"],
                    "52": ["four", "4"],
                    "53": ["five", "5"],
                    "54": ["six", "6"],
                    "55": ["seven", "7"],
                    "56": ["eight", "8"],
                    "57": ["nine", "9"],
                    "96": ["numzero", "num0"],
                    "97": ["numone", "num1"],
                    "98": ["numtwo", "num2"],
                    "99": ["numthree", "num3"],
                    "100": ["numfour", "num4"],
                    "101": ["numfive", "num5"],
                    "102": ["numsix", "num6"],
                    "103": ["numseven", "num7"],
                    "104": ["numeight", "num8"],
                    "105": ["numnine", "num9"],
                    "106": ["nummultiply", "num*"],
                    "107": ["numadd", "num+"],
                    "108": ["numenter"],
                    "109": ["numsubtract", "num-"],
                    "110": ["numdecimal", "num."],
                    "111": ["numdivide", "num/"],
                    "144": ["numlock", "num"],
                    "112": ["f1"],
                    "113": ["f2"],
                    "114": ["f3"],
                    "115": ["f4"],
                    "116": ["f5"],
                    "117": ["f6"],
                    "118": ["f7"],
                    "119": ["f8"],
                    "120": ["f9"],
                    "121": ["f10"],
                    "122": ["f11"],
                    "123": ["f12"]
                },
                macros: [["shift + `", ["tilde", "~"]], ["shift + 1", ["exclamation", "exclamationpoint", "!"]], ["shift + 2", ["at", "@"]], ["shift + 3", ["number", "#"]], ["shift + 4", ["dollar", "dollars", "dollarsign", "$"]], ["shift + 5", ["percent", "%"]], ["shift + 6", ["caret", "^"]], ["shift + 7", ["ampersand", "and", "&"]], ["shift + 8", ["asterisk", "*"]], ["shift + 9", ["openparen", "("]], ["shift + 0", ["closeparen", ")"]], ["shift + -", ["underscore", "_"]], ["shift + =", ["plus", "+"]], ["shift + (", ["opencurlybrace", "opencurlybracket", "{"]], ["shift + )", ["closecurlybrace", "closecurlybracket", "}"]], ["shift + \\", ["verticalbar", "|"]], ["shift + ;", ["colon", ":"]], ["shift + '", ["quotationmark", '"']], ["shift + !,", ["openanglebracket", "<"]], ["shift + .", ["closeanglebracket", ">"]], ["shift + /", ["questionmark", "?"]]]
            };
            for (aI = 65; aI <= 90; aI += 1) {
                usLocale.map[aI] = String.fromCharCode(aI + 32);
                usLocale.macros.push(["shift + " + String.fromCharCode(aI + 32) + ", capslock + " + String.fromCharCode(aI + 32), [String.fromCharCode(aI)]]);
            }
            registerLocale("us", usLocale);
            getSetLocale("us");
            enable();
            KeyboardJS.enable = enable;
            KeyboardJS.disable = disable;
            KeyboardJS.activeKeys = getActiveKeys;
            KeyboardJS.releaseKey = removeActiveKey;
            KeyboardJS.pressKey = addActiveKey;
            KeyboardJS.on = createBinding;
            KeyboardJS.clear = removeBindingByKeyCombo;
            KeyboardJS.clear.key = removeBindingByKeyName;
            KeyboardJS.locale = getSetLocale;
            KeyboardJS.locale.register = registerLocale;
            KeyboardJS.macro = createMacro;
            KeyboardJS.macro.remove = removeMacro;
            KeyboardJS.key = {};
            KeyboardJS.key.name = getKeyName;
            KeyboardJS.key.code = getKeyCode;
            KeyboardJS.combo = {};
            KeyboardJS.combo.active = isSatisfiedCombo;
            KeyboardJS.combo.parse = parseKeyCombo;
            KeyboardJS.combo.stringify = stringifyKeyCombo;

            function enable() {
                if (window.addEventListener) {
                    window.document.addEventListener("keydown", keydown, false);
                    window.document.addEventListener("keyup", keyup, false);
                    window.addEventListener("blur", reset, false);
                    window.addEventListener("webkitfullscreenchange", reset, false);
                    window.addEventListener("mozfullscreenchange", reset, false);
                } else if (window.attachEvent) {
                    window.document.attachEvent("onkeydown", keydown);
                    window.document.attachEvent("onkeyup", keyup);
                    window.attachEvent("onblur", reset);
                }
            }
            function disable() {
                reset();
                if (window.removeEventListener) {
                    window.document.removeEventListener("keydown", keydown, false);
                    window.document.removeEventListener("keyup", keyup, false);
                    window.removeEventListener("blur", reset, false);
                    window.removeEventListener("webkitfullscreenchange", reset, false);
                    window.removeEventListener("mozfullscreenchange", reset, false);
                } else if (window.detachEvent) {
                    window.document.detachEvent("onkeydown", keydown);
                    window.document.detachEvent("onkeyup", keyup);
                    window.detachEvent("onblur", reset);
                }
            }
            function reset(event) {
                activeKeys = [];
                pruneMacros();
                pruneBindings(event);
            }
            function keydown(event) {
                var keyNames, keyName, kI;
                keyNames = getKeyName(event.keyCode);
                if (keyNames.length < 1) {
                    return;
                }
                event.isRepeat = false;
                for (kI = 0; kI < keyNames.length; kI += 1) {
                    keyName = keyNames[kI];
                    if (getActiveKeys().indexOf(keyName) != -1) event.isRepeat = true;
                    addActiveKey(keyName);
                }
                executeMacros();
                executeBindings(event);
            }
            function keyup(event) {
                var keyNames, kI;
                keyNames = getKeyName(event.keyCode);
                if (keyNames.length < 1) {
                    return;
                }
                for (kI = 0; kI < keyNames.length; kI += 1) {
                    removeActiveKey(keyNames[kI]);
                }
                pruneMacros();
                pruneBindings(event);
            }
            function getKeyName(keyCode) {
                return map[keyCode] || [];
            }
            function getKeyCode(keyName) {
                var keyCode;
                for (keyCode in map) {
                    if (!map.hasOwnProperty(keyCode)) {
                        continue;
                    }
                    if (map[keyCode].indexOf(keyName) > -1) {
                        return keyCode;
                    }
                }
                return false;
            }
            function createMacro(combo, injectedKeys) {
                if (typeof combo !== "string" && (typeof combo !== "object" || typeof combo.push !== "function")) {
                    throw new Error("Cannot create macro. The combo must be a string or array.");
                }
                if (typeof injectedKeys !== "object" || typeof injectedKeys.push !== "function") {
                    throw new Error("Cannot create macro. The injectedKeys must be an array.");
                }
                macros.push([combo, injectedKeys]);
            }
            function removeMacro(combo) {
                var macro, mI;
                if (typeof combo !== "string" && (typeof combo !== "object" || typeof combo.push !== "function")) {
                    throw new Error("Cannot remove macro. The combo must be a string or array.");
                }
                for (mI = 0; mI < macros.length; mI += 1) {
                    macro = macros[mI];
                    if (compareCombos(combo, macro[0])) {
                        removeActiveKey(macro[1]);
                        macros.splice(mI, 1);
                        break;
                    }
                }
            }
            function executeMacros() {
                var mI, combo, kI;
                for (mI = 0; mI < macros.length; mI += 1) {
                    combo = parseKeyCombo(macros[mI][0]);
                    if (activeMacros.indexOf(macros[mI]) === -1 && isSatisfiedCombo(combo)) {
                        activeMacros.push(macros[mI]);
                        for (kI = 0; kI < macros[mI][1].length; kI += 1) {
                            addActiveKey(macros[mI][1][kI]);
                        }
                    }
                }
            }
            function pruneMacros() {
                var mI, combo, kI;
                for (mI = 0; mI < activeMacros.length; mI += 1) {
                    combo = parseKeyCombo(activeMacros[mI][0]);
                    if (isSatisfiedCombo(combo) === false) {
                        for (kI = 0; kI < activeMacros[mI][1].length; kI += 1) {
                            removeActiveKey(activeMacros[mI][1][kI]);
                        }
                        activeMacros.splice(mI, 1);
                        mI -= 1;
                    }
                }
            }
            function createBinding(keyCombo, keyDownCallback, keyUpCallback) {
                var api = {},
                    binding, subBindings = [],
                    bindingApi = {},
                    kI, subCombo;
                if (typeof keyCombo === "string") {
                    keyCombo = parseKeyCombo(keyCombo);
                }
                for (kI = 0; kI < keyCombo.length; kI += 1) {
                    binding = {};
                    subCombo = stringifyKeyCombo([keyCombo[kI]]);
                    if (typeof subCombo !== "string") {
                        throw new Error("Failed to bind key combo. The key combo must be string.");
                    }
                    binding.keyCombo = subCombo;
                    binding.keyDownCallback = [];
                    binding.keyUpCallback = [];
                    if (keyDownCallback) {
                        binding.keyDownCallback.push(keyDownCallback);
                    }
                    if (keyUpCallback) {
                        binding.keyUpCallback.push(keyUpCallback);
                    }
                    bindings.push(binding);
                    subBindings.push(binding);
                }
                api.clear = clear;
                api.on = on;
                return api;

                function clear() {
                    var bI;
                    for (bI = 0; bI < subBindings.length; bI += 1) {
                        bindings.splice(bindings.indexOf(subBindings[bI]), 1);
                    }
                }
                function on(eventName) {
                    var api = {},
                        callbacks, cI, bI;
                    if (typeof eventName !== "string") {
                        throw new Error("Cannot bind callback. The event name must be a string.");
                    }
                    if (eventName !== "keyup" && eventName !== "keydown") {
                        throw new Error('Cannot bind callback. The event name must be a "keyup" or "keydown".');
                    }
                    callbacks = Array.prototype.slice.apply(arguments, [1]);
                    for (cI = 0; cI < callbacks.length; cI += 1) {
                        if (typeof callbacks[cI] === "function") {
                            if (eventName === "keyup") {
                                for (bI = 0; bI < subBindings.length; bI += 1) {
                                    subBindings[bI].keyUpCallback.push(callbacks[cI]);
                                }
                            } else if (eventName === "keydown") {
                                for (bI = 0; bI < subBindings.length; bI += 1) {
                                    subBindings[bI].keyDownCallback.push(callbacks[cI]);
                                }
                            }
                        }
                    }
                    api.clear = clear;
                    return api;

                    function clear() {
                        var cI, bI;
                        for (cI = 0; cI < callbacks.length; cI += 1) {
                            if (typeof callbacks[cI] === "function") {
                                if (eventName === "keyup") {
                                    for (bI = 0; bI < subBindings.length; bI += 1) {
                                        subBindings[bI].keyUpCallback.splice(subBindings[bI].keyUpCallback.indexOf(callbacks[cI]), 1);
                                    }
                                } else {
                                    for (bI = 0; bI < subBindings.length; bI += 1) {
                                        subBindings[bI].keyDownCallback.splice(subBindings[bI].keyDownCallback.indexOf(callbacks[cI]), 1);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            function removeBindingByKeyCombo(keyCombo) {
                var bI, binding, keyName;
                for (bI = 0; bI < bindings.length; bI += 1) {
                    binding = bindings[bI];
                    if (compareCombos(keyCombo, binding.keyCombo)) {
                        bindings.splice(bI, 1);
                        bI -= 1;
                    }
                }
            }
            function removeBindingByKeyName(keyName) {
                var bI, kI, binding;
                if (keyName) {
                    for (bI = 0; bI < bindings.length; bI += 1) {
                        binding = bindings[bI];
                        for (kI = 0; kI < binding.keyCombo.length; kI += 1) {
                            if (binding.keyCombo[kI].indexOf(keyName) > -1) {
                                bindings.splice(bI, 1);
                                bI -= 1;
                                break;
                            }
                        }
                    }
                } else {
                    bindings = [];
                }
            }
            function executeBindings(event) {
                var bI, sBI, binding, bindingKeys, remainingKeys, cI, killEventBubble, kI, bindingKeysSatisfied, index, sortedBindings = [],
                    bindingWeight;
                remainingKeys = [].concat(activeKeys);
                for (bI = 0; bI < bindings.length; bI += 1) {
                    bindingWeight = extractComboKeys(bindings[bI].keyCombo).length;
                    if (!sortedBindings[bindingWeight]) {
                        sortedBindings[bindingWeight] = [];
                    }
                    sortedBindings[bindingWeight].push(bindings[bI]);
                }
                for (sBI = sortedBindings.length - 1; sBI >= 0; sBI -= 1) {
                    if (!sortedBindings[sBI]) {
                        continue;
                    }
                    for (bI = 0; bI < sortedBindings[sBI].length; bI += 1) {
                        binding = sortedBindings[sBI][bI];
                        bindingKeys = extractComboKeys(binding.keyCombo);
                        bindingKeysSatisfied = true;
                        for (kI = 0; kI < bindingKeys.length; kI += 1) {
                            if (remainingKeys.indexOf(bindingKeys[kI]) === -1) {
                                bindingKeysSatisfied = false;
                                break;
                            }
                        }
                        if (bindingKeysSatisfied && isSatisfiedCombo(binding.keyCombo)) {
                            activeBindings.push(binding);
                            for (kI = 0; kI < bindingKeys.length; kI += 1) {
                                index = remainingKeys.indexOf(bindingKeys[kI]);
                                if (index > -1) {
                                    remainingKeys.splice(index, 1);
                                    kI -= 1;
                                }
                            }
                            for (cI = 0; cI < binding.keyDownCallback.length; cI += 1) {
                                if (binding.keyDownCallback[cI](event, getActiveKeys(), binding.keyCombo) === false) {
                                    killEventBubble = true;
                                }
                            }
                            if (killEventBubble === true) {
                                event.preventDefault();
                                event.stopPropagation();
                            }
                        }
                    }
                }
            }
            function pruneBindings(event) {
                var bI, cI, binding, killEventBubble;
                for (bI = 0; bI < activeBindings.length; bI += 1) {
                    binding = activeBindings[bI];
                    if (isSatisfiedCombo(binding.keyCombo) === false) {
                        for (cI = 0; cI < binding.keyUpCallback.length; cI += 1) {
                            if (binding.keyUpCallback[cI](event, getActiveKeys(), binding.keyCombo) === false) {
                                killEventBubble = true;
                            }
                        }
                        if (killEventBubble === true) {
                            event.preventDefault();
                            event.stopPropagation();
                        }
                        activeBindings.splice(bI, 1);
                        bI -= 1;
                    }
                }
            }
            function compareCombos(keyComboArrayA, keyComboArrayB) {
                var cI, sI, kI;
                keyComboArrayA = parseKeyCombo(keyComboArrayA);
                keyComboArrayB = parseKeyCombo(keyComboArrayB);
                if (keyComboArrayA.length !== keyComboArrayB.length) {
                    return false;
                }
                for (cI = 0; cI < keyComboArrayA.length; cI += 1) {
                    if (keyComboArrayA[cI].length !== keyComboArrayB[cI].length) {
                        return false;
                    }
                    for (sI = 0; sI < keyComboArrayA[cI].length; sI += 1) {
                        if (keyComboArrayA[cI][sI].length !== keyComboArrayB[cI][sI].length) {

                            return false;
                        }
                        for (kI = 0; kI < keyComboArrayA[cI][sI].length; kI += 1) {
                            if (keyComboArrayB[cI][sI].indexOf(keyComboArrayA[cI][sI][kI]) === -1) {
                                return false;
                            }
                        }
                    }
                }
                return true;
            }
            function isSatisfiedCombo(keyCombo) {
                var cI, sI, stage, kI, stageOffset = 0,
                    index, comboMatches;
                keyCombo = parseKeyCombo(keyCombo);
                for (cI = 0; cI < keyCombo.length; cI += 1) {
                    comboMatches = true;
                    stageOffset = 0;
                    for (sI = 0; sI < keyCombo[cI].length; sI += 1) {
                        stage = [].concat(keyCombo[cI][sI]);
                        for (kI = stageOffset; kI < activeKeys.length; kI += 1) {
                            index = stage.indexOf(activeKeys[kI]);
                            if (index > -1) {
                                stage.splice(index, 1);
                                stageOffset = kI;
                            }
                        }
                        if (stage.length !== 0) {
                            comboMatches = false;
                            break;
                        }
                    }
                    if (comboMatches) {
                        return true;
                    }
                }
                return false;
            }
            function extractComboKeys(keyCombo) {
                var cI, sI, kI, keys = [];
                keyCombo = parseKeyCombo(keyCombo);
                for (cI = 0; cI < keyCombo.length; cI += 1) {
                    for (sI = 0; sI < keyCombo[cI].length; sI += 1) {
                        keys = keys.concat(keyCombo[cI][sI]);
                    }
                }
                return keys;
            }
            function parseKeyCombo(keyCombo) {
                var s = keyCombo,
                    i = 0,
                    op = 0,
                    ws = false,
                    nc = false,
                    combos = [],
                    combo = [],
                    stage = [],
                    key = "";
                if (typeof keyCombo === "object" && typeof keyCombo.push === "function") {
                    return keyCombo;
                }
                if (typeof keyCombo !== "string") {
                    throw new Error('Cannot parse "keyCombo" because its type is "' + typeof keyCombo + '". It must be a "string".');
                }
                while (s.charAt(i) === " ") {
                    i += 1;
                }
                while (true) {
                    if (s.charAt(i) === " ") {
                        while (s.charAt(i) === " ") {
                            i += 1;
                        }
                        ws = true;
                    } else if (s.charAt(i) === ",") {
                        if (op || nc) {
                            throw new Error("Failed to parse key combo. Unexpected , at character index " + i + ".");
                        }
                        nc = true;
                        i += 1;
                    } else if (s.charAt(i) === "+") {
                        if (key.length) {
                            stage.push(key);
                            key = "";
                        }
                        if (op || nc) {
                            throw new Error("Failed to parse key combo. Unexpected + at character index " + i + ".");
                        }
                        op = true;
                        i += 1;
                    } else if (s.charAt(i) === ">") {
                        if (key.length) {
                            stage.push(key);
                            key = "";
                        }
                        if (stage.length) {
                            combo.push(stage);
                            stage = [];
                        }
                        if (op || nc) {
                            throw new Error("Failed to parse key combo. Unexpected > at character index " + i + ".");
                        }
                        op = true;
                        i += 1;
                    } else if (i < s.length - 1 && s.charAt(i) === "!" && (s.charAt(i + 1) === ">" || s.charAt(i + 1) === "," || s.charAt(i + 1) === "+")) {
                        key += s.charAt(i + 1);
                        op = false;
                        ws = false;
                        nc = false;
                        i += 2;
                    } else if (i < s.length && s.charAt(i) !== "+" && s.charAt(i) !== ">" && s.charAt(i) !== "," && s.charAt(i) !== " ") {
                        if (op === false && ws === true || nc === true) {
                            if (key.length) {
                                stage.push(key);
                                key = "";
                            }
                            if (stage.length) {
                                combo.push(stage);
                                stage = [];
                            }
                            if (combo.length) {
                                combos.push(combo);
                                combo = [];
                            }
                        }
                        op = false;
                        ws = false;
                        nc = false;
                        while (i < s.length && s.charAt(i) !== "+" && s.charAt(i) !== ">" && s.charAt(i) !== "," && s.charAt(i) !== " ") {
                            key += s.charAt(i);
                            i += 1;
                        }
                    } else {
                        i += 1;
                        continue;
                    }
                    if (i >= s.length) {
                        if (key.length) {
                            stage.push(key);
                            key = "";
                        }
                        if (stage.length) {
                            combo.push(stage);
                            stage = [];
                        }
                        if (combo.length) {
                            combos.push(combo);
                            combo = [];
                        }
                        break;
                    }
                }
                return combos;
            }
            function stringifyKeyCombo(keyComboArray) {
                var cI, ccI, output = [];
                if (typeof keyComboArray === "string") {
                    return keyComboArray;
                }
                if (typeof keyComboArray !== "object" || typeof keyComboArray.push !== "function") {
                    throw new Error("Cannot stringify key combo.");
                }
                for (cI = 0; cI < keyComboArray.length; cI += 1) {
                    output[cI] = [];
                    for (ccI = 0; ccI < keyComboArray[cI].length; ccI += 1) {
                        output[cI][ccI] = keyComboArray[cI][ccI].join(" + ");
                    }
                    output[cI] = output[cI].join(" > ");
                }
                return output.join(" ");
            }
            function getActiveKeys() {
                return [].concat(activeKeys);
            }
            function addActiveKey(keyName) {
                if (keyName.match(/\s/)) {
                    throw new Error("Cannot add key name " + keyName + " to active keys because it contains whitespace.");
                }
                if (activeKeys.indexOf(keyName) > -1) {
                    return;
                }
                activeKeys.push(keyName);
            }
            function removeActiveKey(keyName) {
                var keyCode = getKeyCode(keyName);
                if (keyCode === "91" || keyCode === "92") {
                    activeKeys = [];
                } else {
                    activeKeys.splice(activeKeys.indexOf(keyName), 1);
                }
            }
            function registerLocale(localeName, localeMap) {
                if (typeof localeName !== "string") {
                    throw new Error("Cannot register new locale. The locale name must be a string.");
                }
                if (typeof localeMap !== "object") {
                    throw new Error("Cannot register " + localeName + " locale. The locale map must be an object.");
                }
                if (typeof localeMap.map !== "object") {
                    throw new Error("Cannot register " + localeName + " locale. The locale map is invalid.");
                }
                if (!localeMap.macros) {
                    localeMap.macros = [];
                }
                locales[localeName] = localeMap;
            }
            function getSetLocale(localeName) {
                if (localeName) {
                    if (typeof localeName !== "string") {
                        throw new Error("Cannot set locale. The locale name must be a string.");
                    }
                    if (!locales[localeName]) {
                        throw new Error("Cannot set locale to " + localeName + " because it does not exist. If you would like to submit a " + localeName + " locale map for KeyboardJS please submit it at https://github.com/RobertWHurst/KeyboardJS/issues.");
                    }
                    map = locales[localeName].map;
                    macros = locales[localeName].macros;
                    locale = localeName;
                }
                return locale;
            }
            this.Keyboard = KeyboardJS;
        },
        "on": function(keyCombo, onDownCallback, onUpCallback) {
            this.Keyboard.on(keyCombo, onDownCallback, onUpCallback);
        },
        "getActiveKeys": function() {
            return this.Keyboard.activeKeys();
        }
    }
});

//begin-------------------AlloyPaper.Loader---------------------begin

AlloyPaper.Loader = Class.extend({
    "ctor": function() {
        this.res = {};
        this.loadedCount = 0;
        this.resCount = -1;
        this.FILE_PATTERN = /(\w+:\/{2})?((?:\w+\.){2}\w+)?(\/?[\S]+\/|\/)?([\w\-%\.]+)(?:\.)(\w+)?(\?\S+)?/i;
        this.ns = 6;
        this.sounds = [];
        for (var i = 0; i < this.ns; i++) this.sounds.push([]);
        this.playing = [];
    },
    "get": function(id) {
        return this.res[id];
    },
    "loadRes2": function(arr) {
        this.resCount = arr.length;
        for (var i = 0; i < arr.length; i++) {
			this.loadImage(arr[i].id, arr[i].src);
        }
    },
    "loadRes": function(arr) {
        this.resCount = arr.length;
        for (var i = 0; i < arr.length; i++) {
            var type=this._getTypeByExtension(arr[i].src.match(this.FILE_PATTERN)[5]);
            if (type === "audio") {
                this.loadAudio(arr[i].id, arr[i].src);
            } else if (type === "js") {
                this.loadScript(arr[i].src);
            } else if (type === "img") {
                this.loadImage(arr[i].id, arr[i].src);
            }
        }
    },
    "loadImage": function(id, src) {
        var img = document.createElement("img");
        var self = this;
        img.onload = function() {
            self._handleLoad(this, id);
            img.onreadystatechange = null;
        };
        img.onreadystatechange = function() {
            if (img.readyState == "loaded" || img.readyState == "complete") {
                self._handleLoad(this, id);
                img.onload = null;
            }
        };
        img.onerror = function() {};
        img.src = src;
    },
    "loadAudio": function(id, src) {
        var tag = document.createElement("audio");
        tag.autoplay = false;
        this.res[id] = tag;
        tag.src = null;
        tag.preload = "auto";
        tag.onerror = function() {};
        tag.onstalled = function() {};
        var self = this;
        var _audioCanPlayHandler = function() {
            self.playing[id] = 0;
            for (var i = 0; i < self.ns; i++) {
                self.sounds[i][id] = new Audio(src);
            }
            self.loadedCount++;
            self.handleProgress&&self.handleProgress(self.loadedCount, self.resCount);
            self._clean(this);
            this.removeEventListener && this.removeEventListener("canplaythrough", _audioCanPlayHandler, false);
            self.checkComplete();
        };
        tag.addEventListener("canplaythrough", _audioCanPlayHandler, false);
        tag.src = src;
        if (tag.load != null) {
            tag.load();
        }
    },
    "loadScript": function (url) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        var self = this;
        if (script.readyState) {  //IE
            script.onreadystatechange = function () {
                if (script.readyState == "loaded" ||
                        script.readyState == "complete") {
                    script.onreadystatechange = null;
                    self._handleLoad();
                }
            };
        } else {  //Others
            script.onload = function () {
                self._handleLoad();
            };
        }

        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    },
    "checkComplete": function() {
        if (this.loadedCount === this.resCount) {
            this.handleComplete();
        }
    },
    "complete": function(fn) {
        this.handleComplete = fn;
    },
    "progress": function(fn) {
        this.handleProgress = fn;
    },
    "playSound": function (id, volume) {
        var sound = this.sounds[this.playing[id]][id];
        sound.volume = volume === undefined ? 1 : volume;
        sound.play();
        ++this.playing[id];
        if (this.playing[id] >= this.ns) this.playing[id] = 0;
    },
    "_handleLoad": function (currentImg, id) {
        if (currentImg) {
            this._clean(currentImg);
            this.res[id] = currentImg;        
        }
        this.loadedCount++;
        if (this.handleProgress) this.handleProgress(this.loadedCount, this.resCount);
        this.checkComplete();
    },
    "_getTypeByExtension": function(extension) {
        switch (extension) {
        case "jpeg":
        case "jpg":
        case "gif":
        case "png":
        case "webp":
        case "bmp":
            return "img";
        case "ogg":
        case "mp3":
        case "wav":
            return "audio";
        case "js":
            return "js";
        }
    },
    "_clean": function(tag) {
        tag.onload = null;
        tag.onstalled = null;
        tag.onprogress = null;
        tag.onerror = null;
    }
});

//end-------------------AlloyPaper.Loader---------------------end


//begin-------------------AlloyPaper.Matrix2D---------------------begin

AlloyPaper.Matrix2D = Class.extend({
    "statics": {
        "DEG_TO_RAD": 0.017453292519943295
    },
    "ctor": function(a, b, c, d, tx, ty) {
        this.a = a == null ? 1 : a;
        this.b = b || 0;
        this.c = c || 0;
        this.d = d == null ? 1 : d;
        this.tx = tx || 0;
        this.ty = ty || 0;
        return this;
    },
    "identity": function() {
        this.a = this.d = 1;
        this.b = this.c = this.tx = this.ty = 0;
        return this;
    },
    "appendTransform": function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
        if (rotation % 360) {
            var r = rotation * AlloyPaper.Matrix2D.DEG_TO_RAD;
            var cos = Math.cos(r);
            var sin = Math.sin(r);
        } else {
            cos = 1;
            sin = 0;
        }
        if (skewX || skewY) {
            skewX *= AlloyPaper.Matrix2D.DEG_TO_RAD;
            skewY *= AlloyPaper.Matrix2D.DEG_TO_RAD;
            this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
            this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, 0, 0);
        } else {
            this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y);
        }
        if (regX || regY) {
            this.tx -= regX * this.a + regY * this.c;
            this.ty -= regX * this.b + regY * this.d;
        }
        return this;
    },
    "append": function(a, b, c, d, tx, ty) {
        var a1 = this.a;
        var b1 = this.b;
        var c1 = this.c;
        var d1 = this.d;
        this.a = a * a1 + b * c1;
        this.b = a * b1 + b * d1;
        this.c = c * a1 + d * c1;
        this.d = c * b1 + d * d1;
        this.tx = tx * a1 + ty * c1 + this.tx;
        this.ty = tx * b1 + ty * d1 + this.ty;
        return this;
    },
    "initialize": function(a, b, c, d, tx, ty) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.tx = tx;
        this.ty = ty;
        return this;
    },
    "setValues": function(a, b, c, d, tx, ty) {
        this.a = a == null ? 1 : a;
        this.b = b || 0;
        this.c = c || 0;
        this.d = d == null ? 1 : d;
        this.tx = tx || 0;
        this.ty = ty || 0;
        return this;
    },
    "copy": function(matrix) {
        return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
    }
});

//end-------------------AlloyPaper.Matrix2D---------------------end

(function () {
    var observe = function (target, arr, callback) {
        var _observe = function (target, arr, callback) {
            if (!target.$observer) target.$observer = this;
            var $observer = target.$observer;
            var eventPropArr = [];
            if (observe.isArray(target)) {
                $observer.mock(target);
            }
            for (var prop in target) {
                if (target.hasOwnProperty(prop)) {
                    if (callback) {
                        if (observe.isArray(arr) && observe.isInArray(arr, prop)) {
                            eventPropArr.push(prop);
                            $observer.watch(target, prop);
                        } else if (observe.isString(arr) && prop == arr) {
                            eventPropArr.push(prop);
                            $observer.watch(target, prop);
                        }
                    } else {
                        eventPropArr.push(prop);
                        $observer.watch(target, prop);
                    }
                }
            }
            $observer.target = target;
            if (!$observer.propertyChangedHandler) $observer.propertyChangedHandler = [];
            var propChanged = callback ? callback : arr;
            $observer.propertyChangedHandler.push({ all: !callback, propChanged: propChanged, eventPropArr: eventPropArr });
        };
        _observe.prototype = {
            "onPropertyChanged": function (prop, value, oldValue, target, path) {
                if (value !== oldValue && this.propertyChangedHandler) {
                    var rootName = observe._getRootName(prop, path);
                    for (var i = 0, len = this.propertyChangedHandler.length; i < len; i++) {
                        var handler = this.propertyChangedHandler[i];
                        if (handler.all || observe.isInArray(handler.eventPropArr, rootName) || rootName.indexOf("Array-") === 0) {
                            handler.propChanged.call(this.target, prop, value, oldValue, path);
                        }
                    }
                }
                if (prop.indexOf("Array-") !== 0 && typeof value === "object") {
                    this.watch(target, prop, target.$observeProps.$observerPath);
                }
            },
            "mock": function (target) {
                var self = this;
                observe.methods.forEach(function (item) {
                    target[item] = function () {
                        var old = Array.prototype.slice.call(this, 0);
                        var result = Array.prototype[item].apply(this, Array.prototype.slice.call(arguments));
                        if (new RegExp("\\b" + item + "\\b").test(observe.triggerStr)) {
                            for (var cprop in this) {
                                if (this.hasOwnProperty(cprop) && !observe.isFunction(this[cprop])) {
                                    self.watch(this, cprop, this.$observeProps.$observerPath);
                                }
                            }
                            //todo
                            self.onPropertyChanged("Array-" + item, this, old, this, this.$observeProps.$observerPath);
                        }
                        return result;
                    };
                });
            },
            "watch": function (target, prop, path) {
                if (prop === "$observeProps" || prop === "$observer") return;
                if (observe.isFunction(target[prop])) return;
                if (!target.$observeProps) target.$observeProps = {};
                if (path !== undefined) {
                    target.$observeProps.$observerPath = path;
                } else {
                    target.$observeProps.$observerPath = "#";
                }
                var self = this;
                var currentValue = target.$observeProps[prop] = target[prop];
                Object.defineProperty(target, prop, {
                    get: function () {
                        return this.$observeProps[prop];
                    },
                    set: function (value) {
                        var old = this.$observeProps[prop];
                        this.$observeProps[prop] = value;
                        self.onPropertyChanged(prop, value, old, this, target.$observeProps.$observerPath);
                    }
                });
                if (typeof currentValue == "object") {
                    if (observe.isArray(currentValue)) {
                        this.mock(currentValue);
                    }
                    for (var cprop in currentValue) {
                        if (currentValue.hasOwnProperty(cprop)) {
                            this.watch(currentValue, cprop, target.$observeProps.$observerPath + "-" + prop);
                        }
                    }
                }
            }
        };
        return new _observe(target, arr, callback)
    };
    observe.methods = ["concat", "every", "filter", "forEach", "indexOf", "join", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "unshift", "toLocaleString", "toString", "size"];
    observe.triggerStr = ["concat", "pop", "push", "reverse", "shift", "sort", "splice", "unshift", "size"].join(",");
    observe.isArray = function (obj) {
        return Object.prototype.toString.call(obj) === '[object Array]';
    };
    observe.isString = function (obj) {
        return typeof obj === "string";
    };
    observe.isInArray = function (arr, item) {
        for (var i = arr.length; --i > -1;) {
            if (item === arr[i]) return true;
        }
        return false;
    };
    observe.isFunction = function (obj) {
        return Object.prototype.toString.call(obj) == '[object Function]';
    };
    observe.twoWay = function (objA, aProp, objB, bProp) {
        if (typeof objA[aProp] === "object" && typeof objB[bProp] === "object") {
            observe(objA, aProp, function (name, value) {
                objB[bProp] = this[aProp];
            })
            observe(objB, bProp, function (name, value) {
                objA[aProp] = this[bProp];
            })
        } else {
            observe(objA, aProp, function (name, value) {
                objB[bProp] = value;
            })
            observe(objB, bProp, function (name, value) {
                objA[aProp] = value;
            })
        }
    }
    observe._getRootName = function (prop, path) {
        if (path === "#") {
            return prop;
        }
        return path.split("-")[1];
    }

    observe.add = function (obj, prop, value) {
        obj[prop] = value;
        var $observer = obj.$observer;
        $observer.watch(obj, prop);
    }
    Array.prototype.size = function (length) {
        this.length = length;
    }

    AlloyPaper.Observe = observe;
})();

//begin-------------------AlloyPaper.RAF---------------------begin

AlloyPaper.RAF = Class.extend({
    "statics": {
        "ctor": function() {
            var requestAnimFrame = function() {
                return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
                function(callback, element) {
                    window.setTimeout(callback, 1e3 / 60);
                };
            }();
            var requestInterval = function(fn, delay) {
                if (!window.requestAnimationFrame && !window.webkitRequestAnimationFrame && !(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && !window.oRequestAnimationFrame && !window.msRequestAnimationFrame) return window.setInterval(fn, delay);
                var start = new Date().getTime(),
                    handle = {};

                function loop() {
                    var current = new Date().getTime(),
                        delta = current - start;
                    if (delta >= delay) {
                        fn.call();
                        start = new Date().getTime();
                    }
                    handle.value = requestAnimFrame(loop);
                }
                handle.value = requestAnimFrame(loop);
                return handle;
            };
            var clearRequestInterval = function(handle) {
                if (handle) {
                    setTimeout(function() {
                        window.cancelAnimationFrame ? window.cancelAnimationFrame(handle.value) : window.webkitCancelAnimationFrame ? window.webkitCancelAnimationFrame(handle.value) : window.webkitCancelRequestAnimationFrame ? window.webkitCancelRequestAnimationFrame(handle.value) : window.mozCancelRequestAnimationFrame ? window.mozCancelRequestAnimationFrame(handle.value) : window.oCancelRequestAnimationFrame ? window.oCancelRequestAnimationFrame(handle.value) : window.msCancelRequestAnimationFrame ? window.msCancelRequestAnimationFrame(handle.value) : clearInterval(handle);
                    }, 0);
                }
            };
            this.requestInterval = requestInterval;
            this.clearRequestInterval = clearRequestInterval;
        }
    }
});

//end-------------------AlloyPaper.RAF---------------------end


//begin-------------------AlloyPaper.To---------------------begin

AlloyPaper.To = Class.extend({
    "statics": {
        "ctor": function () {
            this.bounceOut = AlloyPaper.TWEEN.Easing.Bounce.Out,
            this.linear = AlloyPaper.TWEEN.Easing.Linear.None,
            this.quadraticIn = AlloyPaper.TWEEN.Easing.Quadratic.In,
            this.quadraticOut = AlloyPaper.TWEEN.Easing.Quadratic.Out,
            this.quadraticInOut = AlloyPaper.TWEEN.Easing.Quadratic.InOut,
            this.cubicIn = AlloyPaper.TWEEN.Easing.Cubic.In,
            this.cubicOut = AlloyPaper.TWEEN.Easing.Cubic.Out,
            this.cubicInOut = AlloyPaper.TWEEN.Easing.Cubic.InOut,
            this.quarticIn = AlloyPaper.TWEEN.Easing.Quartic.In,
            this.quarticOut = AlloyPaper.TWEEN.Easing.Quartic.Out,
            this.quarticInOut = AlloyPaper.TWEEN.Easing.Quartic.InOut,
            this.quinticIn = AlloyPaper.TWEEN.Easing.Quintic.In,
            this.quinticOut = AlloyPaper.TWEEN.Easing.Quintic.Out,
            this.quinticInOut = AlloyPaper.TWEEN.Easing.Quintic.InOut,
            this.sinusoidalIn = AlloyPaper.TWEEN.Easing.Sinusoidal.In,
            this.sinusoidalOut = AlloyPaper.TWEEN.Easing.Sinusoidal.Out,
            this.sinusoidalInOut = AlloyPaper.TWEEN.Easing.Sinusoidal.InOut,
            this.exponentialIn = AlloyPaper.TWEEN.Easing.Exponential.In,
            this.exponentialOut = AlloyPaper.TWEEN.Easing.Exponential.Out,
            this.exponentialInOut = AlloyPaper.TWEEN.Easing.Exponential.InOut,
            this.circularIn = AlloyPaper.TWEEN.Easing.Circular.In,
            this.circularOut = AlloyPaper.TWEEN.Easing.Circular.Out,
            this.circularInOut = AlloyPaper.TWEEN.Easing.Circular.InOut,
            this.elasticIn = AlloyPaper.TWEEN.Easing.Elastic.In,
            this.elasticOut = AlloyPaper.TWEEN.Easing.Elastic.Out,
            this.elasticInOut = AlloyPaper.TWEEN.Easing.Elastic.InOut,
            this.backIn = AlloyPaper.TWEEN.Easing.Back.In,
            this.backOut = AlloyPaper.TWEEN.Easing.Back.Out,
            this.backInOut = AlloyPaper.TWEEN.Easing.Back.InOut,
            this.bounceIn = AlloyPaper.TWEEN.Easing.Bounce.In,
            this.bounceOut = AlloyPaper.TWEEN.Easing.Bounce.Out,
            this.bounceInOut = AlloyPaper.TWEEN.Easing.Bounce.InOut,
            this.interpolationLinear = AlloyPaper.TWEEN.Interpolation.Linear,
            this.interpolationBezier = AlloyPaper.TWEEN.Interpolation.Bezier,
            this.interpolationCatmullRom = AlloyPaper.TWEEN.Interpolation.CatmullRom;
        },
        "get": function (element) {
            var to = new this(element);
            var stage = this.getStage(element);
            stage && stage.toList.push(to);
            return to;
        },
        "getStage": function (element) {
            if (!element.parent) return;
            if (element.parent instanceof AlloyPaper.Stage) {
                return element.parent;
            } else {
                return this.getStage(element.parent);
            }
        }
    },
    "ctor": function(element) {
        this.element = element;
        this.cmds = [];
        this.index = 0;
        this.tweens = [];
        this._pause = false;
        this.loop = setInterval(function() {
            AlloyPaper.TWEEN.update();
        }, 15);
        this.cycleCount = 0;
    },
    "to": function() {
        this.cmds.push(["to"]);
        return this;
    },
    "set": function(prop, value, time, ease) {
        this.cmds[this.cmds.length - 1].push([prop, [value, time, ease]]);
        return this;
    },
    "x": function() {
        this.cmds[this.cmds.length - 1].push(["x", arguments]);
        return this;
    },
    "y": function() {
        this.cmds[this.cmds.length - 1].push(["y", arguments]);
        return this;
    },
    "z": function() {
        this.cmds[this.cmds.length - 1].push(["z", arguments]);
        return this;
    },
    "rotation": function() {
        this.cmds[this.cmds.length - 1].push(["rotation", arguments]);
        return this;
    },
    "scaleX": function() {
        this.cmds[this.cmds.length - 1].push(["scaleX", arguments]);
        return this;
    },
    "scaleY": function() {
        this.cmds[this.cmds.length - 1].push(["scaleY", arguments]);
        return this;
    },
    "skewX": function() {
        this.cmds[this.cmds.length - 1].push(["skewX", arguments]);
        return this;
    },
    "skewY": function() {
        this.cmds[this.cmds.length - 1].push(["skewY", arguments]);
        return this;
    },
    "originX": function() {
        this.cmds[this.cmds.length - 1].push(["originX", arguments]);
        return this;
    },
    "originY": function() {
        this.cmds[this.cmds.length - 1].push(["originY", arguments]);
        return this;
    },
    "alpha": function() {
        this.cmds[this.cmds.length - 1].push(["alpha", arguments]);
        return this;
    },
    "begin": function(fn) {
        this.cmds[this.cmds.length - 1].begin = fn;
        return this;
    },
    "progress": function(fn) {
        this.cmds[this.cmds.length - 1].progress = fn;
        return this;
    },
    "end": function(fn) {
        this.cmds[this.cmds.length - 1].end = fn;
        return this;
    },
    "wait": function() {
        this.cmds.push(["wait", arguments]);
        return this;
    },
    "then": function() {
        this.cmds.push(["then", arguments]);
        return this;
    },
    "cycle": function() {
        this.cmds.push(["cycle", arguments]);
        return this;
    },
    "rubber": function() {
        this.cmds = this.cmds.concat([["to", ["scaleX", {
            "0": 1.25,
            "1": 300}], ["scaleY", {
            "0": .75,
            "1": 300}]], ["to", ["scaleX", {
            "0": .75,
            "1": 100}], ["scaleY", {
            "0": 1.25,
            "1": 100}]], ["to", ["scaleX", {
            "0": 1.15,
            "1": 100}], ["scaleY", {
            "0": .85,
            "1": 100}]], ["to", ["scaleX", {
            "0": .95,
            "1": 150}], ["scaleY", {
            "0": 1.05,
            "1": 150}]], ["to", ["scaleX", {
            "0": 1.05,
            "1": 100}], ["scaleY", {
            "0": .95,
            "1": 100}]], ["to", ["scaleX", {
            "0": 1,
            "1": 250}], ["scaleY", {
            "0": 1,
            "1": 250}]]]);
        return this;
    },
    "bounceIn": function() {
        this.cmds = this.cmds.concat([["to", ["scaleX", {
            "0": 0,
            "1": 0}], ["scaleY", {
            "0": 0,
            "1": 0}]], ["to", ["scaleX", {
            "0": 1.35,
            "1": 200}], ["scaleY", {
            "0": 1.35,
            "1": 200}]], ["to", ["scaleX", {
            "0": .9,
            "1": 100}], ["scaleY", {
            "0": .9,
            "1": 100}]], ["to", ["scaleX", {
            "0": 1.1,
            "1": 100}], ["scaleY", {
            "0": 1.1,
            "1": 100}]], ["to", ["scaleX", {
            "0": .95,
            "1": 100}], ["scaleY", {
            "0": .95,
            "1": 100}]], ["to", ["scaleX", {
            "0": 1,
            "1": 100}], ["scaleY", {
            "0": 1,
            "1": 100}]]]);
        return this;
    },
    "flipInX": function() {
        this.cmds = this.cmds.concat([["to", ["rotateX", {
            "0": -90,
            "1": 0}]], ["to", ["rotateX", {
            "0": 20,
            "1": 300}]], ["to", ["rotateX", {
            "0": -20,
            "1": 300}]], ["to", ["rotateX", {
            "0": 10,
            "1": 300}]], ["to", ["rotateX", {
            "0": -5,
            "1": 300}]], ["to", ["rotateX", {
            "0": 0,
            "1": 300}]]]);
        return this;
    },
    "zoomOut": function() {
        this.cmds = this.cmds.concat([["to", ["scaleX", {
            "0": 0,
            "1": 400}], ["scaleY", {
            "0": 0,
            "1": 400}]]]);
        return this;
    },
    "start": function() {
        if (this._pause) return;
        var len = this.cmds.length;
        if (this.index < len) {
            this.exec(this.cmds[this.index], this.index == len - 1);
        } else {
            clearInterval(this.loop);
        }
        return this;
    },
    "pause": function() {
        this._pause = true;
        for (var i = 0, len = this.tweens.length; i < len; i++) {
            this.tweens[i].pause();
        }
        if (this.currentTask == "wait") {
            this.timeout -= new Date() - this.currentTaskBegin;
            this.currentTaskBegin = new Date();
        }
    },
    "toggle": function() {
        if (this._pause) {
            this.play();
        } else {
            this.pause();
        }
    },
    "play": function() {
        this._pause = false;
        for (var i = 0, len = this.tweens.length; i < len; i++) {
            this.tweens[i].play();
        }
        var self = this;
        if (this.currentTask == "wait") {
            setTimeout(function() {
                if (self._pause) return;
                self.index++;
                self.start();
                if (self.index == self.cmds.length && self.complete) self.complete();
            }, this.timeout);
        }
    },
    "stop": function () {
        for (var i = 0, len = this.tweens.length; i < len; i++) {
            this.tweens[i].pause();
            AlloyPaper.TWEEN.remove(this.tweens[i]);
        }
        this.cmds.length = 0;

    },
    "exec": function(cmd, last) {
        var len = cmd.length,
            self = this;
        this.currentTask = cmd[0];
        switch (this.currentTask) {
        case "to":
            self.stepCompleteCount = 0;
            for (var i = 1; i < len; i++) {
                var task = cmd[i];
                var ease = task[1][2];
                var target = {};
                var prop = task[0];
                target[prop] = task[1][0];
                var t = new AlloyPaper.TWEEN.Tween(this.element).to(target, task[1][1]).onStart(function() {
                    if (cmd.start) cmd.start();
                }).onUpdate(function() {
                    if (cmd.progress) cmd.progress.call(self.element);
                    self.element[prop] = this[prop];
                }).easing(ease ? ease : AlloyPaper.To.linear).onComplete(function() {
                    self.stepCompleteCount++;
                    if (self.stepCompleteCount == len - 1) {
                        if (cmd.end) cmd.end.call(self.element);
                        if (last && self.complete) self.complete();
                        self.index++;
                        self.start();
                    }
                }).start();
                this.tweens.push(t);
            }
            break;
        case "wait":
            this.currentTaskBegin = new Date();
            this.timeout = cmd[1][0];
            setTimeout(function() {
                if (self._pause) return;
                self.index++;
                self.start();
                if (cmd.end) cmd.end.call(self.element);
                if (last && self.complete) self.complete();
            }, cmd[1][0]);
            break;
        case "then":
            var arg = cmd[1][0];
            arg.index = 0;
            arg.complete = function() {
                self.index++;
                self.start();
                if (last && self.complete) self.complete();
            };
            arg.start();
            break;
        case "cycle":
            var count = cmd[1][1];
            if (count && self.cycleCount == count) {
                self.index++;
                self.start();
                if (last && self.complete) self.complete();
            } else {
                self.cycleCount++;
                self.index = cmd[1][0];
                self.start();
            }
            break;
        }
    }
});

//end-------------------AlloyPaper.To---------------------end


//begin-------------------AlloyPaper.UID---------------------begin

AlloyPaper.UID = Class.extend({
    "statics": {
        "_nextID": 0,
        "_nextCacheID": 1,
        "get": function() {
            return this._nextID++;
        },
        "getCacheID": function() {
            return this._nextCacheID++;
        }
    }
});

//end-------------------AlloyPaper.UID---------------------end


//begin-------------------AlloyPaper.Util---------------------begin

AlloyPaper.Util = Class.extend({
    "statics": {
        "random": function(min, max) {
            return min + Math.floor(Math.random() * (max - min + 1));
        }
    }
});

//end-------------------AlloyPaper.Util---------------------end


//begin-------------------AlloyPaper.Vector2---------------------begin

AlloyPaper.Vector2 = Class.extend({
    "ctor": function(x, y) {
        this.x = x;
        this.y = y;
    },
    "copy": function() {
        return new AlloyPaper.Vector2(this.x, this.y);
    },
    "length": function() {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    },
    "sqrLength": function() {
        return this.x * this.x + this.y * this.y;
    },
    "normalize": function() {
        var inv = 1 / this.length();
        return new AlloyPaper.Vector2(this.x * inv, this.y * inv);
    },
    "negate": function() {
        return new AlloyPaper.Vector2(-this.x, -this.y);
    },
    "add": function(v) {
        this.x += v.x;
        this.y += v.y;
    },
    "subtract": function(v) {
        return new AlloyPaper.Vector2(this.x - v.x, this.y - v.y);
    },
    "multiply": function(f) {
        return new AlloyPaper.Vector2(this.x * f, this.y * f);
    },
    "divide": function(f) {
        var invf = 1 / f;
        return new AlloyPaper.Vector2(this.x * invf, this.y * invf);
    },
    "dot": function(v) {
        return this.x * v.x + this.y * v.y;
    }
});

//end-------------------AlloyPaper.Vector2---------------------end


//begin-------------------AlloyPaper.Renderer---------------------begin

AlloyPaper.Renderer = Class.extend({
    "ctor": function (stage, openWebGL) {
        this.stage = stage;
        this.objs = [];
        this.width = this.stage.width;
        this.height = this.stage.height;
        this.mainCanvas = this.stage.canvas;
        var canvasSupport = !! window.CanvasRenderingContext2D,
            webglSupport = function() {
                try {
                    var canvas = document.createElement("canvas");
                    return !!(window.WebGLRenderingContext && (canvas.getContext("webgl") || canvas.getContext("experimental-webgl")));
                } catch (e) {
                    return false;
                }
            }();
        if (webglSupport && openWebGL) {
            this.renderingEngine = new AlloyPaper.WebGLRenderer(this.stage.canvas);
        } else {
            if (canvasSupport) {
                this.renderingEngine = new AlloyPaper.CanvasRenderer(this.stage.canvas);
            } else {
                throw "your browser does not support canvas and webgl ";
            }
        }
        this.mainCtx = this.renderingEngine.ctx;
    },
    "update": function() {
        var objs = this.objs,
            ctx = this.mainCtx,
            engine = this.renderingEngine;
        objs.length = 0;
        this.computeMatrix();
        engine.clear();
        var l = objs.length;
        for (var m = 0; m < l; m++) {
            engine.renderObj(ctx, objs[m]);
        }
    },
    "computeMatrix": function() {
        for (var i = 0, len = this.stage.children.length; i < len; i++) {
            this._computeMatrix(this.stage.children[i]);
        }
    },
    "initComplex": function(o) {
        o.complexCompositeOperation = this._getCompositeOperation(o);
        o.complexAlpha = this._getAlpha(o, 1);
    },
    "_computeMatrix": function(o, mtx) {
        if (!o.isVisible()) {
            return;
        }
        if (mtx) {
            o._matrix.initialize(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
        } else {
            o._matrix.initialize(1, 0, 0, 1, 0, 0);
        }
        if (o instanceof AlloyPaper.Shape) {
            o._matrix.appendTransform(o.x, o.y, 1, 1, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
        } else {
            o._matrix.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
        }
        if (o instanceof AlloyPaper.Container) {
            var list = o.children,
                len = list.length,
                i = 0;
            for (; i < len; i++) {
                this._computeMatrix(list[i], o._matrix);
            }
        } else {
            if (o instanceof AlloyPaper.Graphics || o instanceof AlloyPaper.Text) {
                this.objs.push(o);
                this.initComplex(o);
            } else {
                o.initAABB();
                if (this.isInStage(o)) {
                    this.objs.push(o);
                    this.initComplex(o);
                }
            }
        }
    },
    "_getCompositeOperation": function(o) {
        if (o.compositeOperation) return o.compositeOperation;
        if (o.parent) return this._getCompositeOperation(o.parent);
    },
    "_getAlpha": function(o, alpha) {
        var result = o.alpha * alpha;
        if (o.parent) {
            return this._getAlpha(o.parent, result);
        }
        return result;
    },
    "isInStage": function(o) {
        return this.collisionBetweenAABB(o.AABB, this.stage.AABB);
    },
    "collisionBetweenAABB": function(AABB1, AABB2) {
        var maxX = AABB1[0] + AABB1[2];
        if (maxX < AABB2[0]) return false;
        var minX = AABB1[0];
        if (minX > AABB2[0] + AABB2[2]) return false;
        var maxY = AABB1[1] + AABB1[3];
        if (maxY < AABB2[1]) return false;
        var minY = AABB1[1];
        if (minY > AABB2[1] + AABB2[3]) return false;
        return true;
    }
});

//end-------------------AlloyPaper.Renderer---------------------end


//begin-------------------AlloyPaper.CanvasRenderer---------------------begin

AlloyPaper.CanvasRenderer = Class.extend({
    "ctor": function(canvas) {
        if (canvas) {
            this.canvas = canvas;
            this.ctx = this.canvas.getContext("2d");
            this.height = this.canvas.height;
            this.width = this.canvas.width;
        }
    },
    "hitAABB": function(ctx, o, evt, type) {
        var list = o.children.slice(0),
            l = list.length;
        for (var i = l - 1; i >= 0; i--) {
            var child = list[i];
            if (!this.isbindingEvent(child)) continue;
            var target = this._hitAABB(ctx, child, evt, type);
            if (target) return target;
        }
    },
    "_hitAABB": function(ctx, o, evt, type) {
        if (!o.isVisible()) {
            return;
        }
        if (o instanceof AlloyPaper.Container) {
            var list = o.children.slice(0),
                l = list.length;
            for (var i = l - 1; i >= 0; i--) {
                var child = list[i];
                var target = this._hitAABB(ctx, child, evt, type);
                if (target) return target;
            }
        } else {
            if (o.AABB && this.checkPointInAABB(evt.stageX, evt.stageY, o.AABB)) {
                this._bubbleEvent(o, type, evt);
                return o;
            }
        }
    },
    "hitRender": function(ctx, o, evt, type) {
        var mtx = o._hitMatrix;
        var list = o.children.slice(0),
            l = list.length;
        for (var i = l - 1; i >= 0; i--) {
            var child = list[i];
            mtx.initialize(1, 0, 0, 1, 0, 0);
            mtx.appendTransform(o.x - evt.stageX, o.y - evt.stageY, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
            if (!this.isbindingEvent(child)) continue;
            ctx.save();
            var target = this._hitRender(ctx, child, mtx, evt, type);
            ctx.restore();
            if (target) return target;
        }
    },
    "_hitRender": function(ctx, o, mtx, evt, type) {
        ctx.clearRect(0, 0, 2, 2);
        if (!o.isVisible()) {
            return;
        }
        if (mtx) {
            o._hitMatrix.initialize(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
        } else {
            o._hitMatrix.initialize(1, 0, 0, 1, 0, 0);
        }
        mtx = o._hitMatrix;
        if (o instanceof AlloyPaper.Shape) {
            mtx.appendTransform(o.x, o.y, 1, 1, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
        } else {
            mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
        }
        var mmyCanvas = o.cacheCanvas || o.txtCanvas || o.shapeCanvas;
        if (mmyCanvas) {
            ctx.globalAlpha = o.complexAlpha;
            ctx.globalCompositeOperation = o.complexCompositeOperation;
            ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
            ctx.drawImage(mmyCanvas, 0, 0);
        } else if (o instanceof AlloyPaper.Container) {
            var list = o.children.slice(0),
                l = list.length;
            for (var i = l - 1; i >= 0; i--) {
                ctx.save();
                var target = this._hitRender(ctx, list[i], mtx, evt, type);
                if (target) return target;
                ctx.restore();
            }
        } else if (o instanceof AlloyPaper.Bitmap || o instanceof AlloyPaper.Sprite) {
            ctx.globalAlpha = o.complexAlpha;
            ctx.globalCompositeOperation = o.complexCompositeOperation;
            var rect = o.rect;
            ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
            ctx.drawImage(o.img, rect[0], rect[1], rect[2], rect[3], 0, 0, rect[2], rect[3]);
        } else if (o instanceof AlloyPaper.Graphics) {
            ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
            o.draw(ctx);
        }
        if (ctx.getImageData(0, 0, 1, 1).data[3] > 1 && !(o instanceof AlloyPaper.Container)) {
            this._bubbleEvent(o, type, evt);
            return o;
        }
    },
    "_bubbleEvent": function(o, type, event) {
        var result = o.execEvent(type, event);
        if (result !== false) {
            if (o.parent && o.parent.events && o.parent.events[type] && o.parent.events[type].length > 0 && o.parent.baseInstanceof !== "Stage") {
                this._bubbleEvent(o.parent, type, event);
            }
        }
    },
    "isbindingEvent": function(obj) {
        if (Object.keys(obj.events).length !== 0) return true;
        if (obj instanceof AlloyPaper.Container) {
            for (var i = 0, len = obj.children.length; i < len; i++) {
                var child = obj.children[i];
                if (child instanceof AlloyPaper.Container) {
                    return this.isbindingEvent(child);
                } else {
                    if (Object.keys(child.events).length !== 0) return true;
                }
            }
        }
        return false;
    },
    "clear": function() {
        this.ctx.clearRect(0, 0, this.width, this.height);
    },
    "renderObj": function(ctx, o) {
        var mtx = o._matrix;
        ctx.save();
        ctx.globalAlpha = o.complexAlpha;
        ctx.globalCompositeOperation = o.complexCompositeOperation;
        o.shadow && this._applyShadow(ctx, o.shadow);
        var mmyCanvas = o.cacheCanvas || o.txtCanvas || o.shapeCanvas;
        if (mmyCanvas) {
            ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
            ctx.drawImage(mmyCanvas, 0, 0);
        } else if (o instanceof AlloyPaper.Bitmap || o instanceof AlloyPaper.Sprite) {
            if (o._clipFn) {
                ctx.beginPath();
                o._clipFn.call(ctx);
                ctx.closePath();
                ctx.clip();
            } 
            var rect = o.rect;
            ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
            ctx.drawImage(o.img, rect[0], rect[1], rect[2], rect[3], 0, 0, rect[2], rect[3]);
        } else if (o instanceof AlloyPaper.Graphics || o instanceof AlloyPaper.Text) {
            ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
            o.draw(ctx);
        }
        ctx.restore();
    },
    "_applyShadow" : function(ctx, shadow) {
        ctx.shadowColor = shadow.color || "transparent";
        ctx.shadowOffsetX = shadow.offsetX||0;
        ctx.shadowOffsetY = shadow.offsetY||0;
        ctx.shadowBlur = shadow.blur||0;
    },
    "clearBackUpCanvasCache": function() {},
    "checkPointInAABB": function(x, y, AABB) {
        var minX = AABB[0];
        if (x < minX) return false;
        var minY = AABB[1];
        if (y < minY) return false;
        var maxX = minX + AABB[2];
        if (x > maxX) return false;
        var maxY = minY + AABB[3];
        if (y > maxY) return false;
        return true;
    }
});

//end-------------------AlloyPaper.CanvasRenderer---------------------end

AlloyPaper.WebGLRenderer = Class.extend({
    "ctor": function(canvas) {
        this.surface = canvas;
        this.snapToPixel = true;
        this.canvasRenderer = new AlloyPaper.CanvasRenderer();
        this.textureCache = {};
        this.textureCanvasCache = {};
        this.initSurface(this.surface);
    },
    "initSurface": function(surface) {
        var options = {
            depth: false,
            alpha: true,
            preserveDrawingBuffer: true,
            antialias: false,
            premultipliedAlpha: true
        };
        var ctx = undefined;
        try {
            ctx = surface.ctx = surface.getContext("webgl", options) || surface.getContext("experimental-webgl", options);
            ctx.viewportWidth = surface.width;
            ctx.viewportHeight = surface.height;
        } catch (e) {}
        if (!ctx) {
            alert("Could not initialise WebGL. Make sure you've updated your browser, or try a different one like Google Chrome.");
        }
        var textureShader = ctx.createShader(ctx.FRAGMENT_SHADER);
        ctx.shaderSource(textureShader, "" + "precision mediump float;\n" + "varying vec3 vTextureCoord;\n" + "varying float vAlpha;\n" + "uniform float uAlpha;\n" + "uniform sampler2D uSampler0;\n" + "void main(void) { \n" + "vec4 color = texture2D(uSampler0, vTextureCoord.st);  \n" + "gl_FragColor = vec4(color.rgb, color.a * vAlpha);\n" + "}");
        ctx.compileShader(textureShader);
        if (!ctx.getShaderParameter(textureShader, ctx.COMPILE_STATUS)) {
            alert(ctx.getShaderInfoLog(textureShader));
        }
        var vertexShader = ctx.createShader(ctx.VERTEX_SHADER);
        ctx.shaderSource(vertexShader, "" + "attribute vec2 aVertexPosition;\n" + "attribute vec3 aTextureCoord;\n" + "attribute float aAlpha;\n" + "uniform bool uSnapToPixel;\n" + "const mat4 pMatrix = mat4(" + 2 / ctx.viewportWidth + ",0,0,0, 0," + -2 / ctx.viewportHeight + ",0,0, 0,0,-2,   0, -1,1,-1,1); \n" + "varying vec3 vTextureCoord;\n" + "varying float vAlpha;\n" + "void main(void) { \n" + "vTextureCoord = aTextureCoord; \n" + "vAlpha = aAlpha; \n" + "gl_Position = pMatrix * vec4(aVertexPosition.x,aVertexPosition.y,0.0, 1.0);\n" + "}");
        ctx.compileShader(vertexShader);
        if (!ctx.getShaderParameter(vertexShader, ctx.COMPILE_STATUS)) {
            alert(ctx.getShaderInfoLog(vertexShader));
        }
        var program = surface.shader = ctx.createProgram();
        ctx.attachShader(program, vertexShader);
        ctx.attachShader(program, textureShader);
        ctx.linkProgram(program);
        if (!ctx.getProgramParameter(program, ctx.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }
        ctx.enableVertexAttribArray(program.vertexPositionAttribute = ctx.getAttribLocation(program, "aVertexPosition"));
        ctx.enableVertexAttribArray(program.uvCoordAttribute = ctx.getAttribLocation(program, "aTextureCoord"));
        ctx.enableVertexAttribArray(program.colorAttribute = ctx.getAttribLocation(program, "aAlpha"));
        program.alphaUniform = ctx.getUniformLocation(program, "uAlpha");
        program.snapToUniform = ctx.getUniformLocation(program, "uSnapToPixel");
        ctx.useProgram(program);
        this._vertexDataCount = 5;
        this._degToRad = Math.PI / 180;
        if (window.Float32Array) {
            this.vertices = new window.Float32Array(this._vertexDataCount * 4);
        } else {
            this.vertices = new Array(this._vertexDataCount * 4);
        }
        this.arrayBuffer = ctx.createBuffer();
        this.indexBuffer = ctx.createBuffer();
        ctx.bindBuffer(ctx.ARRAY_BUFFER, this.arrayBuffer);
        ctx.bindBuffer(ctx.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
        var byteCount = this._vertexDataCount * 4;
        ctx.vertexAttribPointer(program.vertexPositionAttribute, 2, ctx.FLOAT, 0, byteCount, 0);
        ctx.vertexAttribPointer(program.uvCoordAttribute, 2, ctx.FLOAT, 0, byteCount, 2 * 4);
        ctx.vertexAttribPointer(program.colorAttribute, 1, ctx.FLOAT, 0, byteCount, 4 * 4);
        if (window.Uint16Array) {
            this.indices = new window.Uint16Array(6);
        } else {
            this.indices = new Array(6);
        }
        for (var i = 0, l = this.indices.length; i < l; i += 6) {
            var j = i * 4 / 6;
            this.indices.set([j, j + 1, j + 2, j, j + 2, j + 3], i);
        }
        ctx.bufferData(ctx.ARRAY_BUFFER, this.vertices, ctx.STREAM_DRAW);
        ctx.bufferData(ctx.ELEMENT_ARRAY_BUFFER, this.indices, ctx.STATIC_DRAW);
        ctx.viewport(0, 0, ctx.viewportWidth, ctx.viewportHeight);
        ctx.colorMask(true, true, true, true);
        ctx.blendFuncSeparate(ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA, ctx.SRC_ALPHA, ctx.ONE);
        ctx.enable(ctx.BLEND);
        ctx.disable(ctx.DEPTH_TEST);
        surface.init = true;
        this.ctx = ctx;
    },
    "_initTexture": function(src, ctx) {
        if (!this.textureCache[src.src]) {
            src.glTexture = ctx.createTexture();
            src.glTexture.image = src;
            ctx.activeTexture(ctx.TEXTURE0);
            ctx.bindTexture(ctx.TEXTURE_2D, src.glTexture);
            ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, src.glTexture.image);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
            this.textureCache[src.src] = src.glTexture;
            ctx.uniform1i(ctx.getUniformLocation(ctx.canvas.shader, "uSampler0"), 0);
        } else {
            src.glTexture = this.textureCache[src.src];
            ctx.activeTexture(ctx.TEXTURE0);
            ctx.bindTexture(ctx.TEXTURE_2D, src.glTexture);
        }
    },
    "_initCache": function(o, src, ctx) {
        if (!this.textureCanvasCache[o.cacheID]) {
            this.textureCanvasCache[this._preCacheId] = null;
            src.glTexture = ctx.createTexture();
            src.glTexture.image = src;
            ctx.activeTexture(ctx.TEXTURE0);
            ctx.bindTexture(ctx.TEXTURE_2D, src.glTexture);
            ctx.texImage2D(ctx.TEXTURE_2D, 0, ctx.RGBA, ctx.RGBA, ctx.UNSIGNED_BYTE, src.glTexture.image);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MAG_FILTER, ctx.LINEAR);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_MIN_FILTER, ctx.LINEAR);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_S, ctx.CLAMP_TO_EDGE);
            ctx.texParameteri(ctx.TEXTURE_2D, ctx.TEXTURE_WRAP_T, ctx.CLAMP_TO_EDGE);
            ctx.uniform1i(ctx.getUniformLocation(ctx.canvas.shader, "uSampler0"), 0);
            this.textureCanvasCache[o.cacheID] = src.glTexture;
        } else {
            src.glTexture = this.textureCanvasCache[o.cacheID];
            ctx.activeTexture(ctx.TEXTURE0);
            ctx.bindTexture(ctx.TEXTURE_2D, src.glTexture);
        }
    },
    "updateCache": function(ctx, o, w, h) {
        ctx.clearRect(0, 0, w + 1, h + 1);
        this.renderCache(ctx, o);
    },
    "renderCache": function(ctx, o) {
        if (!o.isVisible()) {
            return;
        }
        if (o instanceof AlloyPaper.Container || o instanceof AlloyPaper.Stage) {
            var list = o.children.slice(0);
            for (var i = 0, l = list.length; i < l; i++) {
                ctx.save();
                this.canvasRenderer.render(ctx, list[i]);
                ctx.restore();
            }
        } else if (o instanceof AlloyPaper.Bitmap || o instanceof AlloyPaper.Sprite) {
            var rect = o.rect;
            ctx.drawImage(o.img, rect[0], rect[1], rect[2], rect[3], 0, 0, rect[2], rect[3]);
        } else if (o.txtCanvas) {
            ctx.drawImage(o.txtCanvas, 0, 0);
        } else if (o.shapeCanvas) {
            ctx.drawImage(o.shapeCanvas, 0, 0);
        }
    },
    "clear": function() {
        this.ctx.clear(this.ctx.COLOR_BUFFER_BIT);
    },
    "renderObj": function(ctx, o) {
        var mtx = o._matrix,
            leftSide = 0,
            topSide = 0,
            rightSide = 0,
            bottomSide = 0;
        var uFrame = 0,
            vFrame = 0,
            u = 1,
            v = 1,
            img = 0;
        if (o.complexCompositeOperation === "lighter") {
            ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE);
        } else {
            ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA);
        }
        var mmyCanvas = o.cacheCanvas || o.txtCanvas || o.shapeCanvas;
        if (mmyCanvas) {
            this._initCache(o, mmyCanvas, ctx);
            rightSide = leftSide + mmyCanvas.width;
            bottomSide = topSide + mmyCanvas.height;
        } else if (o instanceof AlloyPaper.Bitmap || o instanceof AlloyPaper.Sprite) {
            var rect = o.rect;
            img = o.img;
            this._initTexture(img, ctx);
            rightSide = leftSide + rect[2];
            bottomSide = topSide + rect[3];
            u = rect[2] / img.width;
            v = rect[3] / img.height;
            uFrame = rect[0] / img.width;
            vFrame = rect[1] / img.height;
        }
        var a = mtx.a,
            b = mtx.b,
            c = mtx.c,
            d = mtx.d,
            tx = mtx.tx,
            ty = mtx.ty,
            lma = leftSide * a,
            lmb = leftSide * b,
            tmc = topSide * c,
            tmd = topSide * d,
            rma = rightSide * a,
            rmb = rightSide * b,
            bmc = bottomSide * c,
            bmd = bottomSide * d;
        var alpha = o.complexAlpha;
        this.vertices.set([lma + tmc + tx, lmb + tmd + ty, uFrame, vFrame, alpha, lma + bmc + tx, lmb + bmd + ty, uFrame, vFrame + v, alpha, rma + bmc + tx, rmb + bmd + ty, uFrame + u, vFrame + v, alpha, rma + tmc + tx, rmb + tmd + ty, uFrame + u, vFrame, alpha], 0);
        ctx.bufferSubData(ctx.ARRAY_BUFFER, 0, this.vertices);
        ctx.drawElements(ctx.TRIANGLES, 6, ctx.UNSIGNED_SHORT, 0);
    },
    "clearBackUpCanvasCache": function() {
        this.textureCanvasCache[1] = null;
    }
});

//begin-------------------AlloyPaper.DisplayObject---------------------begin

AlloyPaper.DisplayObject = Class.extend({
    "ctor": function() {
        this.alpha = this.scaleX = this.scaleY = this.scale = 1;
        this.x = this.y = this.rotation = this.originX = this.originY = this.skewX = this.skewY = this.width = this.height = this.regX = this.regY = 0;
        this.textureReady = true;
        this.visible = true;
        this._matrix = new AlloyPaper.Matrix2D();
        this._hitMatrix = new AlloyPaper.Matrix2D();
        this.events = {};
        this.id = AlloyPaper.UID.get();
        this.cacheID = 0;
        this.baseInstanceof = "DisplayObject";
        this.tickFPS = 60;
        var self = this;
        this._watch(this, "originX", function(prop, value) {
            if (typeof value === "string") {
                self.regX = parseInt(value);
            } else {
                self.regX = self.width * value;
            }
        });
        this._watch(this, "originY", function(prop, value) {
            if (typeof value === "string") {
                self.regY = parseInt(value);
            } else {
                self.regY = self.height * value;
            }
        });
        this._watch(this, "filter", function(prop, value) {
            self.setFilter.apply(self, value);
        });
        this._watch(this, "scale", function(prop, value) {
            this.scaleX = this.scaleY = this.scale;
        });
        this.cursor = "default";
        this.onHover(function () {
            //this._setCursor(this, this.cursor);
        }, function () {
            this._setCursor(this, AlloyPaper.DefaultCursor);
        });
    },
    "_watch": function(target, prop, onPropertyChanged) {
        if (typeof prop === "string") {
            target["__" + prop] = this[prop];
            Object.defineProperty(target, prop, {
                get: function() {
                    return this["__" + prop];
                },
                set: function(value) {
                    this["__" + prop] = value;
                    onPropertyChanged.apply(target, [prop, value]);
                }
            });
        } else {
            for (var i = 0, len = prop.length; i < len; i++) {
                var propName = prop[i];
                target["__" + propName] = this[propName];
                (function(propName) {
                    Object.defineProperty(target, propName, {
                        get: function() {
                            return this["__" + propName];
                        },
                        set: function(value) {
                            this["__" + propName] = value;
                            onPropertyChanged.apply(target, [propName, value]);
                        }
                    });
                })(propName);
            }
        }
    },
    "isVisible": function() {
        return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && this.textureReady);
    },
    "on": function(type, fn) {
        this.events[type] || (this.events[type] = []);
        this.events[type].push(fn);
    },
    "off": function (type, fn) {
        var fns=this.events[type];
        if (fns) {
            var i = 0, len = fns.length;
            for (; i < len; i++) {
                if (fns[i] === fn) {
                    fns.splice(i, 1);
                    break;
                }
            }
        }

    },
    "execEvent": function (type, event) {
        if (this.events) {
            var fns = this.events[type],
                result = true;
            if (fns) {
                for (var i = 0, len = fns.length; i < len; i++) {
                    result = fns[i].call(this, event);
                }
            }
            return result;
        }
    },
    "_setCursor": function (obj, type) {
        if (obj) {
            if (obj.parent instanceof AlloyPaper.Stage) {
                obj.parent.setCursor(type);
            } else {
                this._setCursor(obj.parent, type);
            }
        }
    },
    "clone": function() {
        var o = new AlloyPaper.DisplayObject();
        this.cloneProps(o);
        return o;
    },
    "cloneProps": function(o) {
        o.visible = this.visible;
        o.alpha = this.alpha;
        o.originX = this.originX;
        o.originY = this.originY;
        o.rotation = this.rotation;
        o.scaleX = this.scaleX;
        o.scaleY = this.scaleY;
        o.skewX = this.skewX;
        o.skewY = this.skewY;
        o.x = this.x;
        o.y = this.y;
        o.regX = this.regX;
        o.regY = this.regY;
    },
    "cache": function() {
        if (!this.cacheCanvas) {
            this.cacheCanvas = document.createElement("canvas");
            var bound = this.getBound();
            this.cacheCanvas.width = bound.width;
            this.cacheCanvas.height = bound.height;
            this.cacheCtx = this.cacheCanvas.getContext("2d");
        }
        this.cacheID = AlloyPaper.UID.getCacheID();
        this.updateCache(this.cacheCtx, this, bound.width, bound.width);
    },
    "uncache": function() {
        this.cacheCanvas = null;
        this.cacheCtx = null;
        this.cacheID = null;
    },
    "setFilter": function(r, g, b, a) {
        if (this.width === 0 || this.height === 0) return;
        this.uncache();
        this.cache();
        var imageData = this.cacheCtx.getImageData(0, 0, this.cacheCanvas.width, this.cacheCanvas.height);
        var pix = imageData.data;
        for (var i = 0, n = pix.length; i < n; i += 4) {
            if (pix[i + 3] > 0) {
                pix[i] *= r;
                pix[i + 1] *= g;
                pix[i + 2] *= b;
                pix[i + 3] *= a;
            }
        }
        this.cacheCtx.putImageData(imageData, 0, 0);
    },
    "getBound": function() {
        return {
            width: this.width,
            height: this.height
        };
    },
    "toCenter": function() {
        this.originX = .5;
        this.originY = .5;
        this.x = this.parent.width / 2;
        this.y = this.parent.height / 2;
    },
    "destroy": function() {
        this.cacheCanvas = null;
        this.cacheCtx = null;
        this.cacheID = null;
        this._matrix = null;
        this.events = null;
        if (this.parent) {
            this.parent.remove(this);
        }
    },
    "initAABB": function() {
        var x,
            y,
            width = this.width,
            height = this.height,
            mtx = this._matrix;
        var x_a = width * mtx.a,
            x_b = width * mtx.b;
        var y_c = height * mtx.c,
            y_d = height * mtx.d;
        var tx = mtx.tx,
            ty = mtx.ty;
        var minX = tx,
            maxX = tx,
            minY = ty,
            maxY = ty;
        if ((x = x_a + tx) < minX) {
            minX = x;
        } else if (x > maxX) {
            maxX = x;
        }
        if ((x = x_a + y_c + tx) < minX) {
            minX = x;
        } else if (x > maxX) {
            maxX = x;
        }
        if ((x = y_c + tx) < minX) {
            minX = x;
        } else if (x > maxX) {
            maxX = x;
        }
        if ((y = x_b + ty) < minY) {
            minY = y;
        } else if (y > maxY) {
            maxY = y;
        }
        if ((y = x_b + y_d + ty) < minY) {
            minY = y;
        } else if (y > maxY) {
            maxY = y;
        }
        if ((y = y_d + ty) < minY) {
            minY = y;
        } else if (y > maxY) {
            maxY = y;
        }
        this.AABB = [minX, minY, maxX - minX, maxY - minY];
        this.rectPoints = [{
            x: tx,
            y: ty},{
            x: x_a + tx,
            y: x_b + ty},{
            x: x_a + y_c + tx,
            y: x_b + y_d + ty},{
            x: y_c + tx,
            y: y_d + ty}];
    },
    "updateCache": function(ctx, o, w, h) {
        ctx.clearRect(0, 0, w + 1, h + 1);
        this.renderCache(ctx, o);
    },
    "renderCache": function(ctx, o) {
        if (!o.isVisible()) {
            return;
        }
        if (o instanceof AlloyPaper.Container || o instanceof AlloyPaper.Stage) {
            var list = o.children.slice(0);
            for (var i = 0, l = list.length; i < l; i++) {
                ctx.save();
                this.render(ctx, list[i]);
                ctx.restore();
            }
        } else if (o instanceof AlloyPaper.Bitmap || o instanceof AlloyPaper.Sprite) {
            var rect = o.rect;
            ctx.drawImage(o.img, rect[0], rect[1], rect[2], rect[3], 0, 0, rect[2], rect[3]);
        } else if (o.txtCanvas) {
            ctx.drawImage(o.txtCanvas, 0, 0);
        } else if (o.shapeCanvas) {
            ctx.drawImage(o.shapeCanvas, 0, 0);
        }
    },
    "onClick": function(fn) {
        this.on("click", fn);
    },
    "onMouseDown": function(fn) {
        this.on("pressdown", fn);
    },
    "onMouseMove": function(fn) {
        this.on("mousemove", fn);
    },
    "onMouseUp": function(fn) {
        this.on("pressup", fn);
    },
    "onMouseOver": function(fn) {
        this.on("mouseover", fn);
    },
    "onMouseOut": function(fn) {
        this.on("mouseout", fn);
    },
    "onHover": function(over, out) {
        this.on("mouseover", over);
        this.on("mouseout", out);
    },
    "onPressDown": function(fn) {
        this.on("pressdown", fn);
    },
    "onPressMove": function(fn) {
        this.on("pressmove", fn);
    },
    "onPressUp": function(fn) {
        this.on("pressup", fn);
    },
    "onMouseWheel": function(fn) {
        this.on("mousewheel", fn);
    },
    "onTouchStart": function(fn) {
        this.on("pressdown", fn);
    },
    "onTouchMove": function(fn) {
        this.on("pressmove", fn);
    },
    "onTouchEnd": function(fn) {
        this.on("pressup", fn);
    },
    "onTouchCancel": function () {
        this.on("touchcancel", fn);
    },
    "onDbClick": function(fn) {
        this.on("dblclick", fn);
    },
    "addEventListener": function (type, handler) {
        this.on(this._normalizeEventType(type), handler);
    },
    "removeEventListener": function (type, handler) {
        this.off(this._normalizeEventType(type), handler);
    },
    "_normalizeEventType": function (type) {
        var newType = { "touchstart": "pressdown", "touchmove": "pressmove", "touchend": "pressup" }[type];
        if (newType) return newType;
        return type;
    }
});

//end-------------------AlloyPaper.DisplayObject---------------------end

AlloyPaper.Bitmap = AlloyPaper.DisplayObject.extend({
    "ctor": function(img) {
        this._super();
        Object.defineProperty(this, "rect", {
            get: function () {
                return this["__rect"];
            },
            set: function (value) {
                this["__rect"] = value;
                this.width = value[2];
                this.height = value[3];
                this.regX = value[2] * this.originX;
                this.regY = value[3] * this.originY;
            }
        });
        if (arguments.length === 0) return;
        if (typeof img == "string") {
            this._initWithSrc(img);
            this.imgSrc = img;
        } else {
            this._init(img);
            this.imgSrc = img.src;
        }
    },
    "_initWithSrc": function(img) {
        var cacheImg = AlloyPaper.Cache[img];
        if (cacheImg) {
            this._init(cacheImg);
        } else {
            var self = this;
            this.textureReady = false;
            this.img = document.createElement("img");
            this.img.crossOrigin = "Anonymous";
            this.img.onload = function () {
                if (!self.rect) self.rect = [0, 0, self.img.width, self.img.height];
                AlloyPaper.Cache[img] = self.img;
                self.textureReady = true;
                self.imageLoadHandle && self.imageLoadHandle();
                if (self.filter) self.filter = self.filter;
            };
            this.img.src = img;
        }
    },
    "_init": function(img) {
        if (!img) return;
        this.img = img;
        this.img.crossOrigin = "Anonymous";
        this.width = img.width;
        this.height = img.height;
        this.rect = [0, 0, img.width, img.height];
    },
    "useImage": function(img) {
        if (typeof img == "string") {
            this._initWithSrc(img);
        } else {
            this._init(img);
            this.imageLoadHandle && this.imageLoadHandle();
        }
    },
    "onImageLoad": function(fn) {
        this.imageLoadHandle = fn;
    },
    "clone": function () {
        if (this.textureReady) {
            var o = new AlloyPaper.Bitmap(this.img);
            o.rect = this.rect.slice(0);
            this.cloneProps(o);
            return o;
        } else {
            var o = new AlloyPaper.Bitmap(this.imgSrc);
            this.rect&&(o.rect = this.rect.slice(0));
            this.cloneProps(o);
            return o;
        }
    },
    "clip": function (fn) {
        this._clipFn = fn;
    },
    "flipX": function() {},
    "flipY": function() {}
});

//begin-------------------AlloyPaper.Container---------------------begin

AlloyPaper.Container = AlloyPaper.DisplayObject.extend({
    "ctor": function() {
        this._super();
        this.children = [];
        this.baseInstanceof = "Container";
    },
    "add": function(obj) {
        var len = arguments.length;
        if (len > 1) {
            for (var i = 0; i < len; i++) {
                var item = arguments[i];
                if (item) {
                    this.children.push(item);
                    item.parent = this;
                }
            }
        } else {
            if (obj) {
                this.children.push(obj);
                obj.parent = this;
            }
        }
    },
    "remove": function(obj) {
        var len = arguments.length,
            childLen = this.children.length;
        if (len > 1) {
            for (var j = 0; j < len; j++) {
                var currentObj = arguments[j];
                for (var k = childLen; --k >= 0;) {
                    if (currentObj&&this.children[k].id == currentObj.id) {
                        currentObj.parent = null;
                        this.children.splice(k, 1);
                        break;
                    }
                }
            }
        } else {
            for (var i = childLen; --i >= 0;) {
                if (obj&&this.children[i].id == obj.id) {
                    obj.parent = null;
                    this.children.splice(i, 1);
                    break;
                }
            }
        }
    },
    "clone": function() {
        var o = new AlloyPaper.Container();
        this.cloneProps(o);
        var arr = o.children = [];
        for (var i = this.children.length - 1; i > -1; i--) {
            var clone = this.children[i].clone();
            arr.unshift(clone);
        }
        return o;
    },
    "removeAll": function() {
        var kids = this.children;
        while (kids.length) {
            kids.pop().parent = null;
        }
    },
    "destroy": function() {
        this._super();
        var kids = this.children;
        while (kids.length) {
            var kid = kids.pop();
            kid.destroy();
            kid = null;
        }
    },
    "swapChildrenAt": function(index1, index2) {
        var kids = this.children;
        var o1 = kids[index1];
        var o2 = kids[index2];
        if (!o1 || !o2) {
            return;
        }
        kids[index1] = o2;
        kids[index2] = o1;
    },
    "swapChildren": function(child1, child2) {
        var kids = this.children;
        var index1, index2;
        for (var i = 0, l = kids.length; i < l; i++) {
            if (kids[i] == child1) {
                index1 = i;
            }
            if (kids[i] == child2) {
                index2 = i;
            }
            if (index1 != null && index2 != null) {
                break;
            }
        }
        if (i == l) {
            return;
        }
        kids[index1] = child2;
        kids[index2] = child1;
    },
    "swapToTop": function(child) {
        this.swapChildren(child, this.children[this.children.length - 1]);
    }
});

//end-------------------AlloyPaper.Container---------------------end


//begin-------------------AlloyPaper.Graphics---------------------begin

AlloyPaper.Graphics = AlloyPaper.DisplayObject.extend({
    "ctor": function() {
        this._super();
        this.cmds = [];
        this.assMethod = ["fillStyle", "strokeStyle", "lineWidth"];
    },
    "draw": function(ctx) {
        for (var i = 0, len = this.cmds.length; i < len; i++) {
            var cmd = this.cmds[i];
            if (this.assMethod.join("-").match(new RegExp("\\b" + cmd[0] + "\\b", "g"))) {
                ctx[cmd[0]] = cmd[1][0];
            } else {
                ctx[cmd[0]].apply(ctx, Array.prototype.slice.call(cmd[1]));
            }
        }
    },
    "clearRect": function(x, y, width, height) {
        this.cmds.push(["clearRect", arguments]);
        return this;
    },
    "clear": function() {
        this.cmds.length = 0;
        return this;
    },
    "strokeRect": function() {
        this.cmds.push(["strokeRect", arguments]);
        return this;
    },
    "fillRect": function() {
        this.cmds.push(["fillRect", arguments]);
        return this;
    },
    "beginPath": function() {
        this.cmds.push(["beginPath", arguments]);
        return this;
    },
    "arc": function() {
        this.cmds.push(["arc", arguments]);
        return this;
    },
    "closePath": function() {
        this.cmds.push(["closePath", arguments]);
        return this;
    },
    "fillStyle": function() {
        this.cmds.push(["fillStyle", arguments]);
        return this;
    },
    "fill": function() {
        this.cmds.push(["fill", arguments]);
        return this;
    },
    "strokeStyle": function() {
        this.cmds.push(["strokeStyle", arguments]);
        return this;
    },
    "lineWidth": function() {
        this.cmds.push(["lineWidth", arguments]);
        return this;
    },
    "stroke": function() {
        this.cmds.push(["stroke", arguments]);
        return this;
    },
    "moveTo": function() {
        this.cmds.push(["moveTo", arguments]);
        return this;
    },
    "lineTo": function() {
        this.cmds.push(["lineTo", arguments]);
        return this;
    },
    "bezierCurveTo": function() {
        this.cmds.push(["bezierCurveTo", arguments]);
        return this;
    },
    "clone": function() {}
});

//end-------------------AlloyPaper.Graphics---------------------end


//begin-------------------AlloyPaper.Label---------------------begin

AlloyPaper.Label = AlloyPaper.DisplayObject.extend({
    "ctor": function(option) {
        this._super();
        this.value = option.value;
        this.fontSize = option.fontSize;
        this.fontFamily = option.fontFamily;
        this.color = option.color;
        this.textAlign = "center";
        this.textBaseline = "top";
        this.fontWeight = option.fontWeight || "";
        this.maxWidth = option.maxWidth || 2e3;
        this.square = option.square || false;
        this.txtCanvas = document.createElement("canvas");
        this.txtCtx = this.txtCanvas.getContext("2d");
        this.setDrawOption();
        this.shadow = option.shadow;
        this._watch(this, ["value", "fontSize", "color", "fontFamily"], function() {
            this.setDrawOption();
        });
    },
    "setDrawOption": function() {
        var drawOption = this.getDrawOption({
            txt: this.value,
            maxWidth: this.maxWidth,
            square: this.square,
            size: this.fontSize,
            alignment: this.textAlign,
            color: this.color || "black",
            fontFamily: this.fontFamily,
            fontWeight: this.fontWeight,
            shadow: this.shadow
        });
        this.cacheID = AlloyPaper.UID.getCacheID();
        this.width = drawOption.calculatedWidth;
        this.height = drawOption.calculatedHeight;
    },
    "getDrawOption": function(option) {
        var canvas = this.txtCanvas;
        var ctx = this.txtCtx;
        var canvasX, canvasY;
        var textX, textY;
        var text = [];
        var textToWrite = option.txt;
        var maxWidth = option.maxWidth;
        var squareTexture = option.square;
        var textHeight = option.size;
        var textAlignment = option.alignment;
        var textColour = option.color;
        var fontFamily = option.fontFamily;
        var fontWeight = option.fontWeight;
        ctx.font = textHeight + "px " + fontFamily;
        if (maxWidth && this.measureText(ctx, textToWrite) > maxWidth) {
            maxWidth = this.createMultilineText(ctx, textToWrite, maxWidth, text);
            canvasX = this.getPowerOfTwo(maxWidth);
        } else {
            text.push(textToWrite);
            canvasX = this.getPowerOfTwo(ctx.measureText(textToWrite).width);
        }
        canvasY = this.getPowerOfTwo(textHeight * (text.length + 1));
        if (squareTexture) {
            canvasX > canvasY ? canvasY = canvasX : canvasX = canvasY;
        }
        option.calculatedWidth = canvasX;
        option.calculatedHeight = canvasY;
        canvas.width = canvasX;
        canvas.height = canvasY;
        switch (textAlignment) {
        case "left":
            textX = 0;
            break;
        case "center":
            textX = canvasX / 2;
            break;
        case "right":
            textX = canvasX;
            break;
        }
        textY = canvasY / 2;
        ctx.fillStyle = textColour;
        ctx.textAlign = textAlignment;
        ctx.textBaseline = "middle";
        ctx.font = fontWeight + " " + textHeight + "px " + fontFamily;
        if (option.shadow) {
            ctx.shadowColor = option.shadow.color || "transparent";
            ctx.shadowOffsetX = option.shadow.offsetX || 0;
            ctx.shadowOffsetY = option.shadow.offsetY || 0;
            ctx.shadowBlur = option.shadow.blur || 0;
        } 
        var offset = (canvasY - textHeight * (text.length + 1)) * .5;
        option.cmd = [];
        for (var i = 0; i < text.length; i++) {
            if (text.length > 1) {
                textY = (i + 1) * textHeight + offset;
            }
            option.cmd.push({
                text: text[i],
                x: textX,
                y: textY
            });
            ctx.fillText(text[i], textX, textY);
        }
        return option;
    },
    "getPowerOfTwo": function(value, pow) {
        var temp_pow = pow || 1;
        while (temp_pow < value) {
            temp_pow *= 2;
        }
        return temp_pow;
    },
    "measureText": function(ctx, textToMeasure) {
        return ctx.measureText(textToMeasure).width;
    },
    "createMultilineText": function(ctx, textToWrite, maxWidth, text) {
        textToWrite = textToWrite.replace("\n", " ");
        var currentText = textToWrite;
        var futureText;
        var subWidth = 0;
        var maxLineWidth;
        var wordArray = textToWrite.split(" ");
        var wordsInCurrent, wordArrayLength;
        wordsInCurrent = wordArrayLength = wordArray.length;
        while (this.measureText(ctx, currentText) > maxWidth && wordsInCurrent > 1) {
            wordsInCurrent--;
            currentText = futureText = "";
            for (var i = 0; i < wordArrayLength; i++) {
                if (i < wordsInCurrent) {
                    currentText += wordArray[i];
                    if (i + 1 < wordsInCurrent) {
                        currentText += " ";
                    }
                } else {
                    futureText += wordArray[i];
                    if (i + 1 < wordArrayLength) {
                        futureText += " ";
                    }
                }
            }
        }
        text.push(currentText);
        maxLineWidth = this.measureText(ctx, currentText);
        if (futureText) {
            subWidth = this.createMultilineText(ctx, futureText, maxWidth, text);
            if (subWidth > maxLineWidth) {
                maxLineWidth = subWidth;
            }
        }
        return maxLineWidth;
    },
    "draw": function(ctx) {
        ctx.fillStyle = this.color;
        ctx.font = this.font;
        ctx.textAlign = this.textAlign || "left";
        ctx.textBaseline = this.textBaseline || "top";
        ctx.fillText(this.text, 0, 0);
    }
});

//end-------------------AlloyPaper.Label---------------------end


//begin-------------------AlloyPaper.Particle---------------------begin

AlloyPaper.Particle = AlloyPaper.Bitmap.extend({
    "ctor": function(option) {
        this._super(option.texture);
        this.originX = .5;
        this.originY = .5;
        this.position = option.position;
        this.x = this.position.x;
        this.y = this.position.y;
        this.rotation = option.rotation || 0;
        this.velocity = option.velocity;
        this.acceleration = option.acceleration || new AlloyPaper.Vector2(0, 0);
        this.rotatingSpeed = option.rotatingSpeed || 0;
        this.rotatingAcceleration = option.rotatingAcceleration || 0;
        this.hideSpeed = option.hideSpeed || .01;
        this.zoomSpeed = option.hideSpeed || .01;
        this.isAlive = true;
        this.img = option.texture;
        this.img.src = "";
    },
    "tick": function() {
        this.velocity.add(this.acceleration);
        this.position.add(this.velocity.multiply(.1));
        this.rotatingSpeed += this.rotatingAcceleration;
        this.rotation += this.rotatingSpeed;
        this.alpha -= this.hideSpeed;
        this.x = this.position.x;
        this.y = this.position.y;
    }
});

//end-------------------AlloyPaper.Particle---------------------end



//begin-------------------AlloyPaper.ParticleSystem---------------------begin

AlloyPaper.ParticleSystem = AlloyPaper.Container.extend({
    "ctor": function(option) {
        this._super();
        this.speed = option.speed;
        this.angle = option.angle;
        this.angleRange = option.angleRange;
        this.emitArea = option.emitArea;
        this.gravity = option.gravity || {
            x: 0,
            y: 0
        };
        this.filter = option.filter;
        this.compositeOperation = "lighter";
        this.emitCount = option.emitCount;
        this.maxCount = option.maxCount || 1e3;
        this.emitX = option.emitX;
        this.emitY = option.emitY;
        if (typeof option.texture === "string") {
            if (AlloyPaper.Cache[option.texture]) {
                this.texture = AlloyPaper.Cache[option.texture];
                this.generateFilterTexture(this.texture);
            } else {
                this.bitmap = new AlloyPaper.Bitmap();
                this.bitmap._parent = this;
                this.bitmap.onImageLoad(function() {
                    this._parent.texture = this.img;
                    this._parent.generateFilterTexture(this.img);
                    delete this._parent;
                });
                this.bitmap.useImage(option.texture);
            }
        } else {
            this.texture = option.texture;
            this.generateFilterTexture(option.texture);
        }
        this.totalCount = option.totalCount;
        this.emittedCount = 0;
        this.tickFPS = 60;
        this.hideSpeed = option.hideSpeed || .01;
    },
    "generateFilterTexture": function(texture) {
        var bitmap = new AlloyPaper.Bitmap(texture);
        bitmap.filter = this.filter;
        this.filterTexture = bitmap.cacheCanvas;
    },
    "changeFilter": function (filter) {
        var bitmap = new AlloyPaper.Bitmap(this.texture);
        bitmap.filter = filter;
        this.filterTexture = bitmap.cacheCanvas;
    },
    "emit": function() {
        var angle = (this.angle + AlloyPaper.Util.random(-this.angleRange / 2, this.angleRange / 2)) * Math.PI / 180;
        var halfX = this.emitArea[0] / 2,
            harfY = this.emitArea[1] / 2;
        var particle = new AlloyPaper.Particle({
    position: new AlloyPaper.Vector2(this.emitX + AlloyPaper.Util.random(-halfX, halfX), this.emitY + AlloyPaper.Util.random(-harfY, harfY)),
    velocity: new AlloyPaper.Vector2(this.speed * Math.cos(angle), this.speed * Math.sin(angle)),
    texture: this.filterTexture,
    acceleration: this.gravity,
    hideSpeed: this.hideSpeed
});
        this.add(particle);
        this.emittedCount++;
    },
    "tick": function() {
        if (this.filterTexture) {
            var len = this.children.length;
            if (this.totalCount && this.emittedCount > this.totalCount) {
                if (len === 0) this.destroy();
            } else {
                if (len < this.maxCount) {
                    for (var k = 0; k < this.emitCount; k++) {
                        this.emit();
                    }
                }
            }
            for (var i = 0; i < len; i++) {
                var item = this.children[i];
                if (item.isVisible()) {
                    item.tick();
                } else {
                    this.remove(item);
                    i--;
                    len--;
                }
            }
        }
    }
});

//end-------------------AlloyPaper.ParticleSystem---------------------end

//begin----------------- AlloyPaper.ParticleExplosion -------------------begin
AlloyPaper.ParticleExplosion = AlloyPaper.Container.extend({
    ctor: function (ps, callback) {
        this._super();
        this.ps = ps;
        this.add(ps);
        this.callback = callback;
        this.tickFPS = 0;

        setTimeout(function () {
            this.ps.maxCount = 0;
            this.tickFPS = 60;
        }.bind(this), 1000);
    },
    tick: function () {
        if (this.ps.children.length === 0) {
            this.tickFPS = 0;
            this.parent.remove(this);
            this.callback();

        }
    }
});



//end-----------------AlloyPaper.ParticleExplosion-------------------end

//begin-------------------AlloyPaper.Shape---------------------begin

AlloyPaper.Shape = AlloyPaper.DisplayObject.extend({
    "ctor": function(width, height, debug) {
        this._super();
        this.cmds = [];
        this.assMethod = ["fillStyle", "strokeStyle", "lineWidth"];
        this.width = width;
        this.height = height;
        this._width = width;
        this._height = height;
        this.shapeCanvas = document.createElement("canvas");
        this.shapeCanvas.width = this.width;
        this.shapeCanvas.height = this.height;
        this.shapeCtx = this.shapeCanvas.getContext("2d");
        if (debug) {
            this.fillStyle("red");
            this.fillRect(0, 0, width, height);
        }
        this._watch(this, "scaleX", function(prop, value) {
            this.width = this._width * value;
            this.height = this._height * this.scaleY;
            this.shapeCanvas.width = this.width;
            this.shapeCanvas.height = this.height;
            this.shapeCtx.scale(value, this.scaleY);
            this.end();
        });
        this._watch(this, "scaleY", function(prop, value) {
            this.width = this._width * this.scaleX;
            this.height = this._height * value;
            this.shapeCanvas.width = this.width;
            this.shapeCanvas.height = this.height;
            this.shapeCtx.scale(this.scaleX, value);
            this.end();
        });
    },
    "end": function() {
        this._preCacheId = this.cacheID;
        this.cacheID = AlloyPaper.UID.getCacheID();
        var ctx = this.shapeCtx;
        for (var i = 0, len = this.cmds.length; i < len; i++) {
            var cmd = this.cmds[i];
            if (this.assMethod.join("-").match(new RegExp("\\b" + cmd[0] + "\\b", "g"))) {
                ctx[cmd[0]] = cmd[1][0];
            } else {
                ctx[cmd[0]].apply(ctx, Array.prototype.slice.call(cmd[1]));
            }
        }
    },
    "clearRect": function(x, y, width, height) {
        this.cacheID = AlloyPaper.UID.getCacheID();
        this.shapeCtx.clearRect(x, y, width, height);
    },
    "clear": function() {
        this.cacheID = AlloyPaper.UID.getCacheID();
        this.cmds.length = 0;
        this.shapeCtx.clearRect(0, 0, this.width, this.height);
    },
    "strokeRect": function() {
        this.cmds.push(["strokeRect", arguments]);
        return this;
    },
    "fillRect": function() {
        this.cmds.push(["fillRect", arguments]);
        return this;
    },
    "beginPath": function() {
        this.cmds.push(["beginPath", arguments]);
        return this;
    },
    "arc": function() {
        this.cmds.push(["arc", arguments]);
        return this;
    },
    "closePath": function() {
        this.cmds.push(["closePath", arguments]);
        return this;
    },
    "fillStyle": function() {
        this.cmds.push(["fillStyle", arguments]);
        return this;
    },
    "fill": function() {
        this.cmds.push(["fill", arguments]);
        return this;
    },
    "strokeStyle": function() {
        this.cmds.push(["strokeStyle", arguments]);
        return this;
    },
    "lineWidth": function() {
        this.cmds.push(["lineWidth", arguments]);
        return this;
    },
    "stroke": function() {
        this.cmds.push(["stroke", arguments]);
        return this;
    },
    "moveTo": function() {
        this.cmds.push(["moveTo", arguments]);
        return this;
    },
    "lineTo": function() {
        this.cmds.push(["lineTo", arguments]);
        return this;
    },
    "bezierCurveTo": function() {
        this.cmds.push(["bezierCurveTo", arguments]);
        return this;
    },
    "clone": function() {}
});

//end-------------------AlloyPaper.Shape---------------------end


//begin-------------------AlloyPaper.Sprite---------------------begin

AlloyPaper.Sprite = AlloyPaper.DisplayObject.extend({
    "ctor": function(option) {
        this._super();
        this.option = option;
        this.x = option.x || 0;
        this.y = option.y || 0;
        this.currentFrameIndex = 0;
        this.animationFrameIndex = 0;
        this.currentAnimation = option.currentAnimation || null;
        this.rect = [0, 0, 10, 10];
        this.visible = false;
        this.bitmaps = [];
        this._loadedCount = 0;
        var len = this.option.imgs.length;
        for (var i = 0; i < len; i++) {
            var urlOrImg = this.option.imgs[i];
            if (typeof urlOrImg === "string") {
                if (AlloyPaper.Cache[urlOrImg]) {
                    this.bitmaps.push(new AlloyPaper.Bitmap(AlloyPaper.Cache[urlOrImg]));
                    this._loadedCount++;
                } else {
                    (function(){
                        var bmp = new AlloyPaper.Bitmap();
                        bmp._sprite = this;
                        bmp.onImageLoad(function() {
                            bmp._sprite._loadedCount++;
                            if (bmp._sprite._loadedCount === len) {
                                bmp._sprite.visible = true;
                                delete bmp._sprite;
                            }
                        });
                        bmp.useImage(this.option.imgs[i]);
                        this.bitmaps.push(bmp);
                    })();
                }
            } else {
                this._loadedCount++;
                this.bitmaps.push(new AlloyPaper.Bitmap(urlOrImg));
            }
        }
        if (this._loadedCount === len) {
            this.visible = true;
        }
        this.img = this.bitmaps[0].img;
        this.interval = 1e3 / option.framerate;
        this.loop = null;
        this.paused = false;
        this.animationEnd = option.animationEnd || null;
        if (this.currentAnimation) {
            this.gotoAndPlay(this.currentAnimation);
        }
        this.tickAnimationEnd = option.tickAnimationEnd || null;
    },
    "play": function() {
        this.paused = false;
    },
    "pause": function () {
        this.paused = true;
    },
    "reset": function() {
        this.currentFrameIndex = 0;
        this.animationFrameIndex = 0;
    },
    "gotoAndPlay": function(animation, times) {
        this.paused = false;
        this.reset();
        clearInterval(this.loop);
        this.currentAnimation = animation;
        var self = this;
        var playTimes = 0;
        this.loop = setInterval(function() {
            if (!self.paused) {
                var opt = self.option;
                var frames = opt.animations[self.currentAnimation].frames,
                    len = frames.length;
                self.animationFrameIndex++;
                if (self.animationFrameIndex > len - 1) {
                    playTimes++;
                    self.animationFrameIndex = 0;
                    if (self.tickAnimationEnd) {
                        self.tickAnimationEnd();
                    }
                    if (times && playTimes == times) {
                        if (self.animationEnd) self.animationEnd();
                        self.paused = true;
                        clearInterval(self.loop);
                        self.parent.remove(self);
                    }
                }
                self.rect = opt.frames[frames[self.animationFrameIndex]];
                self.width = self.rect[2];
                self.height = self.rect[3];
                var rect = self.rect,
                    rectLen = rect.length;
                rectLen > 4 && (self.regX = rect[2] * rect[4]);
                rectLen > 5 && (self.regY = rect[3] * rect[5]);
                rectLen > 6 && (self.img = self.bitmaps[rect[6]].img);
            }
        }, this.interval);
    },
    "gotoAndStop": function(animation) {
        this.reset();
        clearInterval(this.loop);
        var self = this;
        self.currentAnimation = animation;
        var opt = self.option;
        var frames = opt.animations[self.currentAnimation].frames;
        self.rect = opt.frames[frames[self.animationFrameIndex]];
        self.width = self.rect[2];
        self.height = self.rect[3];
        var rect = self.rect,
            rectLen = rect.length;
        rectLen > 4 && (self.regX = rect[2] * rect[4]);
        rectLen > 5 && (self.regY = rect[3] * rect[5]);
        rectLen > 6 && (self.img = self.bitmaps[rect[6]].img);
    }
});

//end-------------------AlloyPaper.Sprite---------------------end

AlloyPaper.Stage = AlloyPaper.Container.extend({
    "ctor": function(canvas, openWebGL) {
        this._super();
        this.canvas = typeof canvas == "string" ? document.querySelector(canvas) : canvas;
        this.width = this.canvas.width;
        this.height = this.canvas.height;
        this.AABB = [0, 0, this.width, this.height];
        this.hitAABB = true;
        this.hitRenderer = new AlloyPaper.CanvasRenderer();
        this.hitCanvas = document.createElement("canvas");
        this.hitCanvas.width = 1;
        this.hitCanvas.height = 1;
        this.stageRenderer = new AlloyPaper.Renderer(this, openWebGL);
        this.hitCtx = this.hitCanvas.getContext("2d");
        this._scaleX = this._scaleY = null;
        this.offset = this._getXY(this.canvas);
        this.overObj = null;
        this._paused = false;
        this.fps = 63;
        this.interval = Math.floor(1e3 / this.fps);
        this.toList = [];
        this.tickFns = [];
        this.beginTick = null;
        this.endTick = null;
        var self = this;
        self.loop = setInterval(function() {
            if (self._paused) return;
            self.beginTick && self.beginTick();
            self._tick(self);
            self.endTick && self.endTick();
        }, self.interval);
        Object.defineProperty(this, "useRequestAnimFrame", {
            set: function(value) {
                this._useRequestAnimFrame = value;
                if (value) {
                    clearInterval(self.loop);
                    self.loop = AlloyPaper.RAF.requestInterval(function() {
                        self._tick(self);
                    }, self.interval);
                } else {
                    AlloyPaper.RAF.clearRequestInterval(self.loop);
                    self.loop = setInterval(function() {
                        self._tick(self);
                    }, self.interval);
                }
            },
            get: function() {
                return this._useRequestAnimFrame;
            }
        });
        this._watch(this, "fps", function(prop, value) {
            this.interval = Math.floor(1e3 / value);
            var self = this;
            if (this.useRequestAnimFrame) {
                clearInterval(this.loop);
                try {
                    AlloyPaper.RAF.clearRequestInterval(this.loop);
                } catch (e) {}
                this.loop = AlloyPaper.RAF.requestInterval(function() {
                    self._tick(self);
                }, this.interval);
            } else {
                AlloyPaper.RAF.clearRequestInterval(this.loop);
                try {
                    clearInterval(this.loop);
                } catch (e) {}
                this.loop = setInterval(function() {
                    self._tick(self);
                }, this.interval);
            }
        });
        this._initDebug();
        this._pressmoveObjs = null;
        this.baseInstanceof = "Stage";
        this.overObj = null;
        this._moveInterval = 16;
        this._preMoveTime = new Date();
        this._currentMoveTime = new Date();
        Object.defineProperty(this, "moveFPS", {
            set: function(value) {
                this._moveFPS = value;
                this._moveInterval = 1e3 / value;
            },
            get: function() {
                return this._moveFPS;
            }
        });
        this.canvas.addEventListener("mousemove", this._handleMouseMove.bind(this), false);
        this.canvas.addEventListener("click", this._handleClick.bind(this), false);
        this.canvas.addEventListener("mousedown", this._handleMouseDown.bind(this), false);
        this.canvas.addEventListener("mouseup", this._handleMouseUp.bind(this), false);
        this.canvas.addEventListener("dblclick", this._handleDblClick.bind(this), false);
        this.addEvent(this.canvas, "mousewheel", this._handleMouseWheel.bind(this));
        this.canvas.addEventListener("touchmove", this._handleMouseMove.bind(this), false);
        this.canvas.addEventListener("touchstart", this._handleMouseDown.bind(this), false);
        this.canvas.addEventListener("touchend", this._handleMouseUp.bind(this), false);
        this.canvas.addEventListener("touchcancel", this._handleTouchCancel.bind(this), false);
        document.addEventListener("DOMContentLoaded", this.adjustLayout.bind(this), false);
        window.addEventListener("load", this.adjustLayout.bind(this), false);
        window.addEventListener("resize", this.adjustLayout.bind(this), false);
        this.autoUpdate = true;
        this.scaleType = "normal";

        this.setCursor(AlloyPaper.DefaultCursor);
    },
    "adjustLayout": function() {
        this.offset = this._getXY(this.canvas);
        this.style=this._getStyle();
        if (this._scaleX) {
            this.scaleToScreen(this._scaleX, this._scaleY);
        }
    },
    "pause": function () {
        this._paused = true;
        this._pauseSprite(this);
        this._pauseTween();

    },
    "play": function () {
        this._paused = false;
        this._playSprite(this);
        this._playTween();
    },
    "_pauseSprite": function (obj) {
        for (var i = 0, len = obj.children.length; i < len; i++) {
            var child = obj.children[i];
            if (child instanceof AlloyPaper.Container) {
                this._pauseSprite(child);
            } else if (child instanceof AlloyPaper.Sprite) {
                child.pause();
            }
        }
    },
    "_pauseTween": function () {
        for (var i = 0, len = this.toList.length; i < len; i++) {
            this.toList[i].pause();
        }
    },
    "_playSprite": function (obj) {
        for (var i = 0, len = obj.children.length; i < len; i++) {
            var child = obj.children[i];
            if (child instanceof AlloyPaper.Container) {
                this._playSprite(child);
            } else if (child instanceof AlloyPaper.Sprite) {
                child.play();
            }
        }
    },
    "_playTween": function () {
        for (var i = 0, len = this.toList.length; i < len; i++) {
            this.toList[i].play();
        }
    },
    "toggle": function () {
        if (this._paused) {
            this.play();
        } else {
            this.pause();
        }
    },
    "openDebug": function() {},
    "closeDebug": function() {},
    "_initDebug": function() {
        this.debugDiv = document.createElement("div");
        this.debugDiv.style.cssText = "display:none;position:absolute;z-index:2000;left:0;bottom:0;background-color:yellow;font-size:16px;";
        document.body.appendChild(this.debugDiv);
        Object.defineProperty(this, "debug", {
            set: function(value) {
                this._debug = value;
                if (this._debug) {
                    this.debugDiv.style.display = "block";
                } else {
                    this.debugDiv.style.display = "none";
                }
            },
            get: function() {
                return this._debug;
            }
        });
    },
    "_handleMouseWheel": function(event) {
        this._correctionEvent(event, event.type);
        var callbacks = this.events["mousewheel"];
        if (callbacks) {
            for (var i = 0, len = callbacks.length; i < len; i++) {
                var callback = callbacks[i];
                callback(event);
            }
        }
        if (this.overObj) {
            this.hitRenderer._bubbleEvent(this.overObj, "mousewheel", event);
        }
    },
    "update": function() {
        this.stageRenderer.update();
    },
    "_correctionEvent": function (evt, type) {
        this.adjustLayout();
        if (evt.touches||evt.changedTouches) {
            var firstTouch = evt.touches[0] || evt.changedTouches[0];
            if (firstTouch) {
                evt.stageX = firstTouch.pageX;
                evt.stageY = firstTouch.pageY;
            }
        } else {
            evt.stageX = evt.pageX;
            evt.stageY = evt.pageY;
        }
        //if (this.scaleType !== "normal") {
            var p = this._correction(evt.stageX, evt.stageY);
            evt.stageX = Math.round(p.x);
            evt.stageY = Math.round(p.y);
        //}
        var callbacks = this.events[type];
        if (callbacks) {
            for (var i = 0, len = callbacks.length; i < len; i++) {
                var callback = callbacks[i];
                callback(evt);
            }
        }
    },
    "_handleClick": function(evt) {
        this._correctionEvent(evt, evt.type);
        this._getObjectUnderPoint(evt, evt.type);
    },
    "_handleMouseMove": function(evt) {
        this._currentMoveTime = new Date();
        if (this._currentMoveTime - this._preMoveTime > this._moveInterval / 2) {
            this._correctionEvent(evt, evt.type);
            if (this._pressmoveObjs) {
                var pressmoveHandle = this._pressmoveObjs.events["pressmove"];
                pressmoveHandle && this._pressmoveObjs.execEvent("pressmove", evt);
            }
            var child = this._getObjectUnderPoint(evt, "mousemove");
            if (child) {
                if (this.overObj) {
                    if (child.id != this.overObj.id) {
                        this.hitRenderer._bubbleEvent(this.overObj, "mouseout", evt);
                        this.hitRenderer._bubbleEvent(child, "mouseover", evt);
                        this.overObj = child;
                    } else {
                        this.hitRenderer._bubbleEvent(child, "mousemove", evt);
                    }
                    this._setCursorByOverObject(child);
                } else {
                    this.overObj = child;
                    this.hitRenderer._bubbleEvent(child, "mouseover", evt);
                }
            } else {
                if (this.overObj) {
                    this.hitRenderer._bubbleEvent(this.overObj, "mouseout", evt);
                    this.overObj = null;
                }
            }
            this._preMoveTime = this._currentMoveTime;
        }
    },
    "_getPressmoveTarget": function(o) {
        if (o.events["pressmove"]) {
            this._pressmoveObjs = o;
        }
        if (o.parent) this._getPressmoveTarget(o.parent);
    },
    "_handleMouseDown": function(evt) {
        this._correctionEvent(evt, "pressdown");
        var child = this._getObjectUnderPoint(evt, "pressdown");
        if (child) {
            this._getPressmoveTarget(child);
        }
    },
    "_handleMouseUp": function(evt) {
        this._pressmoveObjs = null;
        this._correctionEvent(evt, "pressup");
        this._getObjectUnderPoint(evt, "pressup");
    },
    "_handleTouchCancel": function (evt) {
        this._pressmoveObjs = null;
        this._correctionEvent(evt, "touchcancel");
        this._getObjectUnderPoint(evt, "touchcancel");
    },
    "_handleDblClick": function(evt) {
        this._correctionEvent(evt, evt.type);
        this._getObjectUnderPoint(evt, evt.type);
    },
    "_getObjectUnderPoint": function(evt, type) {
        if (this.hitAABB) {
            return this.hitRenderer.hitAABB(this.hitCtx, this, evt, type);
        } else {
            return this.hitRenderer.hitRender(this.hitCtx, this, evt, type);
        }
    },
    "_getXY": function(el) {
        var _t = 0,
            _l = 0;
        if (document.documentElement.getBoundingClientRect && el.getBoundingClientRect) {
            var box = el.getBoundingClientRect();
            _l = box.left;
            _t = box.top;
        } else {
            while (el.offsetParent) {
                _t += el.offsetTop;
                _l += el.offsetLeft;
                el = el.offsetParent;
            }
            return [_l, _t];
        }
        return [_l + Math.max(document.documentElement.scrollLeft, document.body.scrollLeft), _t + Math.max(document.documentElement.scrollTop, document.body.scrollTop)];
    },
    "_tick": function(container) {
        if (container && container.tick && container.tickFPS > 0) {
            this._initInterval(container);
            if (!container.hasOwnProperty("_tickInterval")) {
                container.tick();
            } else {
                container._tickIntervalCurrent = new Date();
                if (!container._tickIntervalLast) {
                    container._tickIntervalLast = new Date();
                    container._tickIntervalPrev = new Date();
                }

                var itv = (container._tickIntervalCurrent - container._tickIntervalLast) +( container._tickIntervalCurrent - container._tickIntervalPrev);
                if (itv > container._tickInterval) {
                    container.tick();
                    container._tickIntervalLast = container._tickIntervalCurrent;
                }
                container._tickIntervalPrev= new Date();

            }
        }
        var children = container.children,
            len = children.length;
        for (var i = 0; i < len; i++) {
            var child = children[i];
            if (child) {
                if (child.tick && child.tickFPS > 0) {
                    this._initInterval(child);
                    if (!child.hasOwnProperty("_tickInterval")) {
                        child.tick();
                    } else {
                        child._tickIntervalCurrent = new Date();
                        if (!child._tickIntervalLast){
                            child._tickIntervalLast = new Date();
                            child._tickIntervalPrev = new Date();
                        }
                        var itv =( child._tickIntervalCurrent - child._tickIntervalLast)+(child._tickIntervalCurrent-child._tickIntervalPrev);
                        if (itv > child._tickInterval) {
                            child.tick();
                            child._tickIntervalLast = child._tickIntervalCurrent;
                        }
                        child._tickIntervalPrev= new Date();

                    }
                }
                if (child.baseInstanceof == "Container") {
                    this._tick(child);
                }
            }
        }
    },
    "_initInterval": function(obj) {
        if (obj.hasOwnProperty("tickFPS")) {
            obj._tickInterval = 1e3 / obj.tickFPS;
        }
    },
    "tick": function () {
        for (var i = 0, len = this.tickFns.length; i < len; i++) {
            var fn = this.tickFns[i];
            if (!fn.hasOwnProperty("_ARE_PrevDate")) {
                fn();
                continue;
            }
            fn._ARE_CurrentDate = new Date();
            var interval = (fn._ARE_CurrentDate - fn._ARE_PrevDate) + (fn._ARE_CurrentDate - fn._ARE_LastDate);

            if (interval > fn._ARE_Interval) {
                fn();
                fn._ARE_PrevDate = fn._ARE_CurrentDate;
            }
            fn._ARE_LastDate = fn._ARE_CurrentDate;
        }

        if(this.autoUpdate)this.update();
        if (this.debug) {
            this.getFPS();
            this.debugDiv.innerHTML = "fps : " + this.fpsValue +  " 
object count : " + this.getTotalCount() + "
rendering mode : " + this.getRenderingMode() + "
inner object count : " + this.stageRenderer.objs.length; } }, "onTick": function(fn,interval) { this.tickFns.push(fn); if (interval !== undefined) { fn._ARE_PrevDate = new Date(); fn._ARE_CurrentDate = new Date(); fn._ARE_LastDate = new Date(); fn._ARE_Interval = interval; } }, "setFPS": function(fps) { this.interval = Math.floor(1e3 / fps); }, "onKeyboard": function(keyCombo, onDownCallback, onUpCallback) { AlloyPaper.Keyboard.on(keyCombo, onDownCallback, onUpCallback); }, "getActiveKeys": function() { return AlloyPaper.Keyboard.getActiveKeys(); }, "scaleToScreen": function (scaleX, scaleY) { this.scaleType = "screen"; if (scaleX === 1 && scaleY === 1) { document.body.style.overflow = "hidden"; document.documentElement.style.overflow = "hidden"; } document.body.style.margin = 0; document.documentElement.style.margin = 0; document.body.style.border = 0; document.documentElement.style.border = 0; document.body.style.padding = 0; document.documentElement.style.padding = 0; document.body.style.width = "100%"; document.documentElement.style.width = "100%"; document.body.style.height = "100%"; document.documentElement.style.height = "100%"; this._scaleX = scaleX; this._scaleY = scaleY; var canvas = this.canvas; canvas.style.position = "absolute"; canvas.style.width = scaleX * 100 + "%"; canvas.style.height = scaleY * 100 + "%"; canvas.style.left = 100 * (1 - scaleX) / 2 + "%"; canvas.style.top = 100 * (1 - scaleY) / 2 + "%"; canvas.style.border = "0px solid #ccc"; this.offset = this._getXY(this.canvas); this.style=this._getStyle(); }, "scaleToBox": function (w, h) { this.scaleType = "box"; if (w === window.innerWidth && h === window.innerHeight) { document.body.style.overflow = "hidden"; document.documentElement.style.overflow = "hidden"; } var canvas = this.canvas; canvas.style.position = "absolute"; canvas.style.width = w + "px"; canvas.style.height = h + "px"; canvas.style.left = (window.innerWidth - w) / 2 + "px"; canvas.style.top = (window.innerHeight - h) / 2 + "px"; canvas.style.border = "0px solid #ccc"; this.offset = this._getXY(this.canvas); this.style=this._getStyle(); }, "correctingXY": function (x, y) { if (this.scaleType === "box") { return { x: x * this.width / parseInt( this.canvas.style.width), y: y * this.height / parseInt(this.canvas.style.height) }; } else { return { x: x * this.width / (window.innerWidth * this._scaleX), y: y * this.height / (window.innerHeight * this._scaleY) }; } }, "getTotalCount": function() { var count = 0; var self = this; function getCount(child) { if (child.baseInstanceof == "Container" || child.baseInstanceof == "Stage") { for (var i = 0, len = child.children.length; i < len; i++) { var subChild = child.children[i]; if (subChild instanceof AlloyPaper.Container) { getCount(subChild); } else { count++; } } } else { count++; } } getCount(this); return count; }, "getRenderingMode": function() { if (this.stageRenderer.renderingEngine instanceof AlloyPaper.CanvasRenderer) { return "Canvas"; } return "WebGL"; }, "getFPS": function() { var fps = AlloyPaper.FPS.get(); this.fpsValue = fps.value; }, "addEvent": function(el, type, fn, capture) { if (type === "mousewheel" && document.mozHidden !== undefined) { type = "DOMMouseScroll"; } el.addEventListener(type, function(event) { var type = event.type; if (type == "DOMMouseScroll" || type == "mousewheel") { event.delta = event.wheelDelta ? event.wheelDelta / 120 : -(event.detail || 0) / 3; } fn.call(this, event); }, capture || false); }, "setCursor": function(type) { this.canvas.style.cursor = type; }, "_setCursorByOverObject": function (obj) { if (obj.cursor !== "default") { this.setCursor(obj.cursor); } else { if (obj.parent) { this._setCursorByOverObject(obj.parent); } } }, "destroy": function () { this._super(); this.canvas.parentNode.removeChild(this.canvas); if (this.useRequestAnimFrame) { AlloyPaper.RAF.clearRequestInterval(this.loop); } else { clearInterval(this.loop); } }, "_getStyle":function() { var style = window.getComputedStyle(this.canvas, null); return { boxSizing: style.boxSizing, borderTopWidth: parseInt(style.borderTopWidth), borderLeftWidth: parseInt(style.borderLeftWidth), width:parseInt(style.width), height:parseInt(style.height) }; }, "_correction":function(pageX,pageY){ var x=pageX-this.offset[0]-this.style.borderLeftWidth, y=pageY-this.offset[1]-this.style.borderTopWidth, canvasWidth=this.style.width, canvasHeight=this.style.height; if(this.style.boxSizing==="border-box"){ canvasWidth-=this.style.borderLeftWidth; canvasHeight-=this.style.borderTopWidth; } return {x: this.width*x/canvasWidth,y:this.height*y/canvasHeight}; } }); //begin-------------------AlloyPaper.Text---------------------begin AlloyPaper.Text = AlloyPaper.DisplayObject.extend({ "ctor": function(value, font, color) { this._super(); this.value = value; this.font = font; this.color = color; this.textAlign = "left"; this.textBaseline = "top"; }, "draw": function(ctx) { ctx.fillStyle = this.color; ctx.font = this.font; ctx.textAlign = this.textAlign || "left"; ctx.textBaseline = this.textBaseline || "top"; ctx.fillText(this.value, 0, 0); }, "clone": function() { var t = new AlloyPaper.Text(this.text, this.font, this.color); this.cloneProps(t); return t; }, "getWidth": function () { var measureCtx = document.createElement("canvas").getContext("2d"); measureCtx.font = this.font; var width = measureCtx.measureText(this.value).width; measureCtx = null; return width; } }); //end-------------------AlloyPaper.Text---------------------end return AlloyPaper; }));

你可能感兴趣的:(canvas,HTML5,HTML,+,CSS,+,JS)