1 /**
  2  * enchant.js v0.8.3
  3  * http://enchantjs.com
  4  *
  5  * Copyright UEI Corporation
  6  * Released under the MIT license.
  7  */
  8 
  9 (function(window, undefined) {
 10 
 11 // ECMA-262 5th edition Functions
 12 if (typeof Object.defineProperty !== 'function') {
 13     Object.defineProperty = function(obj, prop, desc) {
 14         if ('value' in desc) {
 15             obj[prop] = desc.value;
 16         }
 17         if ('get' in desc) {
 18             obj.__defineGetter__(prop, desc.get);
 19         }
 20         if ('set' in desc) {
 21             obj.__defineSetter__(prop, desc.set);
 22         }
 23         return obj;
 24     };
 25 }
 26 if (typeof Object.defineProperties !== 'function') {
 27     Object.defineProperties = function(obj, descs) {
 28         for (var prop in descs) {
 29             if (descs.hasOwnProperty(prop)) {
 30                 Object.defineProperty(obj, prop, descs[prop]);
 31             }
 32         }
 33         return obj;
 34     };
 35 }
 36 if (typeof Object.create !== 'function') {
 37     Object.create = function(prototype, descs) {
 38         function F() {
 39         }
 40 
 41         F.prototype = prototype;
 42         var obj = new F();
 43         if (descs != null) {
 44             Object.defineProperties(obj, descs);
 45         }
 46         return obj;
 47     };
 48 }
 49 if (typeof Object.getPrototypeOf !== 'function') {
 50     Object.getPrototypeOf = function(obj) {
 51         return obj.__proto__;
 52     };
 53 }
 54 
 55 if (typeof Function.prototype.bind !== 'function') {
 56     Function.prototype.bind = function(thisObject) {
 57         var func = this;
 58         var args = Array.prototype.slice.call(arguments, 1);
 59         var Nop = function() {
 60         };
 61         var bound = function() {
 62             var a = args.concat(Array.prototype.slice.call(arguments));
 63             return func.apply(
 64                 this instanceof Nop ? this : thisObject || window, a);
 65         };
 66         Nop.prototype = func.prototype;
 67         bound.prototype = new Nop();
 68         return bound;
 69     };
 70 }
 71 
 72 window.getTime = (function() {
 73     var origin;
 74     if (window.performance && window.performance.now) {
 75         origin = Date.now();
 76         return function() {
 77             return origin + window.performance.now();
 78         };
 79     } else if (window.performance && window.performance.webkitNow) {
 80         origin = Date.now();
 81         return function() {
 82             return origin + window.performance.webkitNow();
 83         };
 84     } else {
 85         return Date.now;
 86     }
 87 }());
 88 
 89 // define requestAnimationFrame
 90 window.requestAnimationFrame =
 91     window.requestAnimationFrame ||
 92     window.mozRequestAnimationFrame ||
 93     window.webkitRequestAnimationFrame ||
 94     window.msRequestAnimationFrame ||
 95     (function() {
 96         var lastTime = window.getTime();
 97         var frame = 1000 / 60;
 98         return function(func) {
 99             var _id = setTimeout(function() {
100                 lastTime = window.getTime();
101                 func(lastTime);
102             }, Math.max(0, lastTime + frame - window.getTime()));
103             return _id;
104         };
105     }());
106 
107 /**
108  * Export the library classes globally.
109  *
110  * When no arguments are given, all classes defined in enchant.js as well as all classes defined in
111  * plugins will be exported. When more than one argument is given, by default only classes defined
112  * in enchant.js will be exported. When you wish to export plugin classes you must explicitly deliver
113  * the plugin identifiers as arguments.
114  *
115  * @example
116  * enchant();     // All classes will be exported.
117  * enchant('');   // Only classes in enchant.js will be exported.
118  * enchant('ui'); // enchant.js classes and ui.enchant.js classes will be exported.
119  *
120  * @param {...String} [modules] Export module. Multiple designations possible.
121  * @function
122  * @global
123  * @name enchant
124  */
125 var enchant = function(modules) {
126     if (modules != null) {
127         if (!(modules instanceof Array)) {
128             modules = Array.prototype.slice.call(arguments);
129         }
130         modules = modules.filter(function(module) {
131             return [module].join();
132         });
133     }
134     (function include(module, prefix) {
135         var submodules = [],
136             i, len;
137         for (var prop in module) {
138             if (module.hasOwnProperty(prop)) {
139                 if (typeof module[prop] === 'function') {
140                     window[prop] = module[prop];
141                 } else if (typeof module[prop] === 'object' && module[prop] !== null && Object.getPrototypeOf(module[prop]) === Object.prototype) {
142                     if (modules == null) {
143                         submodules.push(prop);
144                     } else {
145                         i = modules.indexOf(prefix + prop);
146                         if (i !== -1) {
147                             submodules.push(prop);
148                             modules.splice(i, 1);
149                         }
150                     }
151                 }
152             }
153         }
154 
155         for (i = 0, len = submodules.length; i < len; i++) {
156             include(module[submodules[i]], prefix + submodules[i] + '.');
157         }
158     }(enchant, ''));
159 
160     // issue 185
161     if (enchant.Class.getInheritanceTree(window.Game).length <= enchant.Class.getInheritanceTree(window.Core).length) {
162         window.Game = window.Core;
163     }
164 
165     if (modules != null && modules.length) {
166         throw new Error('Cannot load module: ' + modules.join(', '));
167     }
168 };
169 
170 // export enchant
171 window.enchant = enchant;
172 
173 window.addEventListener("message", function(msg, origin) {
174     try {
175         var data = JSON.parse(msg.data);
176         if (data.type === "event") {
177             enchant.Core.instance.dispatchEvent(new enchant.Event(data.value));
178         } else if (data.type === "debug") {
179             switch (data.value) {
180                 case "start":
181                     enchant.Core.instance.start();
182                     break;
183                 case "pause":
184                     enchant.Core.instance.pause();
185                     break;
186                 case "resume":
187                     enchant.Core.instance.resume();
188                     break;
189                 case "tick":
190                     enchant.Core.instance._tick();
191                     break;
192                 default:
193                     break;
194             }
195         }
196     } catch (e) {
197         // ignore
198     }
199 }, false);
200 
201 /**
202  * @name enchant.Class
203  * @class
204  * A Class representing a class which supports inheritance.
205  * @param {Function} [superclass] The class from which the
206  * new class will inherit the class definition.
207  * @param {*} [definition] Class definition.
208  * @constructor
209  */
210 enchant.Class = function(superclass, definition) {
211     return enchant.Class.create(superclass, definition);
212 };
213 
214 /**
215  * Creates a class.
216  *
217  * When defining a class that extends from another class, 
218  * the constructor of the other class will be used by default.
219  * Even if you override this constructor, you must still call it
220  * to ensure that the class is initialized correctly.
221  *
222  * @example
223  * // Creates a Ball class.
224  * var Ball = Class.create({ 
225  *
226  *     // Ball's constructor
227  *     initialize: function(radius) {
228  *       // ... code ...
229  *     }, 
230  *
231  *     // Defines a fall method that doesn't take any arguments.
232  *     fall: function() { 
233  *       // ... code ...
234  *     }
235  * });
236  *
237  * // Creates a Ball class that extends from "Sprite"
238  * var Ball = Class.create(Sprite);  
239  *
240  * // Creates a Ball class that extends from "Sprite"
241  * var Ball = Class.create(Sprite, { 
242  *
243  *     // Overwrite Sprite's constructor
244  *     initialize: function(radius) { 
245  *
246  *         // Call Sprite's constructor.
247  *         Sprite.call(this, radius * 2, radius * 2);
248  *
249  *         this.image = core.assets['ball.gif'];
250  *     }
251  * });
252  *
253  * @param {Function} [superclass] The class from which the
254  * new class will inherit the class definition.
255  * @param {*} [definition] Class definition.
256  * @static
257  */
258 enchant.Class.create = function(superclass, definition) {
259     if (superclass == null && definition) {
260         throw new Error("superclass is undefined (enchant.Class.create)");
261     } else if (superclass == null) {
262         throw new Error("definition is undefined (enchant.Class.create)");
263     }
264 
265     if (arguments.length === 0) {
266         return enchant.Class.create(Object, definition);
267     } else if (arguments.length === 1 && typeof arguments[0] !== 'function') {
268         return enchant.Class.create(Object, arguments[0]);
269     }
270 
271     for (var prop in definition) {
272         if (definition.hasOwnProperty(prop)) {
273             if (typeof definition[prop] === 'object' && definition[prop] !== null && Object.getPrototypeOf(definition[prop]) === Object.prototype) {
274                 if (!('enumerable' in definition[prop])) {
275                     definition[prop].enumerable = true;
276                 }
277             } else {
278                 definition[prop] = { value: definition[prop], enumerable: true, writable: true };
279             }
280         }
281     }
282     var Constructor = function() {
283         if (this instanceof Constructor) {
284             Constructor.prototype.initialize.apply(this, arguments);
285         } else {
286             return new Constructor();
287         }
288     };
289     Constructor.prototype = Object.create(superclass.prototype, definition);
290     Constructor.prototype.constructor = Constructor;
291     if (Constructor.prototype.initialize == null) {
292         Constructor.prototype.initialize = function() {
293             superclass.apply(this, arguments);
294         };
295     }
296 
297     var tree = this.getInheritanceTree(superclass);
298     for (var i = 0, l = tree.length; i < l; i++) {
299         if (typeof tree[i]._inherited === 'function') {
300             tree[i]._inherited(Constructor);
301             break;
302         }
303     }
304 
305     return Constructor;
306 };
307 
308 /**
309  * Get the inheritance tree of this class.
310  * @param {Function} Constructor
311  * @return {Function[]} Parent's constructor
312  */
313 enchant.Class.getInheritanceTree = function(Constructor) {
314     var ret = [];
315     var C = Constructor;
316     var proto = C.prototype;
317     while (C !== Object) {
318         ret.push(C);
319         proto = Object.getPrototypeOf(proto);
320         C = proto.constructor;
321     }
322     return ret;
323 };
324 
325 /**
326  * @namespace
327  * enchant.js environment variables.
328  * Execution settings can be changed by modifying these before calling new Core().
329  */
330 enchant.ENV = {
331     /**
332      * Version of enchant.js
333      * @type String
334      */
335     VERSION: '0.8.3',
336     /**
337      * Identifier of the current browser.
338      * @type String
339      */
340     BROWSER: (function(ua) {
341         if (/Eagle/.test(ua)) {
342             return 'eagle';
343         } else if (/Opera/.test(ua)) {
344             return 'opera';
345         } else if (/MSIE|Trident/.test(ua)) {
346             return 'ie';
347         } else if (/Chrome/.test(ua)) {
348             return 'chrome';
349         } else if (/(?:Macintosh|Windows).*AppleWebKit/.test(ua)) {
350             return 'safari';
351         } else if (/(?:iPhone|iPad|iPod).*AppleWebKit/.test(ua)) {
352             return 'mobilesafari';
353         } else if (/Firefox/.test(ua)) {
354             return 'firefox';
355         } else if (/Android/.test(ua)) {
356             return 'android';
357         } else {
358             return '';
359         }
360     }(navigator.userAgent)),
361     /**
362      * The CSS vendor prefix of the current browser.
363      * @type String
364      */
365     VENDOR_PREFIX: (function() {
366         var ua = navigator.userAgent;
367         if (ua.indexOf('Opera') !== -1) {
368             return 'O';
369         } else if (/MSIE|Trident/.test(ua)) {
370             return 'ms';
371         } else if (ua.indexOf('WebKit') !== -1) {
372             return 'webkit';
373         } else if (navigator.product === 'Gecko') {
374             return 'Moz';
375         } else {
376             return '';
377         }
378     }()),
379     /**
380      * Determines if the current browser supports touch.
381      * True, if touch is enabled.
382      * @type Boolean
383      */
384     TOUCH_ENABLED: (function() {
385         var div = document.createElement('div');
386         div.setAttribute('ontouchstart', 'return');
387         return typeof div.ontouchstart === 'function';
388     }()),
389     /**
390      * Determines if the current browser is an iPhone with a retina display.
391      * True, if this display is a retina display.
392      * @type Boolean
393      */
394     RETINA_DISPLAY: (function() {
395         if (navigator.userAgent.indexOf('iPhone') !== -1 && window.devicePixelRatio === 2) {
396             var viewport = document.querySelector('meta[name="viewport"]');
397             if (viewport == null) {
398                 viewport = document.createElement('meta');
399                 document.head.appendChild(viewport);
400             }
401             viewport.setAttribute('content', 'width=640');
402             return true;
403         } else {
404             return false;
405         }
406     }()),
407     /**
408      * Determines if for current browser Flash should be used to play 
409      * sound instead of the native audio class.
410      * True, if flash should be used.
411      * @type Boolean
412      */
413     USE_FLASH_SOUND: (function() {
414         var ua = navigator.userAgent;
415         var vendor = navigator.vendor || "";
416         // non-local access, not on mobile mobile device, not on safari
417         return (location.href.indexOf('http') === 0 && ua.indexOf('Mobile') === -1 && vendor.indexOf('Apple') !== -1);
418     }()),
419     /**
420      * If click/touch event occure for these tags the setPreventDefault() method will not be called.
421      * @type String[]
422      */
423     USE_DEFAULT_EVENT_TAGS: ['input', 'textarea', 'select', 'area'],
424     /**
425      * Method names of CanvasRenderingContext2D that will be defined as Surface method.
426      * @type String[]
427      */
428     CANVAS_DRAWING_METHODS: [
429         'putImageData', 'drawImage', 'drawFocusRing', 'fill', 'stroke',
430         'clearRect', 'fillRect', 'strokeRect', 'fillText', 'strokeText'
431     ],
432     /**
433      * Keybind Table.
434      * You can use 'left', 'up', 'right', 'down' for preset event.
435      * @example
436      * enchant.ENV.KEY_BIND_TABLE = {
437      *     37: 'left',
438      *     38: 'up',
439      *     39: 'right',
440      *     40: 'down',
441      *     32: 'a', //-> use 'space' key as 'a button'
442      * };
443      * @type Object
444      */
445     KEY_BIND_TABLE: {
446         37: 'left',
447         38: 'up',
448         39: 'right',
449         40: 'down'
450     },
451     /**
452      * If keydown event occure for these keycodes the setPreventDefault() method will be called.
453      * @type Number[]
454      */
455     PREVENT_DEFAULT_KEY_CODES: [37, 38, 39, 40],
456     /**
457      * Determines if Sound is enabled on Mobile Safari.
458      * @type Boolean
459      */
460     SOUND_ENABLED_ON_MOBILE_SAFARI: true,
461     /**
462      * Determines if "touch to start" scene is enabled.
463      * It is necessary on Mobile Safari because WebAudio Sound is
464      * muted by browser until play any sound in touch event handler.
465      * If set it to false, you should control this behavior manually.
466      * @type Boolean
467      */
468     USE_TOUCH_TO_START_SCENE: true,
469     /**
470      * Determines if WebAudioAPI is enabled. (true: use WebAudioAPI instead of Audio element if possible)
471      * @type Boolean
472      */
473     USE_WEBAUDIO: (function() {
474         return location.protocol !== 'file:';
475     }()),
476     /**
477      * Determines if animation feature is enabled. (true: Timeline instance will be generated in new Node)
478      * @type Boolean
479      */
480     USE_ANIMATION: true,
481     /**
482      * Specifies range of the touch detection.
483      * The detection area will be (COLOR_DETECTION_LEVEL * 2 + 1)px square.
484      * @type Boolean
485      */
486     COLOR_DETECTION_LEVEL: 2
487 };
488 
489 /**
490  * @scope enchant.Event.prototype
491  */
492 enchant.Event = enchant.Class.create({
493     /**
494      * @name enchant.Event
495      * @class
496      * A class for an independent implementation of events similar to DOM Events.
497      * Does not include phase concepts.
498      * @param {String} type Event type.
499      * @constructs
500      */
501     initialize: function(type) {
502         /**
503          * The type of the event.
504          * @type String
505          */
506         this.type = type;
507         /**
508          * The target of the event.
509          * @type *
510          */
511         this.target = null;
512         /**
513          * The x-coordinate of the event's occurrence.
514          * @type Number
515          */
516         this.x = 0;
517         /**
518          * The y-coordinate of the event's occurrence.
519          * @type Number
520          */
521         this.y = 0;
522         /**
523          * The x-coordinate of the event's occurrence relative to the object
524          * which issued the event.
525          * @type Number
526          */
527         this.localX = 0;
528         /**
529          * The y-coordinate of the event's occurrence relative to the object
530          * which issued the event.
531          * @type Number
532          */
533         this.localY = 0;
534     },
535     _initPosition: function(pageX, pageY) {
536         var core = enchant.Core.instance;
537         this.x = this.localX = (pageX - core._pageX) / core.scale;
538         this.y = this.localY = (pageY - core._pageY) / core.scale;
539     }
540 });
541 
542 /**
543  * An event dispatched once the core has finished loading.
544  *
545  * When preloading images, it is necessary to wait until preloading is complete
546  * before starting the game.
547  * Issued by: {@link enchant.Core}
548  *
549  * @example
550  * var core = new Core(320, 320);
551  * core.preload('player.gif');
552  * core.onload = function() {
553  *     ... // Describes initial core processing
554  * };
555  * core.start();
556  * @type String
557  */
558 enchant.Event.LOAD = 'load';
559 
560 /**
561  * An event dispatched when an error occurs.
562  * Issued by: {@link enchant.Core}, {@link enchant.Surface}, {@link enchant.WebAudioSound}, {@link enchant.DOMSound}
563  */
564 enchant.Event.ERROR = 'error';
565 
566 /**
567  * An event dispatched when the display size is changed.
568  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
569  @type String
570  */
571 enchant.Event.CORE_RESIZE = 'coreresize';
572 
573 /**
574  * An event dispatched while the core is loading.
575  * Dispatched each time an image is preloaded.
576  * Issued by: {@link enchant.LoadingScene}
577  * @type String
578  */
579 enchant.Event.PROGRESS = 'progress';
580 
581 /**
582  * An event which is occurring when a new frame is beeing processed.
583  * Issued object: {@link enchant.Core}, {@link enchant.Node}
584  * @type String
585  */
586 enchant.Event.ENTER_FRAME = 'enterframe';
587 
588 /**
589  * An event dispatched at the end of processing a new frame.
590  * Issued by: {@link enchant.Core}, {@link enchant.Node}
591  * @type String
592  */
593 enchant.Event.EXIT_FRAME = 'exitframe';
594 
595 /**
596  * An event dispatched when a Scene begins.
597  * Issued by: {@link enchant.Scene}
598  * @type String
599  */
600 enchant.Event.ENTER = 'enter';
601 
602 /**
603  * An event dispatched when a Scene ends.
604  * Issued by: {@link enchant.Scene}
605  * @type String
606  */
607 enchant.Event.EXIT = 'exit';
608 
609 /**
610  * An event dispatched when a Child is added to a Node.
611  * Issued by: {@link enchant.Group}, {@link enchant.Scene}
612  * @type String
613  */
614 enchant.Event.CHILD_ADDED = 'childadded';
615 
616 /**
617  * An event dispatched when a Node is added to a Group.
618  * Issued by: {@link enchant.Node}
619  * @type String
620  */
621 enchant.Event.ADDED = 'added';
622 
623 /**
624  * An event dispatched when a Node is added to a Scene.
625  * Issued by: {@link enchant.Node}
626  * @type String
627  */
628 enchant.Event.ADDED_TO_SCENE = 'addedtoscene';
629 
630 /**
631  * An event dispatched when a Child is removed from a Node.
632  * Issued by: {@link enchant.Group}, {@link enchant.Scene}
633  * @type String
634  * @type String
635  */
636 enchant.Event.CHILD_REMOVED = 'childremoved';
637 
638 /**
639  * An event dispatched when a Node is deleted from a Group.
640  * Issued by: {@link enchant.Node}
641  * @type String
642  */
643 enchant.Event.REMOVED = 'removed';
644 
645 /**
646  * An event dispatched when a Node is deleted from a Scene.
647  * Issued by: {@link enchant.Node}
648  * @type String
649  */
650 enchant.Event.REMOVED_FROM_SCENE = 'removedfromscene';
651 
652 /**
653  * An event dispatched when a touch event intersecting a Node begins.
654  * A mouse event counts as a touch event. Issued by: {@link enchant.Node}
655  * @type String
656  */
657 enchant.Event.TOUCH_START = 'touchstart';
658 
659 /**
660  * An event dispatched when a touch event intersecting the Node has been moved.
661  * A mouse event counts as a touch event. Issued by: {@link enchant.Node}
662  * @type String
663  */
664 enchant.Event.TOUCH_MOVE = 'touchmove';
665 
666 /**
667  * An event dispatched when a touch event intersecting the Node ends.
668  * A mouse event counts as a touch event. Issued by: {@link enchant.Node}
669  * @type String
670  */
671 enchant.Event.TOUCH_END = 'touchend';
672 
673 /**
674  * An event dispatched when an Entity is rendered.
675  * Issued by: {@link enchant.Entity}
676  * @type String
677  */
678 enchant.Event.RENDER = 'render';
679 
680 /**
681  * An event dispatched when a button is pressed.
682  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
683  * @type String
684  */
685 enchant.Event.INPUT_START = 'inputstart';
686 
687 /**
688  * An event dispatched when button inputs change.
689  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
690  * @type String
691  */
692 enchant.Event.INPUT_CHANGE = 'inputchange';
693 
694 /**
695  * An event dispatched when button input ends.
696  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
697  * @type String
698  */
699 enchant.Event.INPUT_END = 'inputend';
700 
701 /**
702  * An internal event which is occurring when a input changes.
703  * Issued object: {@link enchant.InputSource}
704  * @type String
705  */
706 enchant.Event.INPUT_STATE_CHANGED = 'inputstatechanged';
707 
708 /**
709  * An event dispatched when the 'left' button is pressed.
710  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
711  * @type String
712  */
713 enchant.Event.LEFT_BUTTON_DOWN = 'leftbuttondown';
714 
715 /**
716  * An event dispatched when the 'left' button is released.
717  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
718  * @type String
719  */
720 enchant.Event.LEFT_BUTTON_UP = 'leftbuttonup';
721 
722 /**
723  * An event dispatched when the 'right' button is pressed.
724  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
725  * @type String
726  */
727 enchant.Event.RIGHT_BUTTON_DOWN = 'rightbuttondown';
728 
729 /**
730  * An event dispatched when the 'right' button is released.
731  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
732  * @type String
733  */
734 enchant.Event.RIGHT_BUTTON_UP = 'rightbuttonup';
735 
736 /**
737  * An event dispatched when the 'up' button is pressed.
738  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
739  * @type String
740  */
741 enchant.Event.UP_BUTTON_DOWN = 'upbuttondown';
742 
743 /**
744  * An event dispatched when the 'up' button is released.
745  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
746  * @type String
747  */
748 enchant.Event.UP_BUTTON_UP = 'upbuttonup';
749 
750 /**
751  * An event dispatched when the 'down' button is pressed.
752  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
753  * @type String
754  */
755 enchant.Event.DOWN_BUTTON_DOWN = 'downbuttondown';
756 
757 /**
758  * An event dispatched when the 'down' button is released.
759  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
760  * @type String
761  */
762 enchant.Event.DOWN_BUTTON_UP = 'downbuttonup';
763 
764 /**
765  * An event dispatched when the 'a' button is pressed.
766  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
767  * @type String
768  */
769 enchant.Event.A_BUTTON_DOWN = 'abuttondown';
770 
771 /**
772  * An event dispatched when the 'a' button is released.
773  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
774  * @type String
775  */
776 enchant.Event.A_BUTTON_UP = 'abuttonup';
777 
778 /**
779  * An event dispatched when the 'b' button is pressed.
780  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
781  * @type String
782  */
783 enchant.Event.B_BUTTON_DOWN = 'bbuttondown';
784 
785 /**
786  * An event dispatched when the 'b' button is released.
787  * Issued by: {@link enchant.Core}, {@link enchant.Scene}
788  * @type String
789  */
790 enchant.Event.B_BUTTON_UP = 'bbuttonup';
791 
792 /**
793  * An event dispatched when an Action is added to a Timeline.
794  * When looped, an Action is removed from the Timeline and added back into it.
795  * @type String
796  */
797 enchant.Event.ADDED_TO_TIMELINE = "addedtotimeline";
798 
799 /**
800  * An event dispatched when an Action is removed from a Timeline.
801  * When looped, an Action is removed from the timeline and added back into it.
802  * @type String
803  */
804 enchant.Event.REMOVED_FROM_TIMELINE = "removedfromtimeline";
805 
806 /**
807  * An event dispatched when an Action begins.
808  * @type String
809  */
810 enchant.Event.ACTION_START = "actionstart";
811 
812 /**
813  * An event dispatched when an Action finishes.
814  * @type String
815  */
816 enchant.Event.ACTION_END = "actionend";
817 
818 /**
819  * An event dispatched when an Action has gone through one frame.
820  * @type String
821  */
822 enchant.Event.ACTION_TICK = "actiontick";
823 
824 /**
825  * An event dispatched to the Timeline when an Action is added.
826  * @type String
827  */
828 enchant.Event.ACTION_ADDED = "actionadded";
829 
830 /**
831  * An event dispatched to the Timeline when an Action is removed.
832  * @type String
833  */
834 enchant.Event.ACTION_REMOVED = "actionremoved";
835 
836 /**
837  * An event dispatched when an animation finishes, meaning null element was encountered
838  * Issued by: {@link enchant.Sprite}
839  * @type String
840  */
841 enchant.Event.ANIMATION_END = "animationend";
842 
843 /**
844  * @scope enchant.EventTarget.prototype
845  */
846 enchant.EventTarget = enchant.Class.create({
847     /**
848      * @name enchant.EventTarget
849      * @class
850      * A class for implementation of events similar to DOM Events.
851      * However, it does not include the concept of phases.
852      * @constructs
853      */
854     initialize: function() {
855         this._listeners = {};
856     },
857     /**
858      * Add a new event listener which will be executed when the event
859      * is dispatched.
860      * @param {String} type Type of the events.
861      * @param {Function(e:enchant.Event)} listener Event listener to be added.
862      */
863     addEventListener: function(type, listener) {
864         var listeners = this._listeners[type];
865         if (listeners == null) {
866             this._listeners[type] = [listener];
867         } else if (listeners.indexOf(listener) === -1) {
868             listeners.unshift(listener);
869 
870         }
871     },
872     /**
873      * Synonym for addEventListener.
874      * @param {String} type Type of the events.
875      * @param {Function(e:enchant.Event)} listener Event listener to be added.
876      * @see enchant.EventTarget#addEventListener
877      */
878     on: function() {
879         this.addEventListener.apply(this, arguments);
880     },
881     /**
882      * Delete an event listener.
883      * @param {String} [type] Type of the events.
884      * @param {Function(e:enchant.Event)} listener Event listener to be deleted.
885      */
886     removeEventListener: function(type, listener) {
887         var listeners = this._listeners[type];
888         if (listeners != null) {
889             var i = listeners.indexOf(listener);
890             if (i !== -1) {
891                 listeners.splice(i, 1);
892             }
893         }
894     },
895     /**
896      * Clear all defined event listeners of a given type.
897      * If no type is given, all listeners will be removed.
898      * @param {String} type Type of the events.
899      */
900     clearEventListener: function(type) {
901         if (type != null) {
902             delete this._listeners[type];
903         } else {
904             this._listeners = {};
905         }
906     },
907     /**
908      * Issue an event.
909      * @param {enchant.Event} e Event to be issued.
910      */
911     dispatchEvent: function(e) {
912         e.target = this;
913         e.localX = e.x - this._offsetX;
914         e.localY = e.y - this._offsetY;
915         if (this['on' + e.type] != null){
916             this['on' + e.type](e);
917         }
918         var listeners = this._listeners[e.type];
919         if (listeners != null) {
920             listeners = listeners.slice();
921             for (var i = 0, len = listeners.length; i < len; i++) {
922                 listeners[i].call(this, e);
923             }
924         }
925     }
926 });
927 
928 (function() {
929     var core;
930     /**
931      * @scope enchant.Core.prototype
932      */
933     enchant.Core = enchant.Class.create(enchant.EventTarget, {
934         /**
935          * @name enchant.Core
936          * @class
937          * A class for controlling the core’s main loop and scenes.
938          *
939          * There can be only one instance at a time. When the
940          * constructor is executed while an instance exists, the
941          * existing instance will be overwritten. The existing instance
942          * can be accessed from {@link enchant.Core.instance}.
943          *
944          * @param {Number} [width=320] The width of the core viewport.
945          * @param {Number} [height=320] The height of the core viewport.
946          * @constructs
947          * @extends enchant.EventTarget
948          */
949         initialize: function(width, height) {
950             if (window.document.body === null) {
951                 // @TODO postpone initialization after window.onload
952                 throw new Error("document.body is null. Please excute 'new Core()' in window.onload.");
953             }
954 
955             enchant.EventTarget.call(this);
956             var initial = true;
957             if (core) {
958                 initial = false;
959                 core.stop();
960             }
961             core = enchant.Core.instance = this;
962 
963             this._calledTime = 0;
964             this._mousedownID = 0;
965             this._surfaceID = 0;
966             this._soundID = 0;
967 
968             this._scenes = [];
969 
970             width = width || 320;
971             height = height || 320;
972 
973             var stage = document.getElementById('enchant-stage');
974             var scale, sWidth, sHeight;
975             if (!stage) {
976                 stage = document.createElement('div');
977                 stage.id = 'enchant-stage';
978                 stage.style.position = 'absolute';
979 
980                 if (document.body.firstChild) {
981                     document.body.insertBefore(stage, document.body.firstChild);
982                 } else {
983                     document.body.appendChild(stage);
984                 }
985                 scale = Math.min(
986                     window.innerWidth / width,
987                     window.innerHeight / height
988                 );
989                 this._pageX = stage.getBoundingClientRect().left;
990                 this._pageY = stage.getBoundingClientRect().top;
991             } else {
992                 var style = window.getComputedStyle(stage);
993                 sWidth = parseInt(style.width, 10);
994                 sHeight = parseInt(style.height, 10);
995                 if (sWidth && sHeight) {
996                     scale = Math.min(
997                         sWidth / width,
998                         sHeight / height
999                     );
1000                 } else {
1001                     scale = 1;
1002                 }
1003                 while (stage.firstChild) {
1004                     stage.removeChild(stage.firstChild);
1005                 }
1006                 stage.style.position = 'relative';
1007 
1008                 var bounding = stage.getBoundingClientRect();
1009                 this._pageX = Math.round(window.scrollX || window.pageXOffset + bounding.left);
1010                 this._pageY = Math.round(window.scrollY || window.pageYOffset + bounding.top);
1011             }
1012             stage.style.fontSize = '12px';
1013             stage.style.webkitTextSizeAdjust = 'none';
1014             stage.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)';
1015             this._element = stage;
1016 
1017             this.addEventListener('coreresize', this._oncoreresize);
1018 
1019             this._width = width;
1020             this._height = height;
1021             this.scale = scale;
1022 
1023             /**
1024              * The frame rate of the core.
1025              * @type Number
1026              */
1027             this.fps = 30;
1028             /**
1029              * The number of frames processed since the core was started.
1030              * @type Number
1031              */
1032             this.frame = 0;
1033             /**
1034              * Indicates whether or not the core can be executed.
1035              * @type Boolean
1036              */
1037             this.ready = false;
1038             /**
1039              * Indicates whether or not the core is currently running.
1040              * @type Boolean
1041              */
1042             this.running = false;
1043             /**
1044              * Object which stores loaded assets using their paths as keys.
1045              * @type Object
1046              */
1047             this.assets = {};
1048             var assets = this._assets = [];
1049             (function detectAssets(module) {
1050                 if (module.assets) {
1051                     enchant.Core.instance.preload(module.assets);
1052                 }
1053                 for (var prop in module) {
1054                     if (module.hasOwnProperty(prop)) {
1055                         if (typeof module[prop] === 'object' && module[prop] !== null && Object.getPrototypeOf(module[prop]) === Object.prototype) {
1056                             detectAssets(module[prop]);
1057                         }
1058                     }
1059                 }
1060             }(enchant));
1061 
1062             /**
1063              * The Scene which is currently displayed. This Scene is on top of the Scene stack.
1064              * @type enchant.Scene
1065              */
1066             this.currentScene = null;
1067             /**
1068              * The root Scene. The Scene at the bottom of the Scene stack.
1069              * @type enchant.Scene
1070              */
1071             this.rootScene = new enchant.Scene();
1072             this.pushScene(this.rootScene);
1073             /**
1074              * The Scene to be displayed during loading.
1075              * @type enchant.Scene
1076              */
1077             this.loadingScene = new enchant.LoadingScene();
1078 
1079             /**
1080              [/lang:ja]
1081              * Indicates whether or not {@link enchant.Core#start} has been called.
1082              [/lang]
1083              * @type Boolean
1084              * @private
1085              */
1086             this._activated = false;
1087 
1088             this._offsetX = 0;
1089             this._offsetY = 0;
1090 
1091             /**
1092              * Object that saves the current input state for the core.
1093              * @type Object
1094              */
1095             this.input = {};
1096 
1097             this.keyboardInputManager = new enchant.KeyboardInputManager(window.document, this.input);
1098             this.keyboardInputManager.addBroadcastTarget(this);
1099             this._keybind = this.keyboardInputManager._binds;
1100 
1101             if (!enchant.ENV.KEY_BIND_TABLE) {
1102                 enchant.ENV.KEY_BIND_TABLE = {};
1103             }
1104 
1105             for (var prop in enchant.ENV.KEY_BIND_TABLE) {
1106                 this.keybind(prop, enchant.ENV.KEY_BIND_TABLE[prop]);
1107             }
1108 
1109             if (initial) {
1110                 stage = enchant.Core.instance._element;
1111                 var evt;
1112                 document.addEventListener('keydown', function(e) {
1113                     core.dispatchEvent(new enchant.Event('keydown'));
1114                     if (enchant.ENV.PREVENT_DEFAULT_KEY_CODES.indexOf(e.keyCode) !== -1) {
1115                         e.preventDefault();
1116                         e.stopPropagation();
1117                     }
1118                 }, true);
1119 
1120                 if (enchant.ENV.TOUCH_ENABLED) {
1121                     stage.addEventListener('touchstart', function(e) {
1122                         var tagName = (e.target.tagName).toLowerCase();
1123                         if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1124                             e.preventDefault();
1125                             if (!core.running) {
1126                                 e.stopPropagation();
1127                             }
1128                         }
1129                     }, true);
1130                     stage.addEventListener('touchmove', function(e) {
1131                         var tagName = (e.target.tagName).toLowerCase();
1132                         if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1133                             e.preventDefault();
1134                             if (!core.running) {
1135                                 e.stopPropagation();
1136                             }
1137                         }
1138                     }, true);
1139                     stage.addEventListener('touchend', function(e) {
1140                         var tagName = (e.target.tagName).toLowerCase();
1141                         if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1142                             e.preventDefault();
1143                             if (!core.running) {
1144                                 e.stopPropagation();
1145                             }
1146                         }
1147                     }, true);
1148                 }
1149                 stage.addEventListener('mousedown', function(e) {
1150                     var tagName = (e.target.tagName).toLowerCase();
1151                     if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1152                         e.preventDefault();
1153                         core._mousedownID++;
1154                         if (!core.running) {
1155                             e.stopPropagation();
1156                         }
1157                     }
1158                 }, true);
1159                 stage.addEventListener('mousemove', function(e) {
1160                     var tagName = (e.target.tagName).toLowerCase();
1161                     if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1162                         e.preventDefault();
1163                         if (!core.running) {
1164                             e.stopPropagation();
1165                         }
1166                     }
1167                 }, true);
1168                 stage.addEventListener('mouseup', function(e) {
1169                     var tagName = (e.target.tagName).toLowerCase();
1170                     if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1171                         e.preventDefault();
1172                         if (!core.running) {
1173                             e.stopPropagation();
1174                         }
1175                     }
1176                 }, true);
1177                 core._touchEventTarget = {};
1178                 if (enchant.ENV.TOUCH_ENABLED) {
1179                     stage.addEventListener('touchstart', function(e) {
1180                         var core = enchant.Core.instance;
1181                         var evt = new enchant.Event(enchant.Event.TOUCH_START);
1182                         var touches = e.changedTouches;
1183                         var touch, target;
1184                         for (var i = 0, l = touches.length; i < l; i++) {
1185                             touch = touches[i];
1186                             evt._initPosition(touch.pageX, touch.pageY);
1187                             target = core.currentScene._determineEventTarget(evt);
1188                             core._touchEventTarget[touch.identifier] = target;
1189                             target.dispatchEvent(evt);
1190                         }
1191                     }, false);
1192                     stage.addEventListener('touchmove', function(e) {
1193                         var core = enchant.Core.instance;
1194                         var evt = new enchant.Event(enchant.Event.TOUCH_MOVE);
1195                         var touches = e.changedTouches;
1196                         var touch, target;
1197                         for (var i = 0, l = touches.length; i < l; i++) {
1198                             touch = touches[i];
1199                             target = core._touchEventTarget[touch.identifier];
1200                             if (target) {
1201                                 evt._initPosition(touch.pageX, touch.pageY);
1202                                 target.dispatchEvent(evt);
1203                             }
1204                         }
1205                     }, false);
1206                     stage.addEventListener('touchend', function(e) {
1207                         var core = enchant.Core.instance;
1208                         var evt = new enchant.Event(enchant.Event.TOUCH_END);
1209                         var touches = e.changedTouches;
1210                         var touch, target;
1211                         for (var i = 0, l = touches.length; i < l; i++) {
1212                             touch = touches[i];
1213                             target = core._touchEventTarget[touch.identifier];
1214                             if (target) {
1215                                 evt._initPosition(touch.pageX, touch.pageY);
1216                                 target.dispatchEvent(evt);
1217                                 delete core._touchEventTarget[touch.identifier];
1218                             }
1219                         }
1220                     }, false);
1221                 }
1222                 stage.addEventListener('mousedown', function(e) {
1223                     var core = enchant.Core.instance;
1224                     var evt = new enchant.Event(enchant.Event.TOUCH_START);
1225                     evt._initPosition(e.pageX, e.pageY);
1226                     var target = core.currentScene._determineEventTarget(evt);
1227                     core._touchEventTarget[core._mousedownID] = target;
1228                     target.dispatchEvent(evt);
1229                 }, false);
1230                 stage.addEventListener('mousemove', function(e) {
1231                     var core = enchant.Core.instance;
1232                     var evt = new enchant.Event(enchant.Event.TOUCH_MOVE);
1233                     evt._initPosition(e.pageX, e.pageY);
1234                     var target = core._touchEventTarget[core._mousedownID];
1235                     if (target) {
1236                         target.dispatchEvent(evt);
1237                     }
1238                 }, false);
1239                 stage.addEventListener('mouseup', function(e) {
1240                     var core = enchant.Core.instance;
1241                     var evt = new enchant.Event(enchant.Event.TOUCH_END);
1242                     evt._initPosition(e.pageX, e.pageY);
1243                     var target = core._touchEventTarget[core._mousedownID];
1244                     if (target) {
1245                         target.dispatchEvent(evt);
1246                     }
1247                     delete core._touchEventTarget[core._mousedownID];
1248                 }, false);
1249             }
1250         },
1251         /**
1252          * The width of the core screen.
1253          * @type Number
1254          */
1255         width: {
1256             get: function() {
1257                 return this._width;
1258             },
1259             set: function(w) {
1260                 this._width = w;
1261                 this._dispatchCoreResizeEvent();
1262             }
1263         },
1264         /**
1265          * The height of the core screen.
1266          * @type Number
1267          */
1268         height: {
1269             get: function() {
1270                 return this._height;
1271             },
1272             set: function(h) {
1273                 this._height = h;
1274                 this._dispatchCoreResizeEvent();
1275             }
1276         },
1277         /**
1278          * The scaling of the core rendering.
1279          * @type Number
1280          */
1281         scale: {
1282             get: function() {
1283                 return this._scale;
1284             },
1285             set: function(s) {
1286                 this._scale = s;
1287                 this._dispatchCoreResizeEvent();
1288             }
1289         },
1290         _dispatchCoreResizeEvent: function() {
1291             var e = new enchant.Event('coreresize');
1292             e.width = this._width;
1293             e.height = this._height;
1294             e.scale = this._scale;
1295             this.dispatchEvent(e);
1296         },
1297         _oncoreresize: function(e) {
1298             this._element.style.width = Math.floor(this._width * this._scale) + 'px';
1299             this._element.style.height = Math.floor(this._height * this._scale) + 'px';
1300             var scene;
1301             for (var i = 0, l = this._scenes.length; i < l; i++) {
1302                 scene = this._scenes[i];
1303                 scene.dispatchEvent(e);
1304             }
1305         },
1306         /**
1307          * File preloader.
1308          *
1309          * Loads the files specified in the parameters when
1310          * {@link enchant.Core#start} is called.
1311          * When all files are loaded, a {@link enchant.Event.LOAD}
1312          * event is dispatched from the Core object. Depending on the
1313          * type of each file, different objects will be created and
1314          * stored in {@link enchant.Core#assets} Variable.
1315          *
1316          * When an image file is loaded, a {@link enchant.Surface} is
1317          * created. If a sound file is loaded, an {@link enchant.Sound}
1318          * object is created. Any other file type will be accessible
1319          * as a string.
1320          *
1321          * In addition, because this Surface object is created with
1322          * {@link enchant.Surface.load}, it is not possible to
1323          * manipulate the image directly.
1324          * Refer to the {@link enchant.Surface.load} documentation.
1325          *
1326          * @example
1327          * core.preload('player.gif');
1328          * core.onload = function() {
1329          *     var sprite = new Sprite(32, 32);
1330          *     sprite.image = core.assets['player.gif']; // Access via path
1331          *     ...
1332          * };
1333          * core.start();
1334          *
1335          * @param {...String|String[]} assets Path of images to be preloaded.
1336          * Multiple settings possible.
1337          * @return {enchant.Core} this
1338          */
1339         preload: function(assets) {
1340             var a;
1341             if (!(assets instanceof Array)) {
1342                 if (typeof assets === 'object') {
1343                     a = [];
1344                     for (var name in assets) {
1345                         if (assets.hasOwnProperty(name)) {
1346                             a.push([ assets[name], name ]);
1347                         }
1348                     }
1349                     assets = a;
1350                 } else {
1351                     assets = Array.prototype.slice.call(arguments);
1352                 }
1353             }
1354             Array.prototype.push.apply(this._assets, assets);
1355             return this;
1356         },
1357         /**
1358          * Loads a file.
1359          *
1360          * @param {String} src File path of the resource to be loaded.
1361          * @param {String} [alias] Name you want to designate for the resource to be loaded.
1362          * @param {Function} [callback] Function to be called if the file loads successfully.
1363          * @param {Function} [onerror] Function to be called if the file fails to load.
1364          * @return {enchant.Deferred}
1365          */
1366         load: function(src, alias, callback, onerror) {
1367             var assetName;
1368             if (typeof arguments[1] === 'string') {
1369                 assetName = alias;
1370                 callback = callback || function() {};
1371                 onerror = onerror || function() {};
1372             } else {
1373                 assetName = src;
1374                 var tempCallback = callback;
1375                 callback = arguments[1] || function() {};
1376                 onerror = tempCallback || function() {};
1377             }
1378 
1379             var ext = enchant.Core.findExt(src);
1380 
1381             return enchant.Deferred.next(function() {
1382                 var d = new enchant.Deferred();
1383                 var _callback = function(e) {
1384                     d.call(e);
1385                     callback.call(this, e);
1386                 };
1387                 var _onerror = function(e) {
1388                     d.fail(e);
1389                     onerror.call(this, e);
1390                 };
1391                 if (enchant.Core._loadFuncs[ext]) {
1392                     enchant.Core.instance.assets[assetName] = enchant.Core._loadFuncs[ext](src, ext, _callback, _onerror);
1393                 } else {
1394                     var req = new XMLHttpRequest();
1395                     req.open('GET', src, true);
1396                     req.onreadystatechange = function() {
1397                         if (req.readyState === 4) {
1398                             if (req.status !== 200 && req.status !== 0) {
1399                                 // throw new Error(req.status + ': ' + 'Cannot load an asset: ' + src);
1400                                 var e = new enchant.Event('error');
1401                                 e.message = req.status + ': ' + 'Cannot load an asset: ' + src;
1402                                 _onerror.call(enchant.Core.instance, e);
1403                             }
1404 
1405                             var type = req.getResponseHeader('Content-Type') || '';
1406                             if (type.match(/^image/)) {
1407                                 core.assets[assetName] = enchant.Surface.load(src, _callback, _onerror);
1408                             } else if (type.match(/^audio/)) {
1409                                 core.assets[assetName] = enchant.Sound.load(src, type, _callback, _onerror);
1410                             } else {
1411                                 core.assets[assetName] = req.responseText;
1412                                 _callback.call(enchant.Core.instance, new enchant.Event('load'));
1413                             }
1414                         }
1415                     };
1416                     req.send(null);
1417                 }
1418                 return d;
1419             });
1420         },
1421         /**
1422          * Start the core.
1423          *
1424          * Sets the framerate of the {@link enchant.Core#currentScene}
1425          * according to the value stored in {@link enchant.core#fps}. If
1426          * there are images to preload, loading will begin and the
1427          * loading screen will be displayed.
1428          * @return {enchant.Deferred}
1429          */
1430         start: function(deferred) {
1431             var onloadTimeSetter = function() {
1432                 this.frame = 0;
1433                 this.removeEventListener('load', onloadTimeSetter);
1434             };
1435             this.addEventListener('load', onloadTimeSetter);
1436 
1437             this.currentTime = window.getTime();
1438             this.running = true;
1439             this.ready = true;
1440 
1441             if (!this._activated) {
1442                 this._activated = true;
1443                 if (enchant.ENV.BROWSER === 'mobilesafari' &&
1444                     enchant.ENV.USE_WEBAUDIO &&
1445                     enchant.ENV.USE_TOUCH_TO_START_SCENE) {
1446                     var d = new enchant.Deferred();
1447                     var scene = this._createTouchToStartScene();
1448                     scene.addEventListener(enchant.Event.TOUCH_START, function waitTouch() {
1449                         this.removeEventListener(enchant.Event.TOUCH_START, waitTouch);
1450                         var a = new enchant.WebAudioSound();
1451                         a.buffer = enchant.WebAudioSound.audioContext.createBuffer(1, 1, 48000);
1452                         a.play();
1453                         core.removeScene(scene);
1454                         core.start(d);
1455                     }, false);
1456                     core.pushScene(scene);
1457                     return d;
1458                 }
1459             }
1460 
1461             this._requestNextFrame(0);
1462 
1463             var ret = this._requestPreload()
1464                 .next(function() {
1465                     enchant.Core.instance.loadingScene.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
1466                 });
1467 
1468             if (deferred) {
1469                 ret.next(function(arg) {
1470                     deferred.call(arg);
1471                 })
1472                 .error(function(arg) {
1473                     deferred.fail(arg);
1474                 });
1475             }
1476 
1477             return ret;
1478         },
1479         _requestPreload: function() {
1480             var o = {};
1481             var loaded = 0,
1482                 len = 0,
1483                 loadFunc = function() {
1484                     var e = new enchant.Event('progress');
1485                     e.loaded = ++loaded;
1486                     e.total = len;
1487                     core.loadingScene.dispatchEvent(e);
1488                 };
1489             this._assets
1490                 .reverse()
1491                 .forEach(function(asset) {
1492                     var src, name;
1493                     if (asset instanceof Array) {
1494                         src = asset[0];
1495                         name = asset[1];
1496                     } else {
1497                         src = name = asset;
1498                     }
1499                     if (!o[name]) {
1500                         o[name] = this.load(src, name, loadFunc);
1501                         len++;
1502                     }
1503                 }, this);
1504 
1505             this.pushScene(this.loadingScene);
1506             return enchant.Deferred.parallel(o);
1507         },
1508         _createTouchToStartScene: function() {
1509             var label = new enchant.Label('Touch to Start'),
1510                 size = Math.round(core.width / 10),
1511                 scene = new enchant.Scene();
1512 
1513             label.color = '#fff';
1514             label.font = (size - 1) + 'px bold Helvetica,Arial,sans-serif';
1515             label.textAlign = 'center';
1516             label.width = core.width;
1517             label.height = label._boundHeight;
1518             label.y = (core.height - label.height) / 2;
1519 
1520             scene.backgroundColor = '#000';
1521             scene.addChild(label);
1522 
1523             return scene;
1524         },
1525         /**
1526          * Start application in debug mode.
1527          *
1528          * Core debug mode can be turned on even if the
1529          * {@link enchant.Core#_debug} flag is already set to true.
1530          * @return {enchant.Deferred}
1531          */
1532         debug: function() {
1533             this._debug = true;
1534             return this.start();
1535         },
1536         actualFps: {
1537             get: function() {
1538                 return this._actualFps || this.fps;
1539             }
1540         },
1541         /**
1542          * Requests the next frame.
1543          * @param {Number} delay Amount of time to delay before calling requestAnimationFrame.
1544          * @private
1545          */
1546         _requestNextFrame: function(delay) {
1547             if (!this.ready) {
1548                 return;
1549             }
1550             if (this.fps >= 60 || delay <= 16) {
1551                 this._calledTime = window.getTime();
1552                 window.requestAnimationFrame(this._callTick);
1553             } else {
1554                 setTimeout(function() {
1555                     var core = enchant.Core.instance;
1556                     core._calledTime = window.getTime();
1557                     window.requestAnimationFrame(core._callTick);
1558                 }, Math.max(0, delay));
1559             }
1560         },
1561         /**
1562          * Calls {@link enchant.Core#_tick}.
1563          * @param {Number} time
1564          * @private
1565          */
1566         _callTick: function(time) {
1567             enchant.Core.instance._tick(time);
1568         },
1569         _tick: function(time) {
1570             var e = new enchant.Event('enterframe');
1571             var now = window.getTime();
1572             var elapsed = e.elapsed = now - this.currentTime;
1573             this.currentTime = now;
1574 
1575             this._actualFps = elapsed > 0 ? (1000 / elapsed) : 0;
1576 
1577             var nodes = this.currentScene.childNodes.slice();
1578             var push = Array.prototype.push;
1579             while (nodes.length) {
1580                 var node = nodes.pop();
1581                 node.age++;
1582                 node.dispatchEvent(e);
1583                 if (node.childNodes) {
1584                     push.apply(nodes, node.childNodes);
1585                 }
1586             }
1587 
1588             this.currentScene.age++;
1589             this.currentScene.dispatchEvent(e);
1590             this.dispatchEvent(e);
1591 
1592             this.dispatchEvent(new enchant.Event('exitframe'));
1593             this.frame++;
1594             now = window.getTime();
1595             
1596             this._requestNextFrame(1000 / this.fps - (now - this._calledTime));
1597         },
1598         getTime: function() {
1599             return window.getTime();
1600         },
1601         /**
1602          * Stops the core.
1603          *
1604          * The frame will not be updated, and player input will not be accepted anymore.
1605          * Core can be restarted using {@link enchant.Core#resume}.
1606          */
1607         stop: function() {
1608             this.ready = false;
1609             this.running = false;
1610         },
1611         /**
1612          * Stops the core.
1613          *
1614          * The frame will not be updated, and player input will not be accepted anymore.
1615          * Core can be started again using {@link enchant.Core#resume}.
1616          */
1617         pause: function() {
1618             this.ready = false;
1619         },
1620         /**
1621          * Resumes core operations.
1622          */
1623         resume: function() {
1624             if (this.ready) {
1625                 return;
1626             }
1627             this.currentTime = window.getTime();
1628             this.ready = true;
1629             this.running = true;
1630             this._requestNextFrame(0);
1631         },
1632 
1633         /**
1634          * Switches to a new Scene.
1635          *
1636          * Scenes are controlled using a stack, with the top scene on
1637          * the stack being the one displayed.
1638          * When {@link enchant.Core#pushScene} is executed, the Scene is
1639          * placed top of the stack. Frames will be only updated for the
1640          * Scene which is on the top of the stack.
1641          *
1642          * @param {enchant.Scene} scene The new scene to display.
1643          * @return {enchant.Scene} The new Scene.
1644          */
1645         pushScene: function(scene) {
1646             this._element.appendChild(scene._element);
1647             if (this.currentScene) {
1648                 this.currentScene.dispatchEvent(new enchant.Event('exit'));
1649             }
1650             this.currentScene = scene;
1651             this.currentScene.dispatchEvent(new enchant.Event('enter'));
1652             return this._scenes.push(scene);
1653         },
1654         /**
1655          * Ends the current Scene and returns to the previous Scene.
1656          *
1657          * Scenes are controlled using a stack, with the top scene on
1658          * the stack being the one displayed.
1659          * When {@link enchant.Core#popScene} is executed, the Scene at
1660          * the top of the stack is removed and returned.
1661          *
1662          * @return {enchant.Scene} Removed Scene.
1663          */
1664         popScene: function() {
1665             if (this.currentScene === this.rootScene) {
1666                 return this.currentScene;
1667             }
1668             this._element.removeChild(this.currentScene._element);
1669             this.currentScene.dispatchEvent(new enchant.Event('exit'));
1670             this.currentScene = this._scenes[this._scenes.length - 2];
1671             this.currentScene.dispatchEvent(new enchant.Event('enter'));
1672             return this._scenes.pop();
1673         },
1674         /**
1675          * Overwrites the current Scene with a new Scene.
1676          *
1677          * Executes {@link enchant.Core#popScene} and {@link enchant.Core#pushScene}
1678          * one after another to replace the current scene with the new scene.
1679          *
1680          * @param {enchant.Scene} scene The new scene with which to replace the current scene.
1681          * @return {enchant.Scene} The new Scene.
1682          */
1683         replaceScene: function(scene) {
1684             this.popScene();
1685             return this.pushScene(scene);
1686         },
1687         /**
1688          * Removes a Scene from the Scene stack.
1689          *
1690          * If the scene passed in as a parameter is not the current
1691          * scene, the stack will be searched for the given scene.
1692          * If the given scene does not exist anywhere in the stack,
1693          * this method returns null.
1694          *
1695          * @param {enchant.Scene} scene Scene to be removed.
1696          * @return {enchant.Scene} The deleted Scene.
1697          */
1698         removeScene: function(scene) {
1699             if (this.currentScene === scene) {
1700                 return this.popScene();
1701             } else {
1702                 var i = this._scenes.indexOf(scene);
1703                 if (i !== -1) {
1704                     this._scenes.splice(i, 1);
1705                     this._element.removeChild(scene._element);
1706                     return scene;
1707                 } else {
1708                     return null;
1709                 }
1710             }
1711         },
1712         _buttonListener: function(e) {
1713             this.currentScene.dispatchEvent(e);
1714         },
1715         /**
1716          * Bind a key code to an enchant.js button.
1717          *
1718          * Binds the given key code to the given enchant.js button
1719          * ('left', 'right', 'up', 'down', 'a', 'b').
1720          *
1721          * @param {Number} key Key code for the button to be bound.
1722          * @param {String} button An enchant.js button.
1723          * @return {enchant.Core} this
1724          */
1725         keybind: function(key, button) {
1726             this.keyboardInputManager.keybind(key, button);
1727             this.addEventListener(button + 'buttondown', this._buttonListener);
1728             this.addEventListener(button + 'buttonup', this._buttonListener);
1729             return this;
1730         },
1731         /**
1732          * Delete the key binding for the given key.
1733          *
1734          * @param {Number} key Key code whose binding is to be deleted.
1735          * @return {enchant.Core} this
1736          */
1737         keyunbind: function(key) {
1738             var button = this._keybind[key];
1739             this.keyboardInputManager.keyunbind(key);
1740             this.removeEventListener(button + 'buttondown', this._buttonListener);
1741             this.removeEventListener(button + 'buttonup', this._buttonListener);
1742             return this;
1743         },
1744         changeButtonState: function(button, bool) {
1745             this.keyboardInputManager.changeState(button, bool);
1746         },
1747         /**
1748          * Get the core time (not actual) elapsed since {@link enchant.Core#start} was called.
1749          * @return {Number} Time elapsed (in seconds).
1750          */
1751         getElapsedTime: function() {
1752             return this.frame / this.fps;
1753         }
1754     });
1755 
1756     /**
1757      * Functions for loading assets of the corresponding file type.
1758      * The loading functions must take the file path, extension and
1759      * callback function as arguments, then return the appropriate
1760      * class instance.
1761      * @static
1762      * @private
1763      * @type Object
1764      */
1765     enchant.Core._loadFuncs = {};
1766     enchant.Core._loadFuncs['jpg'] =
1767         enchant.Core._loadFuncs['jpeg'] =
1768             enchant.Core._loadFuncs['gif'] =
1769                 enchant.Core._loadFuncs['png'] =
1770                     enchant.Core._loadFuncs['bmp'] = function(src, ext, callback, onerror) {
1771                         return enchant.Surface.load(src, callback, onerror);
1772                     };
1773     enchant.Core._loadFuncs['mp3'] =
1774         enchant.Core._loadFuncs['aac'] =
1775             enchant.Core._loadFuncs['m4a'] =
1776                 enchant.Core._loadFuncs['wav'] =
1777                     enchant.Core._loadFuncs['ogg'] = function(src, ext, callback, onerror) {
1778                         return enchant.Sound.load(src, 'audio/' + ext, callback, onerror);
1779                     };
1780 
1781     /**
1782      * Get the file extension from a path.
1783      * @param {String} path file path.
1784      * @return {*}
1785      */
1786     enchant.Core.findExt = function(path) {
1787         var matched = path.match(/\.\w+$/);
1788         if (matched && matched.length > 0) {
1789             return matched[0].slice(1).toLowerCase();
1790         }
1791 
1792         // for data URI
1793         if (path.indexOf('data:') === 0) {
1794             return path.split(/[\/;]/)[1].toLowerCase();
1795         }
1796         return null;
1797     };
1798 
1799     /**
1800      * The current Core instance.
1801      * @type enchant.Core
1802      * @static
1803      */
1804     enchant.Core.instance = null;
1805 }());
1806 
1807 /**
1808  * @name enchant.Game
1809  * @class
1810  * enchant.Game is moved to {@link enchant.Core} from v0.6
1811  * @deprecated
1812  */
1813 enchant.Game = enchant.Core;
1814 
1815 /**
1816  * @scope enchant.InputManager.prototype
1817  */
1818 enchant.InputManager = enchant.Class.create(enchant.EventTarget, {
1819     /**
1820      * @name enchant.InputManager
1821      * @class
1822      * Class for managing input.
1823      * @param {*} valueStore object that store input state.
1824      * @param {*} [source=this] source that will be added to event object.
1825      * @constructs
1826      * @extends enchant.EventTarget
1827      */
1828     initialize: function(valueStore, source) {
1829         enchant.EventTarget.call(this);
1830 
1831         /**
1832          * Array that store event target.
1833          * @type enchant.EventTarget[]
1834          */
1835         this.broadcastTarget = [];
1836         /**
1837          * Object that store input state.
1838          * @type Object
1839          */
1840         this.valueStore = valueStore;
1841         /**
1842          * source that will be added to event object.
1843          * @type Object
1844          */
1845         this.source = source || this;
1846 
1847         this._binds = {};
1848 
1849         this._stateHandler = function(e) {
1850             var id = e.source.identifier;
1851             var name = this._binds[id];
1852             this.changeState(name, e.data);
1853         }.bind(this);
1854     },
1855     /**
1856      * Name specified input.
1857      * Input can be watched by flag or event.
1858      * @param {enchant.InputSource} inputSource input source.
1859      * @param {String} name input name.
1860      */
1861     bind: function(inputSource, name) {
1862         inputSource.addEventListener(enchant.Event.INPUT_STATE_CHANGED, this._stateHandler);
1863         this._binds[inputSource.identifier] = name;
1864     },
1865     /**
1866      * Remove binded name.
1867      * @param {enchant.InputSource} inputSource input source.
1868      */
1869     unbind: function(inputSource) {
1870         inputSource.removeEventListener(enchant.Event.INPUT_STATE_CHANGED, this._stateHandler);
1871         delete this._binds[inputSource.identifier];
1872     },
1873     /**
1874      * Add event target.
1875      * @param {enchant.EventTarget} eventTarget broadcast target.
1876      */
1877     addBroadcastTarget: function(eventTarget) {
1878         var i = this.broadcastTarget.indexOf(eventTarget);
1879         if (i === -1) {
1880             this.broadcastTarget.push(eventTarget);
1881         }
1882     },
1883     /**
1884      * Remove event target.
1885      * @param {enchant.EventTarget} eventTarget broadcast target.
1886      */
1887     removeBroadcastTarget: function(eventTarget) {
1888         var i = this.broadcastTarget.indexOf(eventTarget);
1889         if (i !== -1) {
1890             this.broadcastTarget.splice(i, 1);
1891         }
1892     },
1893     /**
1894      * Dispatch event to {@link enchant.InputManager#broadcastTarget}.
1895      * @param {enchant.Event} e event.
1896      */
1897     broadcastEvent: function(e) {
1898         var target = this.broadcastTarget;
1899         for (var i = 0, l = target.length; i < l; i++) {
1900             target[i].dispatchEvent(e);
1901         }
1902     },
1903     /**
1904      * Change state of input.
1905      * @param {String} name input name.
1906      * @param {*} data input state.
1907      */
1908     changeState: function(name, data) {
1909     }
1910 });
1911 
1912 /**
1913  * @scope enchant.InputSource.prototype
1914  */
1915 enchant.InputSource = enchant.Class.create(enchant.EventTarget, {
1916     /**
1917      * @name enchant.InputSource
1918      * @class
1919      * Class that wrap input.
1920      * @param {String} identifier identifier of InputSource.
1921      * @constructs
1922      * @extends enchant.EventTarget
1923      */
1924     initialize: function(identifier) {
1925         enchant.EventTarget.call(this);
1926         /**
1927          * identifier of InputSource.
1928          * @type String
1929          */
1930         this.identifier = identifier;
1931     },
1932     /**
1933      * Notify state change by event.
1934      * @param {*} data state.
1935      */
1936     notifyStateChange: function(data) {
1937         var e = new enchant.Event(enchant.Event.INPUT_STATE_CHANGED);
1938         e.data = data;
1939         e.source = this;
1940         this.dispatchEvent(e);
1941     }
1942 });
1943 
1944 /**
1945  * @scope enchant.BinaryInputManager.prototype
1946  */
1947 enchant.BinaryInputManager = enchant.Class.create(enchant.InputManager, {
1948     /**
1949      * @name enchant.BinaryInputManager
1950      * @class
1951      * Class for managing input.
1952      * @param {*} flagStore object that store input flag.
1953      * @param {String} activeEventNameSuffix event name suffix.
1954      * @param {String} inactiveEventNameSuffix event name suffix.
1955      * @param {*} [source=this] source that will be added to event object.
1956      * @constructs
1957      * @extends enchant.InputManager
1958      */
1959     initialize: function(flagStore, activeEventNameSuffix, inactiveEventNameSuffix, source) {
1960         enchant.InputManager.call(this, flagStore, source);
1961         /**
1962          * The number of active inputs.
1963          * @type Number
1964          */
1965         this.activeInputsNum = 0;
1966         /**
1967          * event name suffix that dispatched by BinaryInputManager.
1968          * @type String
1969          */
1970         this.activeEventNameSuffix = activeEventNameSuffix;
1971         /**
1972          * event name suffix that dispatched by BinaryInputManager.
1973          * @type String
1974          */
1975         this.inactiveEventNameSuffix = inactiveEventNameSuffix;
1976     },
1977     /**
1978      * Name specified input.
1979      * @param {enchant.BinaryInputSource} inputSource input source.
1980      * @param {String} name input name.
1981      * @see enchant.InputManager#bind
1982      */
1983     bind: function(binaryInputSource, name) {
1984         enchant.InputManager.prototype.bind.call(this, binaryInputSource, name);
1985         this.valueStore[name] = false;
1986     },
1987     /**
1988      * Remove binded name.
1989      * @param {enchant.BinaryInputSource} inputSource input source.
1990      * @see enchant.InputManager#unbind
1991      */
1992     unbind: function(binaryInputSource) {
1993         var name = this._binds[binaryInputSource.identifier];
1994         enchant.InputManager.prototype.unbind.call(this, binaryInputSource);
1995         delete this.valueStore[name];
1996     },
1997     /**
1998      * Change state of input.
1999      * @param {String} name input name.
2000      * @param {Boolean} bool input state.
2001      */
2002     changeState: function(name, bool) {
2003         if (bool) {
2004             this._down(name);
2005         } else {
2006             this._up(name);
2007         }
2008     },
2009     _down: function(name) {
2010         var inputEvent;
2011         if (!this.valueStore[name]) {
2012             this.valueStore[name] = true;
2013             inputEvent = new enchant.Event((this.activeInputsNum++) ? 'inputchange' : 'inputstart');
2014             inputEvent.source = this.source;
2015             this.broadcastEvent(inputEvent);
2016         }
2017         var downEvent = new enchant.Event(name + this.activeEventNameSuffix);
2018         downEvent.source = this.source;
2019         this.broadcastEvent(downEvent);
2020     },
2021     _up: function(name) {
2022         var inputEvent;
2023         if (this.valueStore[name]) {
2024             this.valueStore[name] = false;
2025             inputEvent = new enchant.Event((--this.activeInputsNum) ? 'inputchange' : 'inputend');
2026             inputEvent.source = this.source;
2027             this.broadcastEvent(inputEvent);
2028         }
2029         var upEvent = new enchant.Event(name + this.inactiveEventNameSuffix);
2030         upEvent.source = this.source;
2031         this.broadcastEvent(upEvent);
2032     }
2033 });
2034 
2035 /**
2036  * @scope enchant.BinaryInputSource.prototype
2037  */
2038 enchant.BinaryInputSource = enchant.Class.create(enchant.InputSource, {
2039     /**
2040      * @name enchant.BinaryInputSource
2041      * @class
2042      * Class that wrap binary input.
2043      * @param {String} identifier identifier of BinaryInputSource.
2044      * @constructs
2045      * @extends enchant.InputSource
2046      */
2047     initialize: function(identifier) {
2048         enchant.InputSource.call(this, identifier);
2049     }
2050 });
2051 
2052 /**
2053  * @scope enchant.KeyboardInputManager.prototype
2054  */
2055 enchant.KeyboardInputManager = enchant.Class.create(enchant.BinaryInputManager, {
2056     /**
2057      * @name enchant.KeyboardInputManager
2058      * @class
2059      * Class that manage keyboard input.
2060      * @param {HTMLElement} dom element that will be watched.
2061      * @param {*} flagStore object that store input flag.
2062      * @constructs
2063      * @extends enchant.BinaryInputManager
2064      */
2065     initialize: function(domElement, flagStore) {
2066         enchant.BinaryInputManager.call(this, flagStore, 'buttondown', 'buttonup');
2067         this._attachDOMEvent(domElement, 'keydown', true);
2068         this._attachDOMEvent(domElement, 'keyup', false);
2069     },
2070     /**
2071      * Call {@link enchant.BinaryInputManager#bind} with BinaryInputSource equivalent of key code.
2072      * @param {Number} keyCode key code.
2073      * @param {String} name input name.
2074      */
2075     keybind: function(keyCode, name) {
2076         this.bind(enchant.KeyboardInputSource.getByKeyCode('' + keyCode), name);
2077     },
2078     /**
2079      * Call {@link enchant.BinaryInputManager#unbind} with BinaryInputSource equivalent of key code.
2080      * @param {Number} keyCode key code.
2081      */
2082     keyunbind: function(keyCode) {
2083         this.unbind(enchant.KeyboardInputSource.getByKeyCode('' + keyCode));
2084     },
2085     _attachDOMEvent: function(domElement, eventType, state) {
2086         domElement.addEventListener(eventType, function(e) {
2087             var core = enchant.Core.instance;
2088             if (!core || !core.running) {
2089                 return;
2090             }
2091             var code = e.keyCode;
2092             var source = enchant.KeyboardInputSource._instances[code];
2093             if (source) {
2094                 source.notifyStateChange(state);
2095             }
2096         }, true);
2097     }
2098 });
2099 
2100 /**
2101  * @scope enchant.KeyboardInputSource.prototype
2102  */
2103 enchant.KeyboardInputSource = enchant.Class.create(enchant.BinaryInputSource, {
2104     /**
2105      * @name enchant.KeyboardInputSource
2106      * @class
2107      * @param {String} keyCode key code of BinaryInputSource.
2108      * @constructs
2109      * @extends enchant.BinaryInputSource
2110      */
2111     initialize: function(keyCode) {
2112         enchant.BinaryInputSource.call(this, keyCode);
2113     }
2114 });
2115 /**
2116  * @private
2117  */
2118 enchant.KeyboardInputSource._instances = {};
2119 /**
2120  * @static
2121  * Get the instance by key code.
2122  * @param {Number} keyCode key code.
2123  * @return {enchant.KeyboardInputSource} instance.
2124  */
2125 enchant.KeyboardInputSource.getByKeyCode = function(keyCode) {
2126     if (!this._instances[keyCode]) {
2127         this._instances[keyCode] = new enchant.KeyboardInputSource(keyCode);
2128     }
2129     return this._instances[keyCode];
2130 };
2131 
2132 /**
2133  * @scope enchant.Node.prototype
2134  */
2135 enchant.Node = enchant.Class.create(enchant.EventTarget, {
2136     /**
2137      * @name enchant.Node
2138      * @class
2139      * Base class for objects in the display tree which is rooted at a Scene.
2140      * Not to be used directly.
2141      * @constructs
2142      * @extends enchant.EventTarget
2143      */
2144     initialize: function() {
2145         enchant.EventTarget.call(this);
2146 
2147         this._dirty = false;
2148 
2149         this._matrix = [ 1, 0, 0, 1, 0, 0 ];
2150 
2151         this._x = 0;
2152         this._y = 0;
2153         this._offsetX = 0;
2154         this._offsetY = 0;
2155 
2156         /**
2157          * The age (frames) of this node which will be increased before this node receives {@link enchant.Event.ENTER_FRAME} event.
2158          * @type Number
2159          */
2160         this.age = 0;
2161 
2162         /**
2163          * Parent Node of this Node.
2164          * @type enchant.Group
2165          */
2166         this.parentNode = null;
2167         /**
2168          * Scene to which Node belongs.
2169          * @type enchant.Scene
2170          */
2171         this.scene = null;
2172 
2173         this.addEventListener('touchstart', function(e) {
2174             if (this.parentNode) {
2175                 this.parentNode.dispatchEvent(e);
2176             }
2177         });
2178         this.addEventListener('touchmove', function(e) {
2179             if (this.parentNode) {
2180                 this.parentNode.dispatchEvent(e);
2181             }
2182         });
2183         this.addEventListener('touchend', function(e) {
2184             if (this.parentNode) {
2185                 this.parentNode.dispatchEvent(e);
2186             }
2187         });
2188 
2189         // Nodeが生成される際に, tl プロパティに Timeline オブジェクトを追加している.
2190         if (enchant.ENV.USE_ANIMATION) {
2191             this.tl = new enchant.Timeline(this);
2192         }
2193     },
2194     /**
2195      * Move the Node to the given target location.
2196      * @param {Number} x Target x coordinates.
2197      * @param {Number} y Target y coordinates.
2198      */
2199     moveTo: function(x, y) {
2200         this.x = x;
2201         this.y = y;
2202     },
2203     /**
2204      * Move the Node relative to its current position.
2205      * @param {Number} x x axis movement distance.
2206      * @param {Number} y y axis movement distance.
2207      */
2208     moveBy: function(x, y) {
2209         this.x += x;
2210         this.y += y;
2211     },
2212     /**
2213      * x coordinates of the Node.
2214      * @type Number
2215      */
2216     x: {
2217         get: function() {
2218             return this._x;
2219         },
2220         set: function(x) {
2221             if(this._x !== x) {
2222                 this._x = x;
2223                 this._dirty = true;
2224             }
2225         }
2226     },
2227     /**
2228      * y coordinates of the Node.
2229      * @type Number
2230      */
2231     y: {
2232         get: function() {
2233             return this._y;
2234         },
2235         set: function(y) {
2236             if(this._y !== y) {
2237                 this._y = y;
2238                 this._dirty = true;
2239             }
2240         }
2241     },
2242     _updateCoordinate: function() {
2243         var node = this;
2244         var tree = [ node ];
2245         var parent = node.parentNode;
2246         var scene = this.scene;
2247         while (parent && node._dirty) {
2248             tree.unshift(parent);
2249             node = node.parentNode;
2250             parent = node.parentNode;
2251         }
2252         var matrix = enchant.Matrix.instance;
2253         var stack = matrix.stack;
2254         var mat = [];
2255         var newmat, ox, oy;
2256         stack.push(tree[0]._matrix);
2257         for (var i = 1, l = tree.length; i < l; i++) {
2258             node = tree[i];
2259             newmat = [];
2260             matrix.makeTransformMatrix(node, mat);
2261             matrix.multiply(stack[stack.length - 1], mat, newmat);
2262             node._matrix = newmat;
2263             stack.push(newmat);
2264             ox = (typeof node._originX === 'number') ? node._originX : node._width / 2 || 0;
2265             oy = (typeof node._originY === 'number') ? node._originY : node._height / 2 || 0;
2266             var vec = [ ox, oy ];
2267             matrix.multiplyVec(newmat, vec, vec);
2268             node._offsetX = vec[0] - ox;
2269             node._offsetY = vec[1] - oy;
2270             node._dirty = false;
2271         }
2272         matrix.reset();
2273     },
2274     remove: function() {
2275         if (this.parentNode) {
2276             this.parentNode.removeChild(this);
2277         }
2278         if (this.childNodes) {
2279             var childNodes = this.childNodes.slice();
2280             for(var i = childNodes.length-1; i >= 0; i--) {
2281                 childNodes[i].remove();
2282             }
2283         }
2284         
2285         this.clearEventListener();
2286     }
2287 });
2288 
2289 var _intersectBetweenClassAndInstance = function(Class, instance) {
2290     var ret = [];
2291     var c;
2292     for (var i = 0, l = Class.collection.length; i < l; i++) {
2293         c = Class.collection[i];
2294         if (instance._intersectOne(c)) {
2295             ret.push(c);
2296         }
2297     }
2298     return ret;
2299 };
2300 
2301 var _intersectBetweenClassAndClass = function(Class1, Class2) {
2302     var ret = [];
2303     var c1, c2;
2304     for (var i = 0, l = Class1.collection.length; i < l; i++) {
2305         c1 = Class1.collection[i];
2306         for (var j = 0, ll = Class2.collection.length; j < ll; j++) {
2307             c2 = Class2.collection[j];
2308             if (c1._intersectOne(c2)) {
2309                 ret.push([ c1, c2 ]);
2310             }
2311         }
2312     }
2313     return ret;
2314 };
2315 
2316 var _intersectStrictBetweenClassAndInstance = function(Class, instance) {
2317     var ret = [];
2318     var c;
2319     for (var i = 0, l = Class.collection.length; i < l; i++) {
2320         c = Class.collection[i];
2321         if (instance._intersectStrictOne(c)) {
2322             ret.push(c);
2323         }
2324     }
2325     return ret;
2326 };
2327 
2328 var _intersectStrictBetweenClassAndClass = function(Class1, Class2) {
2329     var ret = [];
2330     var c1, c2;
2331     for (var i = 0, l = Class1.collection.length; i < l; i++) {
2332         c1 = Class1.collection[i];
2333         for (var j = 0, ll = Class2.collection.length; j < ll; j++) {
2334             c2 = Class2.collection[j];
2335             if (c1._intersectStrictOne(c2)) {
2336                 ret.push([ c1, c2 ]);
2337             }
2338         }
2339     }
2340     return ret;
2341 };
2342 
2343 var _staticIntersect = function(other) {
2344     if (other instanceof enchant.Entity) {
2345         return _intersectBetweenClassAndInstance(this, other);
2346     } else if (typeof other === 'function' && other.collection) {
2347         return _intersectBetweenClassAndClass(this, other);
2348     }
2349     return false;
2350 };
2351 
2352 var _staticIntersectStrict = function(other) {
2353     if (other instanceof enchant.Entity) {
2354         return _intersectStrictBetweenClassAndInstance(this, other);
2355     } else if (typeof other === 'function' && other.collection) {
2356         return _intersectStrictBetweenClassAndClass(this, other);
2357     }
2358     return false;
2359 };
2360 
2361 var _nodePrototypeClearEventListener = enchant.Node.prototype.clearEventListener;
2362 
2363 /**
2364  * @scope enchant.Entity.prototype
2365  */
2366 enchant.Entity = enchant.Class.create(enchant.Node, {
2367     /**
2368      * @name enchant.Entity
2369      * @class
2370      * A class with objects displayed as DOM elements. Not to be used directly.
2371      * @constructs
2372      * @extends enchant.Node
2373      */
2374     initialize: function() {
2375         var core = enchant.Core.instance;
2376         enchant.Node.call(this);
2377 
2378         this._rotation = 0;
2379         this._scaleX = 1;
2380         this._scaleY = 1;
2381 
2382         this._touchEnabled = true;
2383         this._clipping = false;
2384 
2385         this._originX = null;
2386         this._originY = null;
2387 
2388         this._width = 0;
2389         this._height = 0;
2390         this._backgroundColor = null;
2391         this._debugColor = '#0000ff';
2392         this._opacity = 1;
2393         this._visible = true;
2394         this._buttonMode = null;
2395 
2396         this._style = {};
2397         this.__styleStatus = {};
2398 
2399         this._isContainedInCollection = false;
2400 
2401         /**
2402          * @type String
2403          */
2404         this.compositeOperation = null;
2405 
2406         /**
2407          * Defines this Entity as a button.
2408          * When touched or clicked the corresponding button event is dispatched.
2409          * Valid buttonModes are: left, right, up, down, a, b. 
2410          * @type String
2411          */
2412         this.buttonMode = null;
2413         /**
2414          * Indicates if this Entity is being clicked.
2415          * Only works when {@link enchant.Entity.buttonMode} is set.
2416          * @type Boolean
2417          */
2418         this.buttonPressed = false;
2419         this.addEventListener('touchstart', function() {
2420             if (!this.buttonMode) {
2421                 return;
2422             }
2423             this.buttonPressed = true;
2424             this.dispatchEvent(new enchant.Event(this.buttonMode + 'buttondown'));
2425             core.changeButtonState(this.buttonMode, true);
2426         });
2427         this.addEventListener('touchend', function() {
2428             if (!this.buttonMode) {
2429                 return;
2430             }
2431             this.buttonPressed = false;
2432             this.dispatchEvent(new enchant.Event(this.buttonMode + 'buttonup'));
2433             core.changeButtonState(this.buttonMode, false);
2434         });
2435 
2436         this.enableCollection();
2437     },
2438     /**
2439      * The width of the Entity.
2440      * @type Number
2441      */
2442     width: {
2443         get: function() {
2444             return this._width;
2445         },
2446         set: function(width) {
2447             if(this._width !== width) {
2448                 this._width = width;
2449                 this._dirty = true;
2450             }
2451         }
2452     },
2453     /**
2454      * The height of the Entity.
2455      * @type Number
2456      */
2457     height: {
2458         get: function() {
2459             return this._height;
2460         },
2461         set: function(height) {
2462             if(this._height !== height) {
2463                 this._height = height;
2464                 this._dirty = true;
2465             }
2466         }
2467     },
2468     /**
2469      * The Entity background color.
2470      * Must be provided in the same format as the CSS 'color' property.
2471      * @type String
2472      */
2473     backgroundColor: {
2474         get: function() {
2475             return this._backgroundColor;
2476         },
2477         set: function(color) {
2478             this._backgroundColor = color;
2479         }
2480     },
2481     /**
2482      * The Entity debug color.
2483      * Must be provided in the same format as the CSS 'color' property.
2484      * @type String
2485      */
2486     debugColor: {
2487         get: function() {
2488             return this._debugColor;
2489         },
2490         set: function(color) {
2491             this._debugColor = color;
2492         }
2493     },
2494     /**
2495      * The transparency of this entity.
2496      * Defines the transparency level from 0 to 1
2497      * (0 is completely transparent, 1 is completely opaque).
2498      * @type Number
2499      */
2500     opacity: {
2501         get: function() {
2502             return this._opacity;
2503         },
2504         set: function(opacity) {
2505             this._opacity = parseFloat(opacity);
2506         }
2507     },
2508     /**
2509      * Indicates whether or not to display this Entity.
2510      * @type Boolean
2511      */
2512     visible: {
2513         get: function() {
2514             return this._visible;
2515         },
2516         set: function(visible) {
2517             this._visible = visible;
2518         }
2519     },
2520     /**
2521      * Indicates whether or not this Entity can be touched.
2522      * @type Boolean
2523      */
2524     touchEnabled: {
2525         get: function() {
2526             return this._touchEnabled;
2527         },
2528         set: function(enabled) {
2529             this._touchEnabled = enabled;
2530             if (enabled) {
2531                 this._style.pointerEvents = 'all';
2532             } else {
2533                 this._style.pointerEvents = 'none';
2534             }
2535         }
2536     },
2537     /**
2538      * Performs a collision detection based on whether or not the bounding rectangles are intersecting.
2539      * @param {*} other An object like Entity, with the properties x, y, width, height, which are used for the 
2540      * collision detection.
2541      * @return {Boolean} True, if a collision was detected.
2542      */
2543     intersect: function(other) {
2544         if (other instanceof enchant.Entity) {
2545             return this._intersectOne(other);
2546         } else if (typeof other === 'function' && other.collection) {
2547             return _intersectBetweenClassAndInstance(other, this);
2548         }
2549         return false;
2550     },
2551     _intersectOne: function(other) {
2552         if (this._dirty) {
2553             this._updateCoordinate();
2554         } if (other._dirty) {
2555             other._updateCoordinate();
2556         }
2557         return this._offsetX < other._offsetX + other.width && other._offsetX < this._offsetX + this.width &&
2558             this._offsetY < other._offsetY + other.height && other._offsetY < this._offsetY + this.height;
2559     },
2560     intersectStrict: function(other) {
2561         if (other instanceof enchant.Entity) {
2562             return this._intersectStrictOne(other);
2563         } else if (typeof other === 'function' && other.collection) {
2564             return _intersectStrictBetweenClassAndInstance(other, this);
2565         }
2566         return false;
2567     },
2568     _intersectStrictOne: function(other) {
2569         if (this._dirty) {
2570             this._updateCoordinate();
2571         } if (other._dirty) {
2572             other._updateCoordinate();
2573         }
2574         var rect1 = this.getOrientedBoundingRect(),
2575             rect2 = other.getOrientedBoundingRect(),
2576             lt1 = rect1.leftTop, rt1 = rect1.rightTop,
2577             lb1 = rect1.leftBottom, rb1 = rect1.rightBottom,
2578             lt2 = rect2.leftTop, rt2 = rect2.rightTop,
2579             lb2 = rect2.leftBottom, rb2 = rect2.rightBottom,
2580             ltx1 = lt1[0], lty1 = lt1[1], rtx1 = rt1[0], rty1 = rt1[1],
2581             lbx1 = lb1[0], lby1 = lb1[1], rbx1 = rb1[0], rby1 = rb1[1],
2582             ltx2 = lt2[0], lty2 = lt2[1], rtx2 = rt2[0], rty2 = rt2[1],
2583             lbx2 = lb2[0], lby2 = lb2[1], rbx2 = rb2[0], rby2 = rb2[1],
2584             t1 = [ rtx1 - ltx1, rty1 - lty1 ],
2585             r1 = [ rbx1 - rtx1, rby1 - rty1 ],
2586             b1 = [ lbx1 - rbx1, lby1 - rby1 ],
2587             l1 = [ ltx1 - lbx1, lty1 - lby1 ],
2588             t2 = [ rtx2 - ltx2, rty2 - lty2 ],
2589             r2 = [ rbx2 - rtx2, rby2 - rty2 ],
2590             b2 = [ lbx2 - rbx2, lby2 - rby2 ],
2591             l2 = [ ltx2 - lbx2, lty2 - lby2 ],
2592             cx1 = (ltx1 + rtx1 + lbx1 + rbx1) >> 2,
2593             cy1 = (lty1 + rty1 + lby1 + rby1) >> 2,
2594             cx2 = (ltx2 + rtx2 + lbx2 + rbx2) >> 2,
2595             cy2 = (lty2 + rty2 + lby2 + rby2) >> 2,
2596             i, j, poss1, poss2, dirs1, dirs2, pos1, pos2, dir1, dir2,
2597             px1, py1, px2, py2, dx1, dy1, dx2, dy2, vx, vy, c, c1, c2;
2598         if (t1[0] * (cy2 - lty1) - t1[1] * (cx2 - ltx1) > 0 &&
2599             r1[0] * (cy2 - rty1) - r1[1] * (cx2 - rtx1) > 0 &&
2600             b1[0] * (cy2 - rby1) - b1[1] * (cx2 - rbx1) > 0 &&
2601             l1[0] * (cy2 - lby1) - l1[1] * (cx2 - lbx1) > 0) {
2602             return true;
2603         } else if (t2[0] * (cy1 - lty2) - t2[1] * (cx1 - ltx2) > 0 &&
2604             r2[0] * (cy1 - rty2) - r2[1] * (cx1 - rtx2) > 0 &&
2605             b2[0] * (cy1 - rby2) - b2[1] * (cx1 - rbx2) > 0 &&
2606             l2[0] * (cy1 - lby2) - l2[1] * (cx1 - lbx2) > 0) {
2607             return true;
2608         } else {
2609             poss1 = [ lt1, rt1, rb1, lb1 ];
2610             poss2 = [ lt2, rt2, rb2, lb2 ];
2611             dirs1 = [ t1, r1, b1, l1 ];
2612             dirs2 = [ t2, r2, b2, l2 ];
2613             for (i = 0; i < 4; i++) {
2614                 pos1 = poss1[i];
2615                 px1 = pos1[0]; py1 = pos1[1];
2616                 dir1 = dirs1[i];
2617                 dx1 = dir1[0]; dy1 = dir1[1];
2618                 for (j = 0; j < 4; j++) {
2619                     pos2 = poss2[j];
2620                     px2 = pos2[0]; py2 = pos2[1];
2621                     dir2 = dirs2[j];
2622                     dx2 = dir2[0]; dy2 = dir2[1];
2623                     c = dx1 * dy2 - dy1 * dx2;
2624                     if (c !== 0) {
2625                         vx = px2 - px1;
2626                         vy = py2 - py1;
2627                         c1 = (vx * dy1 - vy * dx1) / c;
2628                         c2 = (vx * dy2 - vy * dx2) / c;
2629                         if (0 < c1 && c1 < 1 && 0 < c2 && c2 < 1) {
2630                             return true;
2631                         }
2632                     }
2633                 }
2634             }
2635             return false;
2636         }
2637     },
2638     /**
2639      * Performs a collision detection based on distance from the Entity's central point.
2640      * @param {*} other An object like Entity, with properties x, y, width, height, which are used for the 
2641      * collision detection.
2642      * @param {Number} [distance] The greatest distance to be considered for a collision.
2643      * The default distance is the average of both objects width and height.
2644      * @return {Boolean} True, if a collision was detected.
2645      */
2646     within: function(other, distance) {
2647         if (this._dirty) {
2648             this._updateCoordinate();
2649         } if (other._dirty) {
2650             other._updateCoordinate();
2651         }
2652         if (distance == null) {
2653             distance = (this.width + this.height + other.width + other.height) / 4;
2654         }
2655         var _;
2656         return (_ = this._offsetX - other._offsetX + (this.width - other.width) / 2) * _ +
2657             (_ = this._offsetY - other._offsetY + (this.height - other.height) / 2) * _ < distance * distance;
2658     },
2659     /**
2660      * Enlarges or shrinks this Entity.
2661      * @param {Number} x Scaling factor on the x axis.
2662      * @param {Number} [y] Scaling factor on the y axis.
2663      */
2664     scale: function(x, y) {
2665         this._scaleX *= x;
2666         this._scaleY *= (y != null) ? y : x;
2667         this._dirty = true;
2668     },
2669     /**
2670      * Rotate this Entity.
2671      * @param {Number} deg Rotation angle (degree).
2672      */
2673     rotate: function(deg) {
2674         this.rotation += deg;
2675     },
2676     /**
2677      * Scaling factor on the x axis of this Entity.
2678      * @type Number
2679      */
2680     scaleX: {
2681         get: function() {
2682             return this._scaleX;
2683         },
2684         set: function(scaleX) {
2685             if(this._scaleX !== scaleX) {
2686                 this._scaleX = scaleX;
2687                 this._dirty = true;
2688             }
2689         }
2690     },
2691     /**
2692      * Scaling factor on the y axis of this Entity.
2693      * @type Number
2694      */
2695     scaleY: {
2696         get: function() {
2697             return this._scaleY;
2698         },
2699         set: function(scaleY) {
2700             if(this._scaleY !== scaleY) {
2701                 this._scaleY = scaleY;
2702                 this._dirty = true;
2703             }
2704         }
2705     },
2706     /**
2707      * Entity rotation angle (degree).
2708      * @type Number
2709      */
2710     rotation: {
2711         get: function() {
2712             return this._rotation;
2713         },
2714         set: function(rotation) {
2715             if(this._rotation !== rotation) {
2716                 this._rotation = rotation;
2717                 this._dirty = true;
2718             }
2719         }
2720     },
2721     /**
2722      * The point of origin used for rotation and scaling.
2723      * @type Number
2724      */
2725     originX: {
2726         get: function() {
2727             return this._originX;
2728         },
2729         set: function(originX) {
2730             if(this._originX !== originX) {
2731                 this._originX = originX;
2732                 this._dirty = true;
2733             }
2734         }
2735     },
2736     /**
2737      * The point of origin used for rotation and scaling.
2738      * @type Number
2739      */
2740     originY: {
2741         get: function() {
2742             return this._originY;
2743         },
2744         set: function(originY) {
2745             if(this._originY !== originY) {
2746                 this._originY = originY;
2747                 this._dirty = true;
2748             }
2749         }
2750     },
2751     /**
2752      */
2753     enableCollection: function() {
2754         this.addEventListener('addedtoscene', this._addSelfToCollection);
2755         this.addEventListener('removedfromscene', this._removeSelfFromCollection);
2756         if (this.scene) {
2757             this._addSelfToCollection();
2758         }
2759     },
2760     /**
2761      */
2762     disableCollection: function() {
2763         this.removeEventListener('addedtoscene', this._addSelfToCollection);
2764         this.removeEventListener('removedfromscene', this._removeSelfFromCollection);
2765         if (this.scene) {
2766             this._removeSelfFromCollection();
2767         }
2768     },
2769     /**#nocode+*/
2770     clearEventListener: function() {
2771         _nodePrototypeClearEventListener.apply(this,arguments);
2772         if (this.scene) {
2773             this._removeSelfFromCollection();
2774         }
2775     },
2776     /**#nocode-*/
2777     _addSelfToCollection: function() {
2778         if (this._isContainedInCollection) {
2779             return;
2780         }
2781 
2782         var Constructor = this.getConstructor();
2783         Constructor._collectionTarget.forEach(function(C) {
2784             C.collection.push(this);
2785         }, this);
2786 
2787         this._isContainedInCollection = true;
2788     },
2789     _removeSelfFromCollection: function() {
2790         if (!this._isContainedInCollection) {
2791             return;
2792         }
2793 
2794         var Constructor = this.getConstructor();
2795         Constructor._collectionTarget.forEach(function(C) {
2796             var i = C.collection.indexOf(this);
2797             if (i !== -1) {
2798                 C.collection.splice(i, 1);
2799             }
2800         }, this);
2801 
2802         this._isContainedInCollection = false;
2803     },
2804     getBoundingRect: function() {
2805         var w = this.width || 0;
2806         var h = this.height || 0;
2807         var mat = this._matrix;
2808         var m11w = mat[0] * w, m12w = mat[1] * w,
2809             m21h = mat[2] * h, m22h = mat[3] * h,
2810             mdx = mat[4], mdy = mat[5];
2811         var xw = [ mdx, m11w + mdx, m21h + mdx, m11w + m21h + mdx ].sort(function(a, b) { return a - b; });
2812         var yh = [ mdy, m12w + mdy, m22h + mdy, m12w + m22h + mdy ].sort(function(a, b) { return a - b; });
2813 
2814         return {
2815             left: xw[0],
2816             top: yh[0],
2817             width: xw[3] - xw[0],
2818             height: yh[3] - yh[0]
2819         };
2820     },
2821     getOrientedBoundingRect: function() {
2822         var w = this.width || 0;
2823         var h = this.height || 0;
2824         var mat = this._matrix;
2825         var m11w = mat[0] * w, m12w = mat[1] * w,
2826             m21h = mat[2] * h, m22h = mat[3] * h,
2827             mdx = mat[4], mdy = mat[5];
2828 
2829         return {
2830             leftTop: [ mdx, mdy ],
2831             rightTop: [ m11w + mdx, m12w + mdy ],
2832             leftBottom: [ m21h + mdx, m22h + mdy ],
2833             rightBottom: [ m11w + m21h + mdx, m12w + m22h + mdy ]
2834         };
2835     },
2836     getConstructor: function() {
2837         return Object.getPrototypeOf(this).constructor;
2838     }
2839 });
2840 
2841 var _collectizeConstructor = function(Constructor) {
2842     if (Constructor._collective) {
2843         return;
2844     }
2845     var rel = enchant.Class.getInheritanceTree(Constructor);
2846     var i = rel.indexOf(enchant.Entity);
2847     if (i !== -1) {
2848         Constructor._collectionTarget = rel.splice(0, i + 1);
2849     } else {
2850         Constructor._collectionTarget = [];
2851     }
2852     Constructor.intersect = _staticIntersect;
2853     Constructor.intersectStrict = _staticIntersectStrict;
2854     Constructor.collection = [];
2855     Constructor._collective = true;
2856 };
2857 
2858 _collectizeConstructor(enchant.Entity);
2859 
2860 enchant.Entity._inherited = function(subclass) {
2861     _collectizeConstructor(subclass);
2862 };
2863 
2864 /**
2865  * @scope enchant.Sprite.prototype
2866  */
2867 enchant.Sprite = enchant.Class.create(enchant.Entity, {
2868     /**
2869      * @name enchant.Sprite
2870      * @class
2871      * Class which can display images.
2872      * @param {Number} width Sprite width.
2873      * @param {Number} height Sprite height.
2874      *
2875      * @example
2876      * var bear = new Sprite(32, 32);
2877      * bear.image = core.assets['chara1.gif'];
2878      *
2879      * @constructs
2880      * @extends enchant.Entity
2881      */
2882     initialize: function(width, height) {
2883         enchant.Entity.call(this);
2884 
2885         this.width = width;
2886         this.height = height;
2887         this._image = null;
2888         this._debugColor = '#ff0000';
2889         this._frameLeft = 0;
2890         this._frameTop = 0;
2891         this._frame = 0;
2892         this._frameSequence = null;
2893     },
2894     /**
2895      * Image displayed in the Sprite.
2896      * @type enchant.Surface
2897      */
2898     image: {
2899         get: function() {
2900             return this._image;
2901         },
2902         set: function(image) {
2903             if (image === undefined) {
2904                 throw new Error('Assigned value on Sprite.image is undefined. Please double-check image path, and check if the image you want to use is preload before use.');
2905             }
2906             if (image === this._image) {
2907                 return;
2908             }
2909             this._image = image;
2910             this._computeFramePosition();
2911         }
2912     },
2913     /**
2914      * Index of the frame to be displayed.
2915      * Frames with the same width and height as Sprite will be arrayed from upper left corner of the 
2916      * {@link enchant.Sprite#image} image. When a sequence of numbers is provided, the displayed frame 
2917      * will switch automatically. At the end of the array the sequence will restart. By setting 
2918      * a value within the sequence to null, the frame switching is stopped.
2919      *
2920      * @example
2921      * var sprite = new Sprite(32, 32);
2922      * sprite.frame = [0, 1, 0, 2]
2923      * //-> 0, 1, 0, 2, 0, 1, 0, 2,..
2924      * sprite.frame = [0, 1, 0, 2, null]
2925      * //-> 0, 1, 0, 2, (2, 2,.. :stop)
2926      *
2927      * @type Number|Array
2928      */
2929     frame: {
2930         get: function() {
2931             return this._frame;
2932         },
2933         set: function(frame) {
2934             if (((this._frameSequence == null) && (this._frame === frame)) || (this._deepCompareToPreviousFrame(frame))) {
2935                 return;
2936             }
2937             if (frame instanceof Array) {
2938                 this._frameSequence = frame;
2939             } else {
2940                 this._frameSequence = null;
2941                 this._frame = frame;
2942                 this._computeFramePosition();
2943             }
2944         }
2945     },
2946     _frameSequence: {
2947         get: function() {
2948             return this.__frameSequence;
2949         },
2950         set: function(frameSequence) {
2951             if(frameSequence && !this.__frameSequence) {
2952                 this.addEventListener(enchant.Event.ENTER_FRAME, this._rotateFrameSequence);
2953             } else if(!frameSequence && this.__frameSequence) {
2954                 this.removeEventListener(enchant.Event.ENTER_FRAME, this._rotateFrameSequence);
2955             }
2956             if(frameSequence) {
2957                 this.__frameSequence = frameSequence.slice();
2958                 this._originalFrameSequence = frameSequence.slice();
2959                 this._rotateFrameSequence();
2960             } else {
2961                 this.__frameSequence = null;
2962                 this._originalFrameSequence = null;
2963             }
2964         }
2965     },
2966     /**
2967      * If we are setting the same frame Array as animation,
2968      * just continue animating.
2969      * @private
2970      */
2971     _deepCompareToPreviousFrame: function(frameArray) {
2972         if (frameArray === this._originalFrameSequence) {
2973             return true;
2974         }
2975         if (frameArray == null || this._originalFrameSequence == null) {
2976             return false;
2977         }
2978         if (!(frameArray instanceof Array)) {
2979             return false;
2980         }
2981         if (frameArray.length !== this._originalFrameSequence.length) {
2982             return false;
2983         }
2984         for (var i = 0; i < frameArray.length; ++i) {
2985             if (frameArray[i] !== this._originalFrameSequence[i]){
2986                 return false;
2987             }
2988         }
2989         return true;
2990     },
2991     /**
2992      * 0 <= frame
2993      * @private
2994      */
2995     _computeFramePosition: function() {
2996         var image = this._image;
2997         var row;
2998         if (image != null) {
2999             row = image.width / this._width | 0;
3000             this._frameLeft = (this._frame % row | 0) * this._width;
3001             this._frameTop = (this._frame / row | 0) * this._height % image.height;
3002         }
3003     },
3004     _rotateFrameSequence: function() {
3005         var frameSequence = this._frameSequence;
3006         if (frameSequence && frameSequence.length !== 0) {
3007             var nextFrame = frameSequence.shift();
3008             if (nextFrame === null) {
3009                 this._frameSequence = null;
3010                 this.dispatchEvent(new enchant.Event(enchant.Event.ANIMATION_END));
3011             } else {
3012                 this._frame = nextFrame;
3013                 this._computeFramePosition();
3014                 frameSequence.push(nextFrame);
3015             }
3016         }
3017     },
3018     /**#nocode+*/
3019     width: {
3020         get: function() {
3021             return this._width;
3022         },
3023         set: function(width) {
3024             this._width = width;
3025             this._computeFramePosition();
3026             this._dirty = true;
3027         }
3028     },
3029     height: {
3030         get: function() {
3031             return this._height;
3032         },
3033         set: function(height) {
3034             this._height = height;
3035             this._computeFramePosition();
3036             this._dirty = true;
3037         }
3038     },
3039     /**#nocode-*/
3040     cvsRender: function(ctx) {
3041         var image = this._image,
3042             w = this._width, h = this._height,
3043             iw, ih, elem, sx, sy, sw, sh;
3044         if (image && w !== 0 && h !== 0) {
3045             iw = image.width;
3046             ih = image.height;
3047             if (iw < w || ih < h) {
3048                 ctx.fillStyle = enchant.Surface._getPattern(image);
3049                 ctx.fillRect(0, 0, w, h);
3050             } else {
3051                 elem = image._element;
3052                 sx = this._frameLeft;
3053                 sy = Math.min(this._frameTop, ih - h);
3054                 // IE9 doesn't allow for negative or 0 widths/heights when drawing on the CANVAS element
3055                 sw = Math.max(0.01, Math.min(iw - sx, w));
3056                 sh = Math.max(0.01, Math.min(ih - sy, h));
3057                 ctx.drawImage(elem, sx, sy, sw, sh, 0, 0, w, h);
3058             }
3059         }
3060     },
3061     domRender: (function() {
3062         if (enchant.ENV.VENDOR_PREFIX === 'ms') {
3063             return function(element) {
3064                 if (this._image) {
3065                     if (this._image._css) {
3066                         this._style['background-image'] = this._image._css;
3067                         this._style['background-position'] =
3068                             -this._frameLeft + 'px ' +
3069                             -this._frameTop + 'px';
3070                     } else if (this._image._element) {
3071                     }
3072                 }
3073             };
3074         } else {
3075             return function(element) {
3076                 if (this._image) {
3077                     if (this._image._css) {
3078                         this._style['background-image'] = this._image._css;
3079                         this._style['background-position'] =
3080                             -this._frameLeft + 'px ' +
3081                             -this._frameTop + 'px';
3082                     } else if (this._image._element) {
3083                     }
3084                 }
3085             };
3086         }
3087     }())
3088 });
3089 
3090 /**
3091  * @scope enchant.Label.prototype
3092  */
3093 enchant.Label = enchant.Class.create(enchant.Entity, {
3094     /**
3095      * @name enchant.Label
3096      * @class
3097      * A class for Label object.
3098      * @constructs
3099      * @extends enchant.Entity
3100      */
3101     initialize: function(text) {
3102         enchant.Entity.call(this);
3103 
3104         this.text = text || '';
3105         this.width = 300;
3106         this.font = '14px serif';
3107         this.textAlign = 'left';
3108 
3109         this._debugColor = '#ff0000';
3110     },
3111     /**#nocode+*/
3112     width: {
3113         get: function() {
3114             return this._width;
3115         },
3116         set: function(width) {
3117             this._width = width;
3118             this._dirty = true;
3119             // issue #164
3120             this.updateBoundArea();
3121         }
3122     },
3123     /**#nocode-*/
3124     /**
3125      * Text to be displayed.
3126      * @type String
3127      */
3128     text: {
3129         get: function() {
3130             return this._text;
3131         },
3132         set: function(text) {
3133             text = '' + text;
3134             if(this._text === text) {
3135                 return;
3136             }
3137             this._text = text;
3138             text = text.replace(/<br ?\/?>/gi, '<br/>');
3139             this._splitText = text.split('<br/>');
3140             this.updateBoundArea();
3141             for (var i = 0, l = this._splitText.length; i < l; i++) {
3142                 text = this._splitText[i];
3143                 var metrics = this.getMetrics(text);
3144                 this._splitText[i] = {};
3145                 this._splitText[i].text = text;
3146                 this._splitText[i].height = metrics.height;
3147                 this._splitText[i].width = metrics.width;
3148             }
3149         }
3150     },
3151     /**
3152      * Specifies horizontal alignment of text.
3153      * Can be set according to the format of the CSS 'text-align' property.
3154      * @type String
3155      */
3156     textAlign: {
3157         get: function() {
3158             return this._style['text-align'];
3159         },
3160         set: function(textAlign) {
3161             this._style['text-align'] = textAlign;
3162             this.updateBoundArea();
3163         }
3164     },
3165     /**
3166      * Font settings.
3167      * Can be set according to the format of the CSS 'font' property.
3168      * @type String
3169      */
3170     font: {
3171         get: function() {
3172             return this._style.font;
3173         },
3174         set: function(font) {
3175             this._style.font = font;
3176             this.updateBoundArea();
3177         }
3178     },
3179     /**
3180      * Text color settings.
3181      * Can be set according to the format of the CSS 'color' property.
3182      * @type String
3183      */
3184     color: {
3185         get: function() {
3186             return this._style.color;
3187         },
3188         set: function(color) {
3189             this._style.color = color;
3190         }
3191     },
3192     cvsRender: function(ctx) {
3193         var x, y = 0;
3194         var labelWidth = this.width;
3195         var charWidth, amount, line, text, c, buf, increase, length;
3196         var bufWidth;
3197         if (this._splitText) {
3198             ctx.textBaseline = 'top';
3199             ctx.font = this.font;
3200             ctx.fillStyle = this.color || '#000000';
3201             charWidth = ctx.measureText(' ').width;
3202             amount = labelWidth / charWidth;
3203             for (var i = 0, l = this._splitText.length; i < l; i++) {
3204                 line = this._splitText[i];
3205                 text = line.text;
3206                 c = 0;
3207                 while (text.length > c + amount || ctx.measureText(text.slice(c, c + amount)).width > labelWidth) {
3208                     buf = '';
3209                     increase = amount;
3210                     length = 0;
3211                     while (increase > 0) {
3212                         if (ctx.measureText(buf).width < labelWidth) {
3213                             length += increase;
3214                             buf = text.slice(c, c + length);
3215                         } else {
3216                             length -= increase;
3217                             buf = text.slice(c, c + length);
3218                         }
3219                         increase = increase / 2 | 0;
3220                     }
3221                     ctx.fillText(buf, 0, y);
3222                     y += line.height - 1;
3223                     c += length;
3224                 }
3225                 buf = text.slice(c, c + text.length);
3226                 if (this.textAlign === 'right') {
3227                     x = labelWidth - ctx.measureText(buf).width;
3228                 } else if (this.textAlign === 'center') {
3229                     x = (labelWidth - ctx.measureText(buf).width) / 2;
3230                 } else {
3231                     x = 0;
3232                 }
3233                 ctx.fillText(buf, x, y);
3234                 y += line.height - 1;
3235             }
3236         }
3237     },
3238     domRender: function(element) {
3239         if (element.innerHTML !== this._text) {
3240             element.innerHTML = this._text;
3241         }
3242     },
3243     detectRender: function(ctx) {
3244         ctx.fillRect(this._boundOffset, 0, this._boundWidth, this._boundHeight);
3245     },
3246     updateBoundArea: function() {
3247         var metrics = this.getMetrics();
3248         this._boundWidth = metrics.width;
3249         this._boundHeight = metrics.height;
3250         if (this.textAlign === 'right') {
3251             this._boundOffset = this.width - this._boundWidth;
3252         } else if (this.textAlign === 'center') {
3253             this._boundOffset = (this.width - this._boundWidth) / 2;
3254         } else {
3255             this._boundOffset = 0;
3256         }
3257     },
3258     getMetrics: function(text) {
3259         var ret = {};
3260         var div, width, height;
3261         if (document.body) {
3262             div = document.createElement('div');
3263             for (var prop in this._style) {
3264                 if(prop !== 'width' && prop !== 'height') {
3265                     div.style[prop] = this._style[prop];
3266                 }
3267             }
3268             text = text || this._text;
3269             div.innerHTML = text.replace(/ /g, ' ');
3270             div.style.whiteSpace = 'noWrap';
3271             div.style.lineHeight = 1;
3272             document.body.appendChild(div);
3273             var computedStyle = getComputedStyle(div);
3274             ret.height = parseInt(computedStyle.height, 10) + 1;
3275             div.style.position = 'absolute';
3276             ret.width = parseInt(computedStyle.width, 10) + 1;
3277             document.body.removeChild(div);
3278         } else {
3279             ret.width = this.width;
3280             ret.height = this.height;
3281         }
3282         return ret;
3283     }
3284 });
3285 
3286 /**
3287  * @scope enchant.Map.prototype
3288  */
3289 enchant.Map = enchant.Class.create(enchant.Entity, {
3290     /**
3291      * @name enchant.Map
3292      * @class
3293      * A class to create and display maps from a tile set.
3294      * @param {Number} tileWidth Tile width.
3295      * @param {Number} tileHeight Tile height.
3296      * @constructs
3297      * @extends enchant.Entity
3298      */
3299     initialize: function(tileWidth, tileHeight) {
3300         var core = enchant.Core.instance;
3301 
3302         enchant.Entity.call(this);
3303 
3304         var surface = new enchant.Surface(core.width, core.height);
3305         this._surface = surface;
3306         var canvas = surface._element;
3307         canvas.style.position = 'absolute';
3308         if (enchant.ENV.RETINA_DISPLAY && core.scale === 2) {
3309             canvas.width = core.width * 2;
3310             canvas.height = core.height * 2;
3311             this._style.webkitTransformOrigin = '0 0';
3312             this._style.webkitTransform = 'scale(0.5)';
3313         } else {
3314             canvas.width = core.width;
3315             canvas.height = core.height;
3316         }
3317         this._context = canvas.getContext('2d');
3318 
3319         this._tileWidth = tileWidth || 0;
3320         this._tileHeight = tileHeight || 0;
3321         this._image = null;
3322         this._data = [
3323             [
3324                 []
3325             ]
3326         ];
3327         this._dirty = false;
3328         this._tight = false;
3329 
3330         this.touchEnabled = false;
3331 
3332         /**
3333          * Two dimensional array to store if collision detection should be performed for a tile.
3334          * @type Number[][]
3335          */
3336         this.collisionData = null;
3337 
3338         this._listeners['render'] = null;
3339         this.addEventListener('render', function() {
3340             if(this._dirty) {
3341                 this._previousOffsetX = this._previousOffsetY = null;
3342             }
3343         });
3344     },
3345     /**
3346      * Set map data.
3347      * Sets the tile data, whereas the data (two-dimensional array with indizes starting from 0) 
3348      * is mapped on the image starting from the upper left corner.
3349      * When more than one map data array is set, they are displayed in reverse order.
3350      * @param {...Number[][]} data Two-dimensional array of tile indizes. Multiple designations possible.
3351      */
3352     loadData: function(data) {
3353         this._data = Array.prototype.slice.apply(arguments);
3354         this._dirty = true;
3355 
3356         this._tight = false;
3357         for (var i = 0, len = this._data.length; i < len; i++) {
3358             var c = 0;
3359             data = this._data[i];
3360             for (var y = 0, l = data.length; y < l; y++) {
3361                 for (var x = 0, ll = data[y].length; x < ll; x++) {
3362                     if (data[y][x] >= 0) {
3363                         c++;
3364                     }
3365                 }
3366             }
3367             if (c / (data.length * data[0].length) > 0.2) {
3368                 this._tight = true;
3369                 break;
3370             }
3371         }
3372     },
3373     /**
3374      * Checks what tile is present at the given position.
3375      * @param {Number} x x coordinates of the point on the map.
3376      * @param {Number} y y coordinates of the point on the map.
3377      * @return {*} The tile data for the given position.
3378      */
3379     checkTile: function(x, y) {
3380         if (x < 0 || this.width <= x || y < 0 || this.height <= y) {
3381             return false;
3382         }
3383         var width = this._image.width;
3384         var height = this._image.height;
3385         var tileWidth = this._tileWidth || width;
3386         var tileHeight = this._tileHeight || height;
3387         x = x / tileWidth | 0;
3388         y = y / tileHeight | 0;
3389         //		return this._data[y][x];
3390         var data = this._data[0];
3391         return data[y][x];
3392     },
3393     /**
3394      * Judges whether or not obstacles are on top of Map.
3395      * @param {Number} x x coordinates of detection spot on map.
3396      * @param {Number} y y coordinates of detection spot on map.
3397      * @return {Boolean} True, if there are obstacles.
3398      */
3399     hitTest: function(x, y) {
3400         if (x < 0 || this.width <= x || y < 0 || this.height <= y) {
3401             return false;
3402         }
3403         var width = this._image.width;
3404         var height = this._image.height;
3405         var tileWidth = this._tileWidth || width;
3406         var tileHeight = this._tileHeight || height;
3407         x = x / tileWidth | 0;
3408         y = y / tileHeight | 0;
3409         if (this.collisionData != null) {
3410             return this.collisionData[y] && !!this.collisionData[y][x];
3411         } else {
3412             for (var i = 0, len = this._data.length; i < len; i++) {
3413                 var data = this._data[i];
3414                 var n;
3415                 if (data[y] != null && (n = data[y][x]) != null &&
3416                     0 <= n && n < (width / tileWidth | 0) * (height / tileHeight | 0)) {
3417                     return true;
3418                 }
3419             }
3420             return false;
3421         }
3422     },
3423     /**
3424      * Image with which the tile set is displayed on the map.
3425      * @type enchant.Surface
3426      */
3427     image: {
3428         get: function() {
3429             return this._image;
3430         },
3431         set: function(image) {
3432             var core = enchant.Core.instance;
3433 
3434             this._image = image;
3435             if (enchant.ENV.RETINA_DISPLAY && core.scale === 2) {
3436                 var img = new enchant.Surface(image.width * 2, image.height * 2);
3437                 var tileWidth = this._tileWidth || image.width;
3438                 var tileHeight = this._tileHeight || image.height;
3439                 var row = image.width / tileWidth | 0;
3440                 var col = image.height / tileHeight | 0;
3441                 for (var y = 0; y < col; y++) {
3442                     for (var x = 0; x < row; x++) {
3443                         img.draw(image, x * tileWidth, y * tileHeight, tileWidth, tileHeight,
3444                             x * tileWidth * 2, y * tileHeight * 2, tileWidth * 2, tileHeight * 2);
3445                     }
3446                 }
3447                 this._doubledImage = img;
3448             }
3449             this._dirty = true;
3450         }
3451     },
3452     /**
3453      * Map tile width.
3454      * @type Number
3455      */
3456     tileWidth: {
3457         get: function() {
3458             return this._tileWidth;
3459         },
3460         set: function(tileWidth) {
3461             if(this._tileWidth !== tileWidth) {
3462                 this._tileWidth = tileWidth;
3463                 this._dirty = true;
3464             }
3465         }
3466     },
3467     /**
3468      * Map tile height.
3469      * @type Number
3470      */
3471     tileHeight: {
3472         get: function() {
3473             return this._tileHeight;
3474         },
3475         set: function(tileHeight) {
3476             if(this._tileHeight !== tileHeight) {
3477                 this._tileHeight = tileHeight;
3478                 this._dirty = true;
3479             }
3480         }
3481     },
3482     /**
3483      * @private
3484      */
3485     width: {
3486         get: function() {
3487             return this._tileWidth * this._data[0][0].length;
3488         }
3489     },
3490     /**
3491      * @private
3492      */
3493     height: {
3494         get: function() {
3495             return this._tileHeight * this._data[0].length;
3496         }
3497     },
3498     /**
3499      * @private
3500      */
3501     redraw: function(x, y, width, height) {
3502         if (this._image == null) {
3503             return;
3504         }
3505 
3506         var image, tileWidth, tileHeight, dx, dy;
3507         if (this._doubledImage) {
3508             image = this._doubledImage;
3509             tileWidth = this._tileWidth * 2;
3510             tileHeight = this._tileHeight * 2;
3511             dx = -this._offsetX * 2;
3512             dy = -this._offsetY * 2;
3513             x *= 2;
3514             y *= 2;
3515             width *= 2;
3516             height *= 2;
3517         } else {
3518             image = this._image;
3519             tileWidth = this._tileWidth;
3520             tileHeight = this._tileHeight;
3521             dx = -this._offsetX;
3522             dy = -this._offsetY;
3523         }
3524         var row = image.width / tileWidth | 0;
3525         var col = image.height / tileHeight | 0;
3526         var left = Math.max((x + dx) / tileWidth | 0, 0);
3527         var top = Math.max((y + dy) / tileHeight | 0, 0);
3528         var right = Math.ceil((x + dx + width) / tileWidth);
3529         var bottom = Math.ceil((y + dy + height) / tileHeight);
3530 
3531         var source = image._element;
3532         var context = this._context;
3533         var canvas = context.canvas;
3534         context.clearRect(x, y, width, height);
3535         for (var i = 0, len = this._data.length; i < len; i++) {
3536             var data = this._data[i];
3537             var r = Math.min(right, data[0].length);
3538             var b = Math.min(bottom, data.length);
3539             for (y = top; y < b; y++) {
3540                 for (x = left; x < r; x++) {
3541                     var n = data[y][x];
3542                     if (0 <= n && n < row * col) {
3543                         var sx = (n % row) * tileWidth;
3544                         var sy = (n / row | 0) * tileHeight;
3545                         context.drawImage(source, sx, sy, tileWidth, tileHeight,
3546                             x * tileWidth - dx, y * tileHeight - dy, tileWidth, tileHeight);
3547                     }
3548                 }
3549             }
3550         }
3551     },
3552     /**
3553      * @private
3554      */
3555     updateBuffer: function() {
3556         if (this._visible === undefined || this._visible) {
3557             var core = enchant.Core.instance;
3558             if (this._dirty || this._previousOffsetX == null) {
3559                 this.redraw(0, 0, core.width, core.height);
3560             } else if (this._offsetX !== this._previousOffsetX ||
3561                     this._offsetY !== this._previousOffsetY) {
3562                 if (this._tight) {
3563                     var x = -this._offsetX;
3564                     var y = -this._offsetY;
3565                     var px = -this._previousOffsetX;
3566                     var py = -this._previousOffsetY;
3567                     var w1 = x - px + core.width;
3568                     var w2 = px - x + core.width;
3569                     var h1 = y - py + core.height;
3570                     var h2 = py - y + core.height;
3571                     if (w1 > this._tileWidth && w2 > this._tileWidth &&
3572                             h1 > this._tileHeight && h2 > this._tileHeight) {
3573                         var sx, sy, dx, dy, sw, sh;
3574                         if (w1 < w2) {
3575                             sx = 0;
3576                             dx = px - x;
3577                             sw = w1;
3578                         } else {
3579                             sx = x - px;
3580                             dx = 0;
3581                             sw = w2;
3582                         }
3583                         if (h1 < h2) {
3584                             sy = 0;
3585                             dy = py - y;
3586                             sh = h1;
3587                         } else {
3588                             sy = y - py;
3589                             dy = 0;
3590                             sh = h2;
3591                         }
3592 
3593                         if (core._buffer == null) {
3594                             core._buffer = document.createElement('canvas');
3595                             core._buffer.width = this._context.canvas.width;
3596                             core._buffer.height = this._context.canvas.height;
3597                         }
3598                         var context = core._buffer.getContext('2d');
3599                         if (this._doubledImage) {
3600                             context.clearRect(0, 0, sw * 2, sh * 2);
3601                             context.drawImage(this._context.canvas,
3602                                     sx * 2, sy * 2, sw * 2, sh * 2, 0, 0, sw * 2, sh * 2);
3603                             context = this._context;
3604                             context.clearRect(dx * 2, dy * 2, sw * 2, sh * 2);
3605                             context.drawImage(core._buffer,
3606                                     0, 0, sw * 2, sh * 2, dx * 2, dy * 2, sw * 2, sh * 2);
3607                         } else {
3608                             context.clearRect(0, 0, sw, sh);
3609                             context.drawImage(this._context.canvas,
3610                                     sx, sy, sw, sh, 0, 0, sw, sh);
3611                             context = this._context;
3612                             context.clearRect(dx, dy, sw, sh);
3613                             context.drawImage(core._buffer,
3614                                     0, 0, sw, sh, dx, dy, sw, sh);
3615                         }
3616 
3617                         if (dx === 0) {
3618                             this.redraw(sw, 0, core.width - sw, core.height);
3619                         } else {
3620                             this.redraw(0, 0, core.width - sw, core.height);
3621                         }
3622                         if (dy === 0) {
3623                             this.redraw(0, sh, core.width, core.height - sh);
3624                         } else {
3625                             this.redraw(0, 0, core.width, core.height - sh);
3626                         }
3627                     } else {
3628                         this.redraw(0, 0, core.width, core.height);
3629                     }
3630                 } else {
3631                     this.redraw(0, 0, core.width, core.height);
3632                 }
3633             }
3634             this._previousOffsetX = this._offsetX;
3635             this._previousOffsetY = this._offsetY;
3636         }
3637     },
3638     cvsRender: function(ctx) {
3639         if (this.width !== 0 && this.height !== 0) {
3640             var core = enchant.Core.instance;
3641             this.updateBuffer();
3642             ctx.save();
3643             ctx.setTransform(1, 0, 0, 1, 0, 0);
3644             var cvs = this._context.canvas;
3645                 ctx.drawImage(cvs, 0, 0, core.width, core.height);
3646             ctx.restore();
3647         }
3648     },
3649     domRender: function(element) {
3650         if (this._image) {
3651             this.updateBuffer();
3652             this._style['background-image'] = this._surface._css;
3653             // bad performance
3654             this._style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'matrix(1, 0, 0, 1, 0, 0)';
3655         }
3656     }
3657 });
3658 
3659 
3660 /**
3661  * @scope enchant.Group.prototype
3662  */
3663 enchant.Group = enchant.Class.create(enchant.Node, {
3664     /**
3665      * @name enchant.Group
3666      * @class
3667      * A class that can hold multiple {@link enchant.Node}.
3668      *
3669      * @example
3670      * var stage = new Group();
3671      * stage.addChild(player);
3672      * stage.addChild(enemy);
3673      * stage.addChild(map);
3674      * stage.addEventListener('enterframe', function() {
3675      *     // Moves the entire frame in according to the player's coordinates.
3676      *     if (this.x > 64 - player.x) {
3677      *         this.x = 64 - player.x;
3678      *     }
3679      * });
3680      * @constructs
3681      * @extends enchant.Node
3682      */
3683     initialize: function() {
3684         /**
3685          * Child Nodes.
3686          * @type enchant.Node[]
3687          */
3688         this.childNodes = [];
3689 
3690         enchant.Node.call(this);
3691 
3692         this._rotation = 0;
3693         this._scaleX = 1;
3694         this._scaleY = 1;
3695 
3696         this._originX = null;
3697         this._originY = null;
3698 
3699         this.__dirty = false;
3700 
3701         [enchant.Event.ADDED_TO_SCENE, enchant.Event.REMOVED_FROM_SCENE]
3702             .forEach(function(event) {
3703                 this.addEventListener(event, function(e) {
3704                     this.childNodes.forEach(function(child) {
3705                         child.scene = this.scene;
3706                         child.dispatchEvent(e);
3707                     }, this);
3708                 });
3709             }, this);
3710     },
3711     /**
3712      * Adds a Node to the Group.
3713      * @param {enchant.Node} node Node to be added.
3714      */
3715     addChild: function(node) {
3716         if (node.parentNode) {
3717             node.parentNode.removeChild(node);
3718         }
3719         this.childNodes.push(node);
3720         node.parentNode = this;
3721         var childAdded = new enchant.Event('childadded');
3722         childAdded.node = node;
3723         childAdded.next = null;
3724         this.dispatchEvent(childAdded);
3725         node.dispatchEvent(new enchant.Event('added'));
3726         if (this.scene) {
3727             node.scene = this.scene;
3728             var addedToScene = new enchant.Event('addedtoscene');
3729             node.dispatchEvent(addedToScene);
3730         }
3731     },
3732     /**
3733      * Incorporates Node into Group.
3734      * @param {enchant.Node} node Node to be incorporated.
3735      * @param {enchant.Node} reference Node in position before insertion.
3736      */
3737     insertBefore: function(node, reference) {
3738         if (node.parentNode) {
3739             node.parentNode.removeChild(node);
3740         }
3741         var i = this.childNodes.indexOf(reference);
3742         if (i !== -1) {
3743             this.childNodes.splice(i, 0, node);
3744             node.parentNode = this;
3745             var childAdded = new enchant.Event('childadded');
3746             childAdded.node = node;
3747             childAdded.next = reference;
3748             this.dispatchEvent(childAdded);
3749             node.dispatchEvent(new enchant.Event('added'));
3750             if (this.scene) {
3751                 node.scene = this.scene;
3752                 var addedToScene = new enchant.Event('addedtoscene');
3753                 node.dispatchEvent(addedToScene);
3754             }
3755         } else {
3756             this.addChild(node);
3757         }
3758     },
3759     /**
3760      * Remove a Node from the Group.
3761      * @param {enchant.Node} node Node to be deleted.
3762      */
3763     removeChild: function(node) {
3764         var i;
3765         if ((i = this.childNodes.indexOf(node)) !== -1) {
3766             this.childNodes.splice(i, 1);
3767             node.parentNode = null;
3768             var childRemoved = new enchant.Event('childremoved');
3769             childRemoved.node = node;
3770             this.dispatchEvent(childRemoved);
3771             node.dispatchEvent(new enchant.Event('removed'));
3772             if (this.scene) {
3773                 node.scene = null;
3774                 var removedFromScene = new enchant.Event('removedfromscene');
3775                 node.dispatchEvent(removedFromScene);
3776             }
3777         }
3778     },
3779     /**
3780      * The Node which is the first child.
3781      * @type enchant.Node
3782      */
3783     firstChild: {
3784         get: function() {
3785             return this.childNodes[0];
3786         }
3787     },
3788     /**
3789      * The Node which is the last child.
3790      * @type enchant.Node
3791      */
3792     lastChild: {
3793         get: function() {
3794             return this.childNodes[this.childNodes.length - 1];
3795         }
3796     },
3797     /**
3798     * Group rotation angle (degree).
3799     * @type Number
3800     */
3801     rotation: {
3802         get: function() {
3803             return this._rotation;
3804         },
3805         set: function(rotation) {
3806             if(this._rotation !== rotation) {
3807                 this._rotation = rotation;
3808                 this._dirty = true;
3809             }
3810         }
3811     },
3812     /**
3813     * Scaling factor on the x axis of the Group.
3814     * @type Number
3815     * @see enchant.Group#originX
3816     * @see enchant.Group#originY
3817     */
3818     scaleX: {
3819         get: function() {
3820             return this._scaleX;
3821         },
3822         set: function(scale) {
3823             if(this._scaleX !== scale) {
3824                 this._scaleX = scale;
3825                 this._dirty = true;
3826             }
3827         }
3828     },
3829     /**
3830     * Scaling factor on the y axis of the Group.
3831     * @type Number
3832     * @see enchant.Group#originX
3833     * @see enchant.Group#originY
3834     */
3835     scaleY: {
3836         get: function() {
3837             return this._scaleY;
3838         },
3839         set: function(scale) {
3840             if(this._scaleY !== scale) {
3841                 this._scaleY = scale;
3842                 this._dirty = true;
3843             }
3844         }
3845     },
3846     /**
3847     * origin point of rotation, scaling
3848     * @type Number
3849     */
3850     originX: {
3851         get: function() {
3852             return this._originX;
3853         },
3854         set: function(originX) {
3855             if(this._originX !== originX) {
3856                 this._originX = originX;
3857                 this._dirty = true;
3858             }
3859         }
3860     },
3861     /**
3862     * origin point of rotation, scaling
3863     * @type Number
3864     */
3865     originY: {
3866         get: function() {
3867             return this._originY;
3868         },
3869         set: function(originY) {
3870             if(this._originY !== originY) {
3871                 this._originY = originY;
3872                 this._dirty = true;
3873             }
3874         }
3875     },
3876     /**#nocode+*/
3877     _dirty: {
3878         get: function() {
3879             return this.__dirty;
3880         },
3881         set: function(dirty) {
3882             dirty = !!dirty;
3883             this.__dirty = dirty;
3884             if (dirty) {
3885                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
3886                     this.childNodes[i]._dirty = true;
3887                 }
3888             }
3889         }
3890     }
3891     /**#nocode-*/
3892 });
3893 
3894 enchant.Matrix = enchant.Class.create({
3895     initialize: function() {
3896         this.reset();
3897     },
3898     reset: function() {
3899         this.stack = [];
3900         this.stack.push([ 1, 0, 0, 1, 0, 0 ]);
3901     },
3902     makeTransformMatrix: function(node, dest) {
3903         var x = node._x;
3904         var y = node._y;
3905         var width = node.width || 0;
3906         var height = node.height || 0;
3907         var rotation = node._rotation || 0;
3908         var scaleX = (typeof node._scaleX === 'number') ? node._scaleX : 1;
3909         var scaleY = (typeof node._scaleY === 'number') ? node._scaleY : 1;
3910         var theta = rotation * Math.PI / 180;
3911         var tmpcos = Math.cos(theta);
3912         var tmpsin = Math.sin(theta);
3913         var w = (typeof node._originX === 'number') ? node._originX : width / 2;
3914         var h = (typeof node._originY === 'number') ? node._originY : height / 2;
3915         var a = scaleX * tmpcos;
3916         var b = scaleX * tmpsin;
3917         var c = scaleY * tmpsin;
3918         var d = scaleY * tmpcos;
3919         dest[0] = a;
3920         dest[1] = b;
3921         dest[2] = -c;
3922         dest[3] = d;
3923         dest[4] = (-a * w + c * h + x + w);
3924         dest[5] = (-b * w - d * h + y + h);
3925     },
3926     multiply: function(m1, m2, dest) {
3927         var a11 = m1[0], a21 = m1[2], adx = m1[4],
3928             a12 = m1[1], a22 = m1[3], ady = m1[5];
3929         var b11 = m2[0], b21 = m2[2], bdx = m2[4],
3930             b12 = m2[1], b22 = m2[3], bdy = m2[5];
3931 
3932         dest[0] = a11 * b11 + a21 * b12;
3933         dest[1] = a12 * b11 + a22 * b12;
3934         dest[2] = a11 * b21 + a21 * b22;
3935         dest[3] = a12 * b21 + a22 * b22;
3936         dest[4] = a11 * bdx + a21 * bdy + adx;
3937         dest[5] = a12 * bdx + a22 * bdy + ady;
3938     },
3939     multiplyVec: function(mat, vec, dest) {
3940         var x = vec[0], y = vec[1];
3941         var m11 = mat[0], m21 = mat[2], mdx = mat[4],
3942             m12 = mat[1], m22 = mat[3], mdy = mat[5];
3943         dest[0] = m11 * x + m21 * y + mdx;
3944         dest[1] = m12 * x + m22 * y + mdy;
3945     }
3946 });
3947 enchant.Matrix.instance = new enchant.Matrix();
3948 
3949 enchant.DetectColorManager = enchant.Class.create({
3950     initialize: function(reso, max) {
3951         this.reference = [];
3952         this.colorResolution = reso || 16;
3953         this.max = max || 1;
3954         this.capacity = Math.pow(this.colorResolution, 3);
3955         for (var i = 1, l = this.capacity; i < l; i++) {
3956             this.reference[i] = null;
3957         }
3958     },
3959     attachDetectColor: function(sprite) {
3960         var i = this.reference.indexOf(null);
3961         if (i === -1) {
3962             i = 1;
3963         }
3964         this.reference[i] = sprite;
3965         return this._getColor(i);
3966     },
3967     detachDetectColor: function(sprite) {
3968         var i = this.reference.indexOf(sprite);
3969         if (i !== -1) {
3970             this.reference[i] = null;
3971         }
3972     },
3973     _getColor: function(n) {
3974         var C = this.colorResolution;
3975         var d = C / this.max;
3976         return [
3977             parseInt((n / C / C) % C, 10) / d,
3978             parseInt((n / C) % C, 10) / d,
3979             parseInt(n % C, 10) / d,
3980             1.0
3981         ];
3982     },
3983     _decodeDetectColor: function(color, i) {
3984         i = i || 0;
3985         var C = this.colorResolution;
3986         return ~~(color[i] * C * C * C / 256) +
3987             ~~(color[i + 1] * C * C / 256) +
3988             ~~(color[i + 2] * C / 256);
3989     },
3990     getSpriteByColor: function(color) {
3991         return this.reference[this._decodeDetectColor(color)];
3992     },
3993     getSpriteByColors: function(rgba) {
3994         var i, l, id, result,
3995             score = 0,
3996             found = {};
3997 
3998         for (i = 0, l = rgba.length; i < l; i+= 4) {
3999             id = this._decodeDetectColor(rgba, i);
4000             found[id] = (found[id] || 0) + 1;
4001         }
4002         for (id in found) {
4003             if (found[id] > score) {
4004                 score = found[id];
4005                 result = id;
4006             }
4007         }
4008 
4009         return this.reference[result];
4010     }
4011 });
4012 
4013 enchant.DomManager = enchant.Class.create({
4014     initialize: function(node, elementDefinition) {
4015         var core = enchant.Core.instance;
4016         this.layer = null;
4017         this.targetNode = node;
4018         if (typeof elementDefinition === 'string') {
4019             this.element = document.createElement(elementDefinition);
4020         } else if (elementDefinition instanceof HTMLElement) {
4021             this.element = elementDefinition;
4022         }
4023         this.style = this.element.style;
4024         this.style.position = 'absolute';
4025         this.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0px 0px';
4026         if (core._debug) {
4027             this.style.border = '1px solid blue';
4028             this.style.margin = '-1px';
4029         }
4030 
4031         var manager = this;
4032         this._setDomTarget = function() {
4033             manager.layer._touchEventTarget = manager.targetNode;
4034         };
4035         this._attachEvent();
4036     },
4037     getDomElement: function() {
4038         return this.element;
4039     },
4040     getDomElementAsNext: function() {
4041         return this.element;
4042     },
4043     getNextManager: function(manager) {
4044         var i = this.targetNode.parentNode.childNodes.indexOf(manager.targetNode);
4045         if (i !== this.targetNode.parentNode.childNodes.length - 1) {
4046             return this.targetNode.parentNode.childNodes[i + 1]._domManager;
4047         } else {
4048             return null;
4049         }
4050     },
4051     addManager: function(childManager, nextManager) {
4052         var nextElement;
4053         if (nextManager) {
4054             nextElement = nextManager.getDomElementAsNext();
4055         }
4056         var element = childManager.getDomElement();
4057         if (element instanceof Array) {
4058             element.forEach(function(child) {
4059                 if (nextElement) {
4060                     this.element.insertBefore(child, nextElement);
4061                 } else {
4062                     this.element.appendChild(child);
4063                 }
4064             }, this);
4065         } else {
4066             if (nextElement) {
4067                 this.element.insertBefore(element, nextElement);
4068             } else {
4069                 this.element.appendChild(element);
4070             }
4071         }
4072         this.setLayer(this.layer);
4073     },
4074     removeManager: function(childManager) {
4075         if (childManager instanceof enchant.DomlessManager) {
4076             childManager._domRef.forEach(function(element) {
4077                 this.element.removeChild(element);
4078             }, this);
4079         } else {
4080             this.element.removeChild(childManager.element);
4081         }
4082         this.setLayer(this.layer);
4083     },
4084     setLayer: function(layer) {
4085         this.layer = layer;
4086         var node = this.targetNode;
4087         var manager;
4088         if (node.childNodes) {
4089             for (var i = 0, l = node.childNodes.length; i < l; i++) {
4090                 manager = node.childNodes[i]._domManager;
4091                 if (manager) {
4092                     manager.setLayer(layer);
4093                 }
4094             }
4095         }
4096     },
4097     render: function(inheritMat) {
4098         var node = this.targetNode;
4099         var matrix = enchant.Matrix.instance;
4100         var stack = matrix.stack;
4101         var dest = [];
4102         matrix.makeTransformMatrix(node, dest);
4103         matrix.multiply(stack[stack.length - 1], dest, dest);
4104         matrix.multiply(inheritMat, dest, inheritMat);
4105         node._matrix = inheritMat;
4106         var ox = (typeof node._originX === 'number') ? node._originX : node.width / 2 || 0;
4107         var oy = (typeof node._originY === 'number') ? node._originY : node.height / 2 || 0;
4108         var vec = [ ox, oy ];
4109         matrix.multiplyVec(dest, vec, vec);
4110 
4111         node._offsetX = vec[0] - ox;
4112         node._offsetY = vec[1] - oy;
4113         if(node.parentNode && !(node.parentNode instanceof enchant.Group)) {
4114             node._offsetX += node.parentNode._offsetX;
4115             node._offsetY += node.parentNode._offsetY;
4116         }
4117         if (node._dirty) {
4118             this.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'matrix(' +
4119                 dest[0].toFixed(10) + ',' +
4120                 dest[1].toFixed(10) + ',' +
4121                 dest[2].toFixed(10) + ',' +
4122                 dest[3].toFixed(10) + ',' +
4123                 dest[4].toFixed(10) + ',' +
4124                 dest[5].toFixed(10) +
4125             ')';
4126         }
4127         this.domRender();
4128     },
4129     domRender: function() {
4130         var node = this.targetNode;
4131         if(!node._style) {
4132             node._style = {};
4133         }
4134         if(!node.__styleStatus) {
4135             node.__styleStatus = {};
4136         }
4137         if (node.width !== null) {
4138             node._style.width = node.width + 'px';
4139         }
4140         if (node.height !== null) {
4141             node._style.height = node.height + 'px';
4142         }
4143         node._style.opacity = node._opacity;
4144         node._style['background-color'] = node._backgroundColor;
4145         if (typeof node._visible !== 'undefined') {
4146             node._style.display = node._visible ? 'block' : 'none';
4147         }
4148         if (typeof node.domRender === 'function') {
4149             node.domRender(this.element);
4150         }
4151         var value;
4152         for (var prop in node._style) {
4153             value = node._style[prop];
4154             if(node.__styleStatus[prop] !== value && value != null) {
4155                 this.style.setProperty(prop, '' + value);
4156                 node.__styleStatus[prop] = value;
4157             }
4158         }
4159     },
4160     _attachEvent: function() {
4161         if (enchant.ENV.TOUCH_ENABLED) {
4162             this.element.addEventListener('touchstart', this._setDomTarget, true);
4163         }
4164         this.element.addEventListener('mousedown', this._setDomTarget, true);
4165     },
4166     _detachEvent: function() {
4167         if (enchant.ENV.TOUCH_ENABLED) {
4168             this.element.removeEventListener('touchstart', this._setDomTarget, true);
4169         }
4170         this.element.removeEventListener('mousedown', this._setDomTarget, true);
4171     },
4172     remove: function() {
4173         this._detachEvent();
4174         this.element = this.style = this.targetNode = null;
4175     }
4176 });
4177 
4178 enchant.DomlessManager = enchant.Class.create({
4179     initialize: function(node) {
4180         this._domRef = [];
4181         this.targetNode = node;
4182     },
4183     _register: function(element, nextElement) {
4184         var i = this._domRef.indexOf(nextElement);
4185         if (element instanceof Array) {
4186             if (i === -1) {
4187                 Array.prototype.push.apply(this._domRef, element);
4188             } else {
4189                 Array.prototype.splice.apply(this._domRef, [i, 0].concat(element));
4190             }
4191         } else {
4192             if (i === -1) {
4193                 this._domRef.push(element);
4194             } else {
4195                 this._domRef.splice(i, 0, element);
4196             }
4197         }
4198     },
4199     getNextManager: function(manager) {
4200         var i = this.targetNode.parentNode.childNodes.indexOf(manager.targetNode);
4201         if (i !== this.targetNode.parentNode.childNodes.length - 1) {
4202             return this.targetNode.parentNode.childNodes[i + 1]._domManager;
4203         } else {
4204             return null;
4205         }
4206     },
4207     getDomElement: function() {
4208         var ret = [];
4209         this.targetNode.childNodes.forEach(function(child) {
4210             ret = ret.concat(child._domManager.getDomElement());
4211         });
4212         return ret;
4213     },
4214     getDomElementAsNext: function() {
4215         if (this._domRef.length) {
4216             return this._domRef[0];
4217         } else {
4218             var nextManager = this.getNextManager(this);
4219             if (nextManager) {
4220                 return nextManager.element;
4221             } else {
4222                 return null;
4223             }
4224         }
4225     },
4226     addManager: function(childManager, nextManager) {
4227         var parentNode = this.targetNode.parentNode;
4228         if (parentNode) {
4229             if (nextManager === null) {
4230                 nextManager = this.getNextManager(this);
4231             }
4232             if (parentNode instanceof enchant.Scene) {
4233                 parentNode._layers.Dom._domManager.addManager(childManager, nextManager);
4234             } else {
4235                 parentNode._domManager.addManager(childManager, nextManager);
4236             }
4237         }
4238         var nextElement = nextManager ? nextManager.getDomElementAsNext() : null;
4239         this._register(childManager.getDomElement(), nextElement);
4240         this.setLayer(this.layer);
4241     },
4242     removeManager: function(childManager) {
4243         var dom;
4244         var i = this._domRef.indexOf(childManager.element);
4245         if (i !== -1) {
4246             dom = this._domRef[i];
4247             dom.parentNode.removeChild(dom);
4248             this._domRef.splice(i, 1);
4249         }
4250         this.setLayer(this.layer);
4251     },
4252     setLayer: function(layer) {
4253         this.layer = layer;
4254         var node = this.targetNode;
4255         var manager;
4256         if (node.childNodes) {
4257             for (var i = 0, l = node.childNodes.length; i < l; i++) {
4258                 manager = node.childNodes[i]._domManager;
4259                 if (manager) {
4260                     manager.setLayer(layer);
4261                 }
4262             }
4263         }
4264     },
4265     render: function(inheritMat) {
4266         var matrix = enchant.Matrix.instance;
4267         var stack = matrix.stack;
4268         var node = this.targetNode;
4269         var dest = [];
4270         matrix.makeTransformMatrix(node, dest);
4271         matrix.multiply(stack[stack.length - 1], dest, dest);
4272         matrix.multiply(inheritMat, dest, inheritMat);
4273         node._matrix = inheritMat;
4274         var ox = (typeof node._originX === 'number') ? node._originX : node.width / 2 || 0;
4275         var oy = (typeof node._originY === 'number') ? node._originY : node.height / 2 || 0;
4276         var vec = [ ox, oy ];
4277         matrix.multiplyVec(dest, vec, vec);
4278         node._offsetX = vec[0] - ox;
4279         node._offsetY = vec[1] - oy;
4280         stack.push(dest);
4281     },
4282     remove: function() {
4283         this._domRef = [];
4284         this.targetNode = null;
4285     }
4286 });
4287 
4288 enchant.DomLayer = enchant.Class.create(enchant.Group, {
4289     initialize: function() {
4290         var core = enchant.Core.instance;
4291         enchant.Group.call(this);
4292 
4293         this._touchEventTarget = null;
4294 
4295         this._element = document.createElement('div');
4296         this._element.style.position = 'absolute';
4297 
4298         this._domManager = new enchant.DomManager(this, this._element);
4299         this._domManager.layer = this;
4300 
4301         this.width = core.width;
4302         this.height = core.height;
4303 
4304         var touch = [
4305             enchant.Event.TOUCH_START,
4306             enchant.Event.TOUCH_MOVE,
4307             enchant.Event.TOUCH_END
4308         ];
4309 
4310         touch.forEach(function(type) {
4311             this.addEventListener(type, function(e) {
4312                 if (this._scene) {
4313                     this._scene.dispatchEvent(e);
4314                 }
4315             });
4316         }, this);
4317 
4318         var __onchildadded = function(e) {
4319             var child = e.node;
4320             var next = e.next;
4321             var self = e.target;
4322             var nextManager = next ? next._domManager : null;
4323             enchant.DomLayer._attachDomManager(child, __onchildadded, __onchildremoved);
4324             self._domManager.addManager(child._domManager, nextManager);
4325             var render = new enchant.Event(enchant.Event.RENDER);
4326             child._dirty = true;
4327             self._domManager.layer._rendering(child, render);
4328         };
4329 
4330         var __onchildremoved = function(e) {
4331             var child = e.node;
4332             var self = e.target;
4333             self._domManager.removeManager(child._domManager);
4334             enchant.DomLayer._detachDomManager(child, __onchildadded, __onchildremoved);
4335         };
4336 
4337         this.addEventListener('childremoved', __onchildremoved);
4338         this.addEventListener('childadded', __onchildadded);
4339 
4340     },
4341     width: {
4342         get: function() {
4343             return this._width;
4344         },
4345         set: function(width) {
4346             this._width = width;
4347             this._element.style.width = width + 'px';
4348         }
4349     },
4350     height: {
4351         get: function() {
4352             return this._height;
4353         },
4354         set: function(height) {
4355             this._height = height;
4356             this._element.style.height = height + 'px';
4357         }
4358     },
4359     addChild: function(node) {
4360         this.childNodes.push(node);
4361         node.parentNode = this;
4362         var childAdded = new enchant.Event('childadded');
4363         childAdded.node = node;
4364         childAdded.next = null;
4365         this.dispatchEvent(childAdded);
4366         node.dispatchEvent(new enchant.Event('added'));
4367         if (this.scene) {
4368             node.scene = this.scene;
4369             var addedToScene = new enchant.Event('addedtoscene');
4370             node.dispatchEvent(addedToScene);
4371         }
4372     },
4373     insertBefore: function(node, reference) {
4374         var i = this.childNodes.indexOf(reference);
4375         if (i !== -1) {
4376             this.childNodes.splice(i, 0, node);
4377             node.parentNode = this;
4378             var childAdded = new enchant.Event('childadded');
4379             childAdded.node = node;
4380             childAdded.next = reference;
4381             this.dispatchEvent(childAdded);
4382             node.dispatchEvent(new enchant.Event('added'));
4383             if (this.scene) {
4384                 node.scene = this.scene;
4385                 var addedToScene = new enchant.Event('addedtoscene');
4386                 node.dispatchEvent(addedToScene);
4387             }
4388         } else {
4389             this.addChild(node);
4390         }
4391     },
4392     _startRendering: function() {
4393         this.addEventListener('exitframe', this._onexitframe);
4394         this._onexitframe();
4395     },
4396     _stopRendering: function() {
4397         this.removeEventListener('exitframe', this._onexitframe);
4398         this._onexitframe();
4399     },
4400     _onexitframe: function() {
4401         this._rendering(this, new enchant.Event(enchant.Event.RENDER));
4402     },
4403     _rendering: function(node, e, inheritMat) {
4404         var child;
4405         if (!inheritMat) {
4406             inheritMat = [ 1, 0, 0, 1, 0, 0 ];
4407         }
4408         node.dispatchEvent(e);
4409         node._domManager.render(inheritMat);
4410         if (node.childNodes) {
4411             for (var i = 0, l = node.childNodes.length; i < l; i++) {
4412                 child = node.childNodes[i];
4413                 this._rendering(child, e, inheritMat.slice());
4414             }
4415         }
4416         if (node._domManager instanceof enchant.DomlessManager) {
4417             enchant.Matrix.instance.stack.pop();
4418         }
4419         node._dirty = false;
4420     },
4421     _determineEventTarget: function() {
4422         var target = this._touchEventTarget;
4423         this._touchEventTarget = null;
4424         return (target === this) ? null : target;
4425     }
4426 });
4427 
4428 enchant.DomLayer._attachDomManager = function(node, onchildadded, onchildremoved) {
4429     var child;
4430     if (!node._domManager) {
4431         node.addEventListener('childadded', onchildadded);
4432         node.addEventListener('childremoved', onchildremoved);
4433         if (node instanceof enchant.Group) {
4434             node._domManager = new enchant.DomlessManager(node);
4435         } else {
4436             if (node._element) {
4437                 node._domManager = new enchant.DomManager(node, node._element);
4438             } else {
4439                 node._domManager = new enchant.DomManager(node, 'div');
4440             }
4441         }
4442     }
4443     if (node.childNodes) {
4444         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4445             child = node.childNodes[i];
4446             enchant.DomLayer._attachDomManager(child, onchildadded, onchildremoved);
4447             node._domManager.addManager(child._domManager, null);
4448         }
4449     }
4450 };
4451 
4452 enchant.DomLayer._detachDomManager = function(node, onchildadded, onchildremoved) {
4453     var child;
4454     node.removeEventListener('childadded', onchildadded);
4455     node.removeEventListener('childremoved', onchildremoved);
4456     if (node.childNodes) {
4457         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4458             child = node.childNodes[i];
4459             node._domManager.removeManager(child._domManager, null);
4460             enchant.DomLayer._detachDomManager(child, onchildadded, onchildremoved);
4461         }
4462     }
4463     node._domManager.remove();
4464     delete node._domManager;
4465 };
4466 
4467 /**
4468  * @scope enchant.CanvasLayer.prototype
4469  */
4470 enchant.CanvasLayer = enchant.Class.create(enchant.Group, {
4471     /**
4472      * @name enchant.CanvasLayer
4473      * @class
4474      * Class that uses the HTML Canvas for rendering.
4475      * The rendering of children will be replaced by the Canvas rendering.
4476      * @constructs
4477      * @extends enchant.Group
4478      */
4479     initialize: function() {
4480         var core = enchant.Core.instance;
4481 
4482         enchant.Group.call(this);
4483 
4484         this._cvsCache = {
4485             matrix: [1, 0, 0, 1, 0, 0],
4486             detectColor: '#000000'
4487         };
4488         this._cvsCache.layer = this;
4489 
4490         this._element = document.createElement('canvas');
4491         this._element.style.position = 'absolute';
4492         // issue 179
4493         this._element.style.left = this._element.style.top = '0px';
4494 
4495         this._detect = document.createElement('canvas');
4496         this._detect.style.position = 'absolute';
4497         this._lastDetected = 0;
4498 
4499         this.context = this._element.getContext('2d');
4500         this._dctx = this._detect.getContext('2d');
4501         this._setImageSmoothingEnable();
4502 
4503         this._colorManager = new enchant.DetectColorManager(16, 256);
4504 
4505         this.width = core.width;
4506         this.height = core.height;
4507 
4508         var touch = [
4509             enchant.Event.TOUCH_START,
4510             enchant.Event.TOUCH_MOVE,
4511             enchant.Event.TOUCH_END
4512         ];
4513 
4514         touch.forEach(function(type) {
4515             this.addEventListener(type, function(e) {
4516                 if (this._scene) {
4517                     this._scene.dispatchEvent(e);
4518                 }
4519             });
4520         }, this);
4521 
4522         var __onchildadded = function(e) {
4523             var child = e.node;
4524             var self = e.target;
4525             var layer;
4526             if (self instanceof enchant.CanvasLayer) {
4527                 layer = self._scene._layers.Canvas;
4528             } else {
4529                 layer = self.scene._layers.Canvas;
4530             }
4531             enchant.CanvasLayer._attachCache(child, layer, __onchildadded, __onchildremoved);
4532             var render = new enchant.Event(enchant.Event.RENDER);
4533             if (self._dirty) {
4534                 self._updateCoordinate();
4535             }
4536             child._dirty = true;
4537             enchant.Matrix.instance.stack.push(self._matrix);
4538             enchant.CanvasRenderer.instance.render(layer.context, child, render);
4539             enchant.Matrix.instance.stack.pop(self._matrix);
4540         };
4541 
4542         var __onchildremoved = function(e) {
4543             var child = e.node;
4544             var self = e.target;
4545             var layer;
4546             if (self instanceof enchant.CanvasLayer) {
4547                 layer = self._scene._layers.Canvas;
4548             } else {
4549                 layer = self.scene._layers.Canvas;
4550             }
4551             enchant.CanvasLayer._detachCache(child, layer, __onchildadded, __onchildremoved);
4552         };
4553 
4554         this.addEventListener('childremoved', __onchildremoved);
4555         this.addEventListener('childadded', __onchildadded);
4556 
4557     },
4558     /**
4559      * The width of the CanvasLayer.
4560      * @type Number
4561      */
4562     width: {
4563         get: function() {
4564             return this._width;
4565         },
4566         set: function(width) {
4567             this._width = width;
4568             this._element.width = this._detect.width = width;
4569             this._setImageSmoothingEnable();
4570         }
4571     },
4572     /**
4573      * The height of the CanvasLayer.
4574      * @type Number
4575      */
4576     height: {
4577         get: function() {
4578             return this._height;
4579         },
4580         set: function(height) {
4581             this._height = height;
4582             this._element.height = this._detect.height = height;
4583             this._setImageSmoothingEnable();
4584         }
4585     },
4586     addChild: function(node) {
4587         this.childNodes.push(node);
4588         node.parentNode = this;
4589         var childAdded = new enchant.Event('childadded');
4590         childAdded.node = node;
4591         childAdded.next = null;
4592         this.dispatchEvent(childAdded);
4593         node.dispatchEvent(new enchant.Event('added'));
4594     },
4595     insertBefore: function(node, reference) {
4596         var i = this.childNodes.indexOf(reference);
4597         if (i !== -1) {
4598             this.childNodes.splice(i, 0, node);
4599             node.parentNode = this;
4600             var childAdded = new enchant.Event('childadded');
4601             childAdded.node = node;
4602             childAdded.next = reference;
4603             this.dispatchEvent(childAdded);
4604             node.dispatchEvent(new enchant.Event('added'));
4605         } else {
4606             this.addChild(node);
4607         }
4608     },
4609     /**
4610      * @private
4611      */
4612     _startRendering: function() {
4613         this.addEventListener('exitframe', this._onexitframe);
4614         this._onexitframe();
4615     },
4616     /**
4617      * @private
4618      */
4619     _stopRendering: function() {
4620         this.removeEventListener('exitframe', this._onexitframe);
4621         this._onexitframe();
4622     },
4623     _onexitframe: function() {
4624         var core = enchant.Core.instance;
4625         var ctx = this.context;
4626         ctx.clearRect(0, 0, core.width, core.height);
4627         var render = new enchant.Event(enchant.Event.RENDER);
4628         enchant.CanvasRenderer.instance.render(ctx, this, render);
4629     },
4630     _determineEventTarget: function(e) {
4631         return this._getEntityByPosition(e.x, e.y);
4632     },
4633     _getEntityByPosition: function(x, y) {
4634         var core = enchant.Core.instance;
4635         var ctx = this._dctx;
4636         if (this._lastDetected < core.frame) {
4637             ctx.clearRect(0, 0, this.width, this.height);
4638             enchant.CanvasRenderer.instance.detectRender(ctx, this);
4639             this._lastDetected = core.frame;
4640         }
4641         var extra = enchant.ENV.COLOR_DETECTION_LEVEL - 1;
4642         var rgba = ctx.getImageData(x - extra, y - extra, 1 + extra * 2, 1 + extra * 2).data;
4643         return this._colorManager.getSpriteByColors(rgba);
4644     },
4645     _setImageSmoothingEnable: function() {
4646         this._dctx.imageSmoothingEnabled =
4647                 this._dctx.msImageSmoothingEnabled =
4648                 this._dctx.mozImageSmoothingEnabled =
4649                 this._dctx.webkitImageSmoothingEnabled = false;
4650     }
4651 });
4652 
4653 enchant.CanvasLayer._attachCache = function(node, layer, onchildadded, onchildremoved) {
4654     var child;
4655     if (!node._cvsCache) {
4656         node._cvsCache = {};
4657         node._cvsCache.matrix = [ 1, 0, 0, 1, 0, 0 ];
4658         node._cvsCache.detectColor = 'rgba(' + layer._colorManager.attachDetectColor(node) + ')';
4659         node.addEventListener('childadded', onchildadded);
4660         node.addEventListener('childremoved', onchildremoved);
4661     }
4662     if (node.childNodes) {
4663         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4664             child = node.childNodes[i];
4665             enchant.CanvasLayer._attachCache(child, layer, onchildadded, onchildremoved);
4666         }
4667     }
4668 };
4669 
4670 enchant.CanvasLayer._detachCache = function(node, layer, onchildadded, onchildremoved) {
4671     var child;
4672     if (node._cvsCache) {
4673         layer._colorManager.detachDetectColor(node);
4674         node.removeEventListener('childadded', onchildadded);
4675         node.removeEventListener('childremoved', onchildremoved);
4676         delete node._cvsCache;
4677     }
4678     if (node.childNodes) {
4679         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4680             child = node.childNodes[i];
4681             enchant.CanvasLayer._detachCache(child, layer, onchildadded, onchildremoved);
4682         }
4683     }
4684 };
4685 
4686 enchant.CanvasRenderer = enchant.Class.create({
4687     render: function(ctx, node, e) {
4688         var width, height, child;
4689         ctx.save();
4690         node.dispatchEvent(e);
4691         // transform
4692         this.transform(ctx, node);
4693         if (typeof node._visible === 'undefined' || node._visible) {
4694             width = node.width;
4695             height = node.height;
4696             // composite
4697             if (node.compositeOperation) {
4698                 ctx.globalCompositeOperation = node.compositeOperation;
4699             }
4700             ctx.globalAlpha = (typeof node._opacity === 'number') ? node._opacity : 1.0;
4701             // render
4702             if (node._backgroundColor) {
4703                 ctx.fillStyle = node._backgroundColor;
4704                 ctx.fillRect(0, 0, width, height);
4705             }
4706 
4707             if (node.cvsRender) {
4708                 node.cvsRender(ctx);
4709             }
4710 
4711             if (enchant.Core.instance._debug && node._debugColor) {
4712                 ctx.strokeStyle = node._debugColor;
4713                 ctx.strokeRect(0, 0, width, height);
4714             }
4715             if (node._clipping) {
4716                 ctx.beginPath();
4717                 ctx.rect(0, 0, width, height);
4718                 ctx.clip();
4719             }
4720             if (node.childNodes) {
4721                 for (var i = 0, l = node.childNodes.length; i < l; i++) {
4722                     child = node.childNodes[i];
4723                     this.render(ctx, child, e);
4724                 }
4725             }
4726         }
4727         ctx.restore();
4728         enchant.Matrix.instance.stack.pop();
4729     },
4730     detectRender: function(ctx, node) {
4731         var width, height, child;
4732         if (typeof node._visible === 'undefined' || node._visible) {
4733             width = node.width;
4734             height = node.height;
4735             ctx.save();
4736             this.transform(ctx, node);
4737             ctx.fillStyle = node._cvsCache.detectColor;
4738             if (node._touchEnabled) {
4739                 if (node.detectRender) {
4740                     node.detectRender(ctx);
4741                 } else {
4742                     ctx.fillRect(0, 0, width, height);
4743                 }
4744             }
4745             if (node._clipping) {
4746                 ctx.beginPath();
4747                 ctx.rect(0, 0, width, height);
4748                 ctx.clip();
4749             }
4750             if (node.childNodes) {
4751                 for (var i = 0, l = node.childNodes.length; i < l; i++) {
4752                     child = node.childNodes[i];
4753                     this.detectRender(ctx, child);
4754                 }
4755             }
4756             ctx.restore();
4757             enchant.Matrix.instance.stack.pop();
4758         }
4759     },
4760     transform: function(ctx, node) {
4761         var matrix = enchant.Matrix.instance;
4762         var stack = matrix.stack;
4763         var newmat, ox, oy, vec;
4764         if (node._dirty) {
4765             matrix.makeTransformMatrix(node, node._cvsCache.matrix);
4766             newmat = [];
4767             matrix.multiply(stack[stack.length - 1], node._cvsCache.matrix, newmat);
4768             node._matrix = newmat;
4769             ox = (typeof node._originX === 'number') ? node._originX : node._width / 2 || 0;
4770             oy = (typeof node._originY === 'number') ? node._originY : node._height / 2 || 0;
4771             vec = [ ox, oy ];
4772             matrix.multiplyVec(newmat, vec, vec);
4773             node._offsetX = vec[0] - ox;
4774             node._offsetY = vec[1] - oy;
4775             node._dirty = false;
4776         } else {
4777             newmat = node._matrix;
4778         }
4779         stack.push(newmat);
4780         ctx.setTransform.apply(ctx, newmat);
4781     }
4782 });
4783 enchant.CanvasRenderer.instance = new enchant.CanvasRenderer();
4784 
4785 /**
4786  * @scope enchant.Scene.prototype
4787  */
4788 enchant.Scene = enchant.Class.create(enchant.Group, {
4789     /**
4790      * @name enchant.Scene
4791      * @class
4792      * Class that becomes the root of the display object tree.
4793      * Child {@link Entity} objects are distributed to the Scene layer according to the drawing method.
4794      * The DOM of each Scene layer has a ({@link enchant.DOMLayer} and  {@link enchant.CanvasLayer}) and is drawn using the Canvas.
4795      * Scenes are drawn in the order that they are added.
4796      *
4797      * @example
4798      * var scene = new Scene();
4799      * scene.addChild(player);
4800      * scene.addChild(enemy);
4801      * core.pushScene(scene);
4802      *
4803      * @constructs
4804      * @extends enchant.Group
4805      */
4806     initialize: function() {
4807         var core = enchant.Core.instance;
4808 
4809         // Call initialize method of enchant.Group
4810         enchant.Group.call(this);
4811 
4812         // All nodes (entities, groups, scenes) have reference to the scene that it belongs to.
4813         this.scene = this;
4814 
4815         this._backgroundColor = null;
4816 
4817         // Create div tag which possesses its layers
4818         this._element = document.createElement('div');
4819         this._element.style.position = 'absolute';
4820         this._element.style.overflow = 'hidden';
4821         this._element.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0';
4822 
4823         this._layers = {};
4824         this._layerPriority = [];
4825 
4826         this.addEventListener(enchant.Event.CHILD_ADDED, this._onchildadded);
4827         this.addEventListener(enchant.Event.CHILD_REMOVED, this._onchildremoved);
4828         this.addEventListener(enchant.Event.ENTER, this._onenter);
4829         this.addEventListener(enchant.Event.EXIT, this._onexit);
4830 
4831         var that = this;
4832         this._dispatchExitframe = function() {
4833             var layer;
4834             for (var prop in that._layers) {
4835                 layer = that._layers[prop];
4836                 layer.dispatchEvent(new enchant.Event(enchant.Event.EXIT_FRAME));
4837             }
4838         };
4839 
4840         this.addEventListener(enchant.Event.CORE_RESIZE, this._oncoreresize);
4841 
4842         this._oncoreresize(core);
4843     },
4844     /**#nocode+*/
4845     x: {
4846         get: function() {
4847             return this._x;
4848         },
4849         set: function(x) {
4850             this._x = x;
4851             for (var type in this._layers) {
4852                 this._layers[type].x = x;
4853             }
4854         }
4855     },
4856     y: {
4857         get: function() {
4858             return this._y;
4859         },
4860         set: function(y) {
4861             this._y = y;
4862             for (var type in this._layers) {
4863                 this._layers[type].y = y;
4864             }
4865         }
4866     },
4867     width: {
4868         get: function() {
4869             return this._width;
4870         },
4871         set: function(width) {
4872             this._width = width;
4873             for (var type in this._layers) {
4874                 this._layers[type].width = width;
4875             }
4876         }
4877     },
4878     height: {
4879         get: function() {
4880             return this._height;
4881         },
4882         set: function(height) {
4883             this._height = height;
4884             for (var type in this._layers) {
4885                 this._layers[type].height = height;
4886             }
4887         }
4888     },
4889     rotation: {
4890         get: function() {
4891             return this._rotation;
4892         },
4893         set: function(rotation) {
4894             this._rotation = rotation;
4895             for (var type in this._layers) {
4896                 this._layers[type].rotation = rotation;
4897             }
4898         }
4899     },
4900     scaleX: {
4901         get: function() {
4902             return this._scaleX;
4903         },
4904         set: function(scaleX) {
4905             this._scaleX = scaleX;
4906             for (var type in this._layers) {
4907                 this._layers[type].scaleX = scaleX;
4908             }
4909         }
4910     },
4911     scaleY: {
4912         get: function() {
4913             return this._scaleY;
4914         },
4915         set: function(scaleY) {
4916             this._scaleY = scaleY;
4917             for (var type in this._layers) {
4918                 this._layers[type].scaleY = scaleY;
4919             }
4920         }
4921     },
4922     backgroundColor: {
4923         get: function() {
4924             return this._backgroundColor;
4925         },
4926         set: function(color) {
4927             this._backgroundColor = this._element.style.backgroundColor = color;
4928         }
4929     },
4930     remove: function() {
4931         this.clearEventListener();
4932 
4933         while (this.childNodes.length > 0) {
4934             this.childNodes[0].remove();
4935         }
4936 
4937         return enchant.Core.instance.removeScene(this);
4938     },
4939     /**#nocode-*/
4940     _oncoreresize: function(e) {
4941         this._element.style.width = e.width + 'px';
4942         this.width = e.width;
4943         this._element.style.height = e.height + 'px';
4944         this.height = e.height;
4945         this._element.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'scale(' + e.scale + ')';
4946 
4947         for (var type in this._layers) {
4948             this._layers[type].dispatchEvent(e);
4949         }
4950     },
4951     addLayer: function(type, i) {
4952         var core = enchant.Core.instance;
4953         if (this._layers[type]) {
4954             return;
4955         }
4956         var layer = new enchant[type + 'Layer']();
4957         if (core.currentScene === this) {
4958             layer._startRendering();
4959         }
4960         this._layers[type] = layer;
4961         var element = layer._element;
4962         if (typeof i === 'number') {
4963             var nextSibling = this._element.childNodes[i];
4964             if (nextSibling) {
4965                 this._element.insertBefore(element, nextSibling);
4966             } else {
4967                 this._element.appendChild(element);
4968             }
4969             this._layerPriority.splice(i, 0, type);
4970         } else {
4971             this._element.appendChild(element);
4972             this._layerPriority.push(type);
4973         }
4974         layer._scene = this;
4975     },
4976     _determineEventTarget: function(e) {
4977         var layer, target;
4978         for (var i = this._layerPriority.length - 1; i >= 0; i--) {
4979             layer = this._layers[this._layerPriority[i]];
4980             target = layer._determineEventTarget(e);
4981             if (target) {
4982                 break;
4983             }
4984         }
4985         if (!target) {
4986             target = this;
4987         }
4988         return target;
4989     },
4990     _onchildadded: function(e) {
4991         var child = e.node;
4992         var next = e.next;
4993         var target, i;
4994         if (child._element) {
4995             target = 'Dom';
4996             i = 1;
4997         } else {
4998             target = 'Canvas';
4999             i = 0;
5000         }
5001         if (!this._layers[target]) {
5002             this.addLayer(target, i);
5003         }
5004         child._layer = this._layers[target];
5005         this._layers[target].insertBefore(child, next);
5006         child.parentNode = this;
5007     },
5008     _onchildremoved: function(e) {
5009         var child = e.node;
5010         child._layer.removeChild(child);
5011         child._layer = null;
5012     },
5013     _onenter: function() {
5014         for (var type in this._layers) {
5015             this._layers[type]._startRendering();
5016         }
5017         enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe);
5018     },
5019     _onexit: function() {
5020         for (var type in this._layers) {
5021             this._layers[type]._stopRendering();
5022         }
5023         enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe);
5024     }
5025 });
5026 
5027 /**
5028  * @scope enchant.LoadingScene.prototype
5029  */
5030 enchant.LoadingScene = enchant.Class.create(enchant.Scene, {
5031     /**
5032      * @name enchant.LoadingScene
5033      * @class
5034      * Default loading scene. If you want to use your own loading animation, overwrite (don't inherit) this class.
5035      * Referred from enchant.Core in default, as `new enchant.LoadingScene` etc.
5036      *
5037      * @example
5038      * enchant.LoadingScene = enchant.Class.create(enchant.Scene, {
5039      *     initialize: function() {
5040      *         enchant.Scene.call(this);
5041      *         this.backgroundColor = 'red';
5042      *         // ...
5043      *         this.addEventListener('progress', function(e) {
5044      *             progress = e.loaded / e.total;
5045      *         });
5046      *         this.addEventListener('enterframe', function() {
5047      *             // animation
5048      *         });
5049      *     }
5050      * });
5051      * @constructs
5052      * @extends enchant.Scene
5053      */
5054     initialize: function() {
5055         enchant.Scene.call(this);
5056         this.backgroundColor = '#000';
5057         var barWidth = this.width * 0.4 | 0;
5058         var barHeight = this.width * 0.05 | 0;
5059         var border = barWidth * 0.03 | 0;
5060         var bar = new enchant.Sprite(barWidth, barHeight);
5061         bar.disableCollection();
5062         bar.x = (this.width - barWidth) / 2;
5063         bar.y = (this.height - barHeight) / 2;
5064         var image = new enchant.Surface(barWidth, barHeight);
5065         image.context.fillStyle = '#fff';
5066         image.context.fillRect(0, 0, barWidth, barHeight);
5067         image.context.fillStyle = '#000';
5068         image.context.fillRect(border, border, barWidth - border * 2, barHeight - border * 2);
5069         bar.image = image;
5070         var progress = 0, _progress = 0;
5071         this.addEventListener('progress', function(e) {
5072             // avoid #167 https://github.com/wise9/enchant.js/issues/177
5073             progress = e.loaded / e.total * 1.0;
5074         });
5075         bar.addEventListener('enterframe', function() {
5076             _progress *= 0.9;
5077             _progress += progress * 0.1;
5078             image.context.fillStyle = '#fff';
5079             image.context.fillRect(border, 0, (barWidth - border * 2) * _progress, barHeight);
5080         });
5081         this.addChild(bar);
5082         this.addEventListener('load', function(e) {
5083             var core = enchant.Core.instance;
5084             core.removeScene(core.loadingScene);
5085             core.dispatchEvent(e);
5086         });
5087     }
5088 });
5089 
5090 /**
5091  * @scope enchant.CanvasScene.prototype
5092  */
5093 enchant.CanvasScene = enchant.Class.create(enchant.Scene, {
5094     /**
5095      * @name enchant.CanvasScene
5096      * @class
5097      * Scene to draw by the Canvas all of the children.
5098      * @constructs
5099      * @extends enchant.Scene
5100      */
5101     initialize: function() {
5102         enchant.Scene.call(this);
5103         this.addLayer('Canvas');
5104     },
5105     _determineEventTarget: function(e) {
5106         var target = this._layers.Canvas._determineEventTarget(e);
5107         if (!target) {
5108             target = this;
5109         }
5110         return target;
5111     },
5112     _onchildadded: function(e) {
5113         var child = e.node;
5114         var next = e.next;
5115         child._layer = this._layers.Canvas;
5116         this._layers.Canvas.insertBefore(child, next);
5117     },
5118     _onenter: function() {
5119         this._layers.Canvas._startRendering();
5120         enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe);
5121     },
5122     _onexit: function() {
5123         this._layers.Canvas._stopRendering();
5124         enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe);
5125     }
5126 });
5127 
5128 /**
5129  * @scope enchant.DOMScene.prototype
5130  */
5131 enchant.DOMScene = enchant.Class.create(enchant.Scene, {
5132     /**
5133      * @name enchant.DOMScene
5134      * @class
5135      * Scene to draw by the DOM all of the children.
5136      * @constructs
5137      * @extends enchant.Scene
5138      */
5139     initialize: function() {
5140         enchant.Scene.call(this);
5141         this.addLayer('Dom');
5142     },
5143     _determineEventTarget: function(e) {
5144         var target = this._layers.Dom._determineEventTarget(e);
5145         if (!target) {
5146             target = this;
5147         }
5148         return target;
5149     },
5150     _onchildadded: function(e) {
5151         var child = e.node;
5152         var next = e.next;
5153         child._layer = this._layers.Dom;
5154         this._layers.Dom.insertBefore(child, next);
5155     },
5156     _onenter: function() {
5157         this._layers.Dom._startRendering();
5158         enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe);
5159     },
5160     _onexit: function() {
5161         this._layers.Dom._stopRendering();
5162         enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe);
5163     }
5164 });
5165 
5166 /**
5167  * @scope enchant.Surface.prototype
5168  */
5169 enchant.Surface = enchant.Class.create(enchant.EventTarget, {
5170     /**
5171      * @name enchant.Surface
5172      * @class
5173      * Class that wraps canvas elements.
5174      *
5175      * Can be used to set the {@link enchant.Sprite} and {@link enchant.Map}'s image properties to be displayed.
5176      * If you wish to access Canvas API use the {@link enchant.Surface#context} property.
5177      *
5178      * @example
5179      * // Creates Sprite that displays a circle.
5180      * var ball = new Sprite(50, 50);
5181      * var surface = new Surface(50, 50);
5182      * surface.context.beginPath();
5183      * surface.context.arc(25, 25, 25, 0, Math.PI*2, true);
5184      * surface.context.fill();
5185      * ball.image = surface;
5186      *
5187      * @param {Number} width Surface width.
5188      * @param {Number} height Surface height.
5189      * @constructs
5190      * @extends enchant.EventTarget
5191      */
5192     initialize: function(width, height) {
5193         enchant.EventTarget.call(this);
5194 
5195         var core = enchant.Core.instance;
5196 
5197         /**
5198          * Surface width.
5199          * @type Number
5200          */
5201         this.width = Math.ceil(width);
5202         /**
5203          * Surface height.
5204          * @type Number
5205          */
5206         this.height = Math.ceil(height);
5207         /**
5208          * Surface drawing context.
5209          * @type CanvasRenderingContext2D
5210          */
5211         this.context = null;
5212 
5213         var id = 'enchant-surface' + core._surfaceID++;
5214         if (document.getCSSCanvasContext) {
5215             this.context = document.getCSSCanvasContext('2d', id, width, height);
5216             this._element = this.context.canvas;
5217             this._css = '-webkit-canvas(' + id + ')';
5218             var context = this.context;
5219         } else if (document.mozSetImageElement) {
5220             this._element = document.createElement('canvas');
5221             this._element.width = width;
5222             this._element.height = height;
5223             this._css = '-moz-element(#' + id + ')';
5224             this.context = this._element.getContext('2d');
5225             document.mozSetImageElement(id, this._element);
5226         } else {
5227             this._element = document.createElement('canvas');
5228             this._element.width = width;
5229             this._element.height = height;
5230             this._element.style.position = 'absolute';
5231             this.context = this._element.getContext('2d');
5232 
5233             enchant.ENV.CANVAS_DRAWING_METHODS.forEach(function(name) {
5234                 var method = this.context[name];
5235                 this.context[name] = function() {
5236                     method.apply(this, arguments);
5237                     this._dirty = true;
5238                 };
5239             }, this);
5240         }
5241     },
5242     /**
5243      * Returns 1 pixel from the Surface.
5244      * @param {Number} x The pixel's x coordinates.
5245      * @param {Number} y The pixel's y coordinates.
5246      * @return {Number[]} An array that holds pixel information in [r, g, b, a] format.
5247      */
5248     getPixel: function(x, y) {
5249         return this.context.getImageData(x, y, 1, 1).data;
5250     },
5251     /**
5252      * Sets one pixel within the surface.
5253      * @param {Number} x The pixel's x coordinates.
5254      * @param {Number} y The pixel's y coordinates.
5255      * @param {Number} r The pixel's red level.
5256      * @param {Number} g The pixel's green level.
5257      * @param {Number} b The pixel's blue level.
5258      * @param {Number} a The pixel's transparency.
5259      */
5260     setPixel: function(x, y, r, g, b, a) {
5261         var pixel = this.context.createImageData(1, 1);
5262         pixel.data[0] = r;
5263         pixel.data[1] = g;
5264         pixel.data[2] = b;
5265         pixel.data[3] = a;
5266         this.context.putImageData(pixel, x, y);
5267     },
5268     /**
5269      * Clears all Surface pixels and makes the pixels transparent.
5270      */
5271     clear: function() {
5272         this.context.clearRect(0, 0, this.width, this.height);
5273     },
5274     /**
5275      * Draws the content of the given Surface onto this surface.
5276      *
5277      * Wraps Canvas API drawImage and if multiple arguments are given,
5278      * these are getting applied to the Canvas drawImage method.
5279      *
5280      * @example
5281      * var src = core.assets['src.gif'];
5282      * var dst = new Surface(100, 100);
5283      * dst.draw(src);         // Draws source at (0, 0)
5284      * dst.draw(src, 50, 50); // Draws source at (50, 50)
5285      * // Draws just 30 horizontal and vertical pixels of source at (50, 50)
5286      * dst.draw(src, 50, 50, 30, 30);
5287      * // Takes the image content in src starting at (10,10) with a (Width, Height) of (40,40),
5288      * // scales it and draws it in this surface at (50, 50) with a (Width, Height) of (30,30).
5289      * dst.draw(src, 10, 10, 40, 40, 50, 50, 30, 30);
5290      *
5291      * @param {enchant.Surface} image Surface used in drawing.
5292      */
5293     draw: function(image) {
5294         image = image._element;
5295         if (arguments.length === 1) {
5296             this.context.drawImage(image, 0, 0);
5297         } else {
5298             var args = arguments;
5299             args[0] = image;
5300             this.context.drawImage.apply(this.context, args);
5301         }
5302     },
5303     /**
5304      * Copies Surface.
5305      * @return {enchant.Surface} The copied Surface.
5306      */
5307     clone: function() {
5308         var clone = new enchant.Surface(this.width, this.height);
5309         clone.draw(this);
5310         return clone;
5311     },
5312     /**
5313      * Creates a data URI scheme from this Surface.
5314      * @return {String} The data URI scheme that identifies this Surface and
5315      * can be used to include this Surface into a dom tree.
5316      */
5317     toDataURL: function() {
5318         var src = this._element.src;
5319         if (src) {
5320             if (src.slice(0, 5) === 'data:') {
5321                 return src;
5322             } else {
5323                 return this.clone().toDataURL();
5324             }
5325         } else {
5326             return this._element.toDataURL();
5327         }
5328     }
5329 });
5330 
5331 /**
5332  * Loads an image and creates a Surface object out of it.
5333  *
5334  * It is not possible to access properties or methods of the {@link enchant.Surface#context}, or to call methods using the Canvas API -
5335  * like {@link enchant.Surface#draw}, {@link enchant.Surface#clear}, {@link enchant.Surface#getPixel}, {@link enchant.Surface#setPixel}.. -
5336  * of the wrapped image created with this method.
5337  * However, it is possible to use this surface to draw it to another surface using the {@link enchant.Surface#draw} method.
5338  * The resulting surface can then be manipulated. (when loading images in a cross-origin resource sharing environment,
5339  * pixel acquisition and other image manipulation might be limited).
5340  *
5341  * @param {String} src The file path of the image to be loaded.
5342  * @param {Function} callback on load callback.
5343  * @param {Function} [onerror] on error callback.
5344  * @static
5345  * @return {enchant.Surface} Surface
5346  */
5347 enchant.Surface.load = function(src, callback, onerror) {
5348     var image = new Image();
5349     var surface = Object.create(enchant.Surface.prototype, {
5350         context: { value: null },
5351         _css: { value: 'url(' + src + ')' },
5352         _element: { value: image }
5353     });
5354     enchant.EventTarget.call(surface);
5355     onerror = onerror || function() {};
5356     surface.addEventListener('load', callback);
5357     surface.addEventListener('error', onerror);
5358     image.onerror = function() {
5359         var e = new enchant.Event(enchant.Event.ERROR);
5360         e.message = 'Cannot load an asset: ' + image.src;
5361         enchant.Core.instance.dispatchEvent(e);
5362         surface.dispatchEvent(e);
5363     };
5364     image.onload = function() {
5365         surface.width = image.width;
5366         surface.height = image.height;
5367         surface.dispatchEvent(new enchant.Event('load'));
5368     };
5369     image.src = src;
5370     return surface;
5371 };
5372 enchant.Surface._staticCanvas2DContext = document.createElement('canvas').getContext('2d');
5373 
5374 enchant.Surface._getPattern = function(surface, force) {
5375     if (!surface._pattern || force) {
5376         surface._pattern = this._staticCanvas2DContext.createPattern(surface._element, 'repeat');
5377     }
5378     return surface._pattern;
5379 };
5380 
5381 if (window.Deferred) {
5382     enchant.Deferred = window.Deferred;
5383 } else {
5384     /**
5385      * @scope enchant.Deferred.prototype
5386      */
5387     enchant.Deferred = enchant.Class.create({
5388         /**
5389          * @name enchant.Deferred
5390          * @class
5391          * <br/>
5392          * See: <a href="http://cho45.stfuawsc.com/jsdeferred/">
5393          * http://cho45.stfuawsc.com/jsdeferred/</a>
5394          *
5395          * @example
5396          * enchant.Deferred
5397          *     .next(function() {
5398          *         return 42;
5399          *     })
5400          *     .next(function(n) {
5401          *         console.log(n); // 42
5402          *     })
5403          *     .next(function() {
5404          *         return core.load('img.png'); // wait loading
5405          *     })
5406          *     .next(function() {
5407          *         var img = core.assets['img.png'];
5408          *         console.log(img instanceof enchant.Surface); // true
5409          *         throw new Error('!!!');
5410          *     })
5411          *     .next(function() {
5412          *         // skip
5413          *     })
5414          *     .error(function(err) {
5415          *          console.log(err.message); // !!!
5416          *     });
5417          *
5418          * @constructs
5419          */
5420         initialize: function() {
5421             this._succ = this._fail = this._next = this._id = null;
5422             this._tail = this;
5423         },
5424         /**
5425          * @param {Function} func
5426          */
5427         next: function(func) {
5428             var q = new enchant.Deferred();
5429             q._succ = func;
5430             return this._add(q);
5431         },
5432         /**
5433          * @param {Function} func
5434          */
5435         error: function(func) {
5436             var q = new enchant.Deferred();
5437             q._fail = func;
5438             return this._add(q);
5439         },
5440         _add: function(queue) {
5441             this._tail._next = queue;
5442             this._tail = queue;
5443             return this;
5444         },
5445         /**
5446          * @param {*} arg
5447          */
5448         call: function(arg) {
5449             var received;
5450             var queue = this;
5451             while (queue && !queue._succ) {
5452                 queue = queue._next;
5453             }
5454             if (!(queue instanceof enchant.Deferred)) {
5455                 return;
5456             }
5457             try {
5458                 received = queue._succ(arg);
5459             } catch (e) {
5460                 return queue.fail(e);
5461             }
5462             if (received instanceof enchant.Deferred) {
5463                 enchant.Deferred._insert(queue, received);
5464             } else if (queue._next instanceof enchant.Deferred) {
5465                 queue._next.call(received);
5466             }
5467         },
5468         /**
5469          * @param {*} arg
5470          */
5471         fail: function(arg) {
5472             var result, err,
5473                 queue = this;
5474             while (queue && !queue._fail) {
5475                 queue = queue._next;
5476             }
5477             if (queue instanceof enchant.Deferred) {
5478                 result = queue._fail(arg);
5479                 queue.call(result);
5480             } else if (arg instanceof Error) {
5481                 throw arg;
5482             } else {
5483                 err = new Error('failed in Deferred');
5484                 err.arg = arg;
5485                 throw err;
5486             }
5487         }
5488     });
5489     enchant.Deferred._insert = function(queue, ins) {
5490         if (queue._next instanceof enchant.Deferred) {
5491             ins._tail._next = queue._next;
5492         }
5493         queue._next = ins;
5494     };
5495     /**
5496      * @param {Function} func
5497      * @return {enchant.Deferred}
5498      * @static
5499      */
5500     enchant.Deferred.next = function(func) {
5501         var q = new enchant.Deferred().next(func);
5502         q._id = setTimeout(function() { q.call(); }, 0);
5503         return q;
5504     };
5505     /**
5506      * @param {Object|enchant.Deferred[]} arg
5507      * @return {enchant.Deferred}
5508      *
5509      * @example
5510      * // array
5511      * enchant.Deferred
5512      *     .parallel([
5513      *         enchant.Deferred.next(function() {
5514      *             return 24;
5515      *         }),
5516      *         enchant.Deferred.next(function() {
5517      *             return 42;
5518      *         })
5519      *     ])
5520      *     .next(function(arg) {
5521      *         console.log(arg); // [ 24, 42 ]
5522      *     });
5523      * // object
5524      * enchant.Deferred
5525      *     .parallel({
5526      *         foo: enchant.Deferred.next(function() {
5527      *             return 24;
5528      *         }),
5529      *         bar: enchant.Deferred.next(function() {
5530      *             return 42;
5531      *         })
5532      *     })
5533      *     .next(function(arg) {
5534      *         console.log(arg.foo); // 24
5535      *         console.log(arg.bar); // 42
5536      *     });
5537      *
5538      * @static
5539      */
5540     enchant.Deferred.parallel = function(arg) {
5541         var q = new enchant.Deferred();
5542         q._id = setTimeout(function() { q.call(); }, 0);
5543         var progress = 0;
5544         var ret = (arg instanceof Array) ? [] : {};
5545         var p = new enchant.Deferred();
5546         for (var prop in arg) {
5547             if (arg.hasOwnProperty(prop)) {
5548                 progress++;
5549                 /*jshint loopfunc:true */
5550                 (function(queue, name) {
5551                     queue.next(function(arg) {
5552                         progress--;
5553                         ret[name] = arg;
5554                         if (progress <= 0) {
5555                             p.call(ret);
5556                         }
5557                     })
5558                     .error(function(err) { p.fail(err); });
5559                     if (typeof queue._id === 'number') {
5560                         clearTimeout(queue._id);
5561                     }
5562                     queue._id = setTimeout(function() { queue.call(); }, 0);
5563                 }(arg[prop], prop));
5564             }
5565         }
5566         if (!progress) {
5567             p._id = setTimeout(function() { p.call(ret); }, 0);
5568         }
5569         return q.next(function() { return p; });
5570     };
5571 }
5572 
5573 /**
5574  * @scope enchant.DOMSound.prototype
5575  */
5576 enchant.DOMSound = enchant.Class.create(enchant.EventTarget, {
5577     /**
5578      * @name enchant.DOMSound
5579      * @class
5580      * Class to wrap audio elements.
5581      *
5582      * Safari, Chrome, Firefox, Opera, and IE all play MP3 files
5583      * (Firefox and Opera play via Flash). WAVE files can be played on
5584      * Safari, Chrome, Firefox, and Opera. When the browser is not compatible with
5585      * the used codec the file will not play.
5586      *
5587      * Instances are created not via constructor but via {@link enchant.DOMSound.load}.
5588      * @constructs
5589      * @extends enchant.EventTarget
5590      */
5591     initialize: function() {
5592         enchant.EventTarget.call(this);
5593         /**
5594          * Sound file duration (seconds).
5595          * @type Number
5596          */
5597         this.duration = 0;
5598         throw new Error("Illegal Constructor");
5599     },
5600     /**
5601      * Begin playing.
5602      */
5603     play: function() {
5604         if (this._element) {
5605             this._element.play();
5606         }
5607     },
5608     /**
5609      * Pause playback.
5610      */
5611     pause: function() {
5612         if (this._element) {
5613             this._element.pause();
5614         }
5615     },
5616     /**
5617      * Stop playing.
5618      */
5619     stop: function() {
5620         this.pause();
5621         this.currentTime = 0;
5622     },
5623     /**
5624      * Create a copy of this Sound object.
5625      * @return {enchant.DOMSound} Copied Sound.
5626      */
5627     clone: function() {
5628         var clone;
5629         if (this._element instanceof Audio) {
5630             clone = Object.create(enchant.DOMSound.prototype, {
5631                 _element: { value: this._element.cloneNode(false) },
5632                 duration: { value: this.duration }
5633             });
5634         } else if (enchant.ENV.USE_FLASH_SOUND) {
5635             return this;
5636         } else {
5637             clone = Object.create(enchant.DOMSound.prototype);
5638         }
5639         enchant.EventTarget.call(clone);
5640         return clone;
5641     },
5642     /**
5643      * Current playback position (seconds).
5644      * @type Number
5645      */
5646     currentTime: {
5647         get: function() {
5648             return this._element ? this._element.currentTime : 0;
5649         },
5650         set: function(time) {
5651             if (this._element) {
5652                 this._element.currentTime = time;
5653             }
5654         }
5655     },
5656     /**
5657      * Volume. 0 (muted) ~ 1 (full volume).
5658      * @type Number
5659      */
5660     volume: {
5661         get: function() {
5662             return this._element ? this._element.volume : 1;
5663         },
5664         set: function(volume) {
5665             if (this._element) {
5666                 this._element.volume = volume;
5667             }
5668         }
5669     }
5670 });
5671 
5672 /**
5673  * Loads an audio file and creates DOMSound object.
5674  * @param {String} src Path of the audio file to be loaded.
5675  * @param {String} [type] MIME Type of the audio file.
5676  * @param {Function} [callback] on load callback.
5677  * @param {Function} [onerror] on error callback.
5678  * @return {enchant.DOMSound} DOMSound
5679  * @static
5680  */
5681 enchant.DOMSound.load = function(src, type, callback, onerror) {
5682     if (type == null) {
5683         var ext = enchant.Core.findExt(src);
5684         if (ext) {
5685             type = 'audio/' + ext;
5686         } else {
5687             type = '';
5688         }
5689     }
5690     type = type.replace('mp3', 'mpeg').replace('m4a', 'mp4');
5691     callback = callback || function() {};
5692     onerror = onerror || function() {};
5693 
5694     var sound = Object.create(enchant.DOMSound.prototype);
5695     enchant.EventTarget.call(sound);
5696     sound.addEventListener('load', callback);
5697     sound.addEventListener('error', onerror);
5698     var audio = new Audio();
5699     if (!enchant.ENV.SOUND_ENABLED_ON_MOBILE_SAFARI &&
5700         enchant.ENV.VENDOR_PREFIX === 'webkit' && enchant.ENV.TOUCH_ENABLED) {
5701         window.setTimeout(function() {
5702             sound.dispatchEvent(new enchant.Event('load'));
5703         }, 0);
5704     } else {
5705         if (!enchant.ENV.USE_FLASH_SOUND && audio.canPlayType(type)) {
5706             audio.addEventListener('canplaythrough', function canplay() {
5707                 sound.duration = audio.duration;
5708                 sound.dispatchEvent(new enchant.Event('load'));
5709                 audio.removeEventListener('canplaythrough', canplay);
5710             }, false);
5711             audio.src = src;
5712             audio.load();
5713             audio.autoplay = false;
5714             audio.onerror = function() {
5715                 var e = new enchant.Event(enchant.Event.ERROR);
5716                 e.message = 'Cannot load an asset: ' + audio.src;
5717                 enchant.Core.instance.dispatchEvent(e);
5718                 sound.dispatchEvent(e);
5719             };
5720             sound._element = audio;
5721         } else if (type === 'audio/mpeg') {
5722             var embed = document.createElement('embed');
5723             var id = 'enchant-audio' + enchant.Core.instance._soundID++;
5724             embed.width = embed.height = 1;
5725             embed.name = id;
5726             embed.src = 'sound.swf?id=' + id + '&src=' + src;
5727             embed.allowscriptaccess = 'always';
5728             embed.style.position = 'absolute';
5729             embed.style.left = '-1px';
5730             sound.addEventListener('load', function() {
5731                 Object.defineProperties(embed, {
5732                     currentTime: {
5733                         get: function() {
5734                             return embed.getCurrentTime();
5735                         },
5736                         set: function(time) {
5737                             embed.setCurrentTime(time);
5738                         }
5739                     },
5740                     volume: {
5741                         get: function() {
5742                             return embed.getVolume();
5743                         },
5744                         set: function(volume) {
5745                             embed.setVolume(volume);
5746                         }
5747                     }
5748                 });
5749                 sound._element = embed;
5750                 sound.duration = embed.getDuration();
5751             });
5752             enchant.Core.instance._element.appendChild(embed);
5753             enchant.DOMSound[id] = sound;
5754         } else {
5755             window.setTimeout(function() {
5756                 sound.dispatchEvent(new enchant.Event('load'));
5757             }, 0);
5758         }
5759     }
5760     return sound;
5761 };
5762 
5763 window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext;
5764 
5765 /**
5766  * @scope enchant.WebAudioSound.prototype
5767  */
5768 enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, {
5769     /**
5770      * @name enchant.WebAudioSound
5771      * @class
5772      * Sound wrapper class for Web Audio API (supported on some webkit-based browsers)
5773      * @constructs
5774      * @extends enchant.EventTarget
5775      */
5776     initialize: function() {
5777         if (!window.AudioContext) {
5778             throw new Error("This browser does not support WebAudio API.");
5779         }
5780         enchant.EventTarget.call(this);
5781         if (!enchant.WebAudioSound.audioContext) {
5782           enchant.WebAudioSound.audioContext = new window.AudioContext();
5783           enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination;
5784         }
5785         this.context = enchant.WebAudioSound.audioContext;
5786         this.src = this.context.createBufferSource();
5787         this.buffer = null;
5788         this._volume = 1;
5789         this._currentTime = 0;
5790         this._state = 0;
5791         this.connectTarget = enchant.WebAudioSound.destination;
5792     },
5793     /**
5794      * Begin playing.
5795      * @param {Boolean} [dup=false] If true, Object plays new sound while keeps last sound.
5796      */
5797     play: function(dup) {
5798         if (this._state === 1 && !dup) {
5799             this.src.disconnect();
5800         }
5801         if (this._state !== 2) {
5802             this._currentTime = 0;
5803         }
5804         var offset = this._currentTime;
5805         var actx = this.context;
5806         this.src = actx.createBufferSource();
5807         if (actx.createGain != null) {
5808             this._gain = actx.createGain();
5809         } else {
5810             this._gain = actx.createGainNode();
5811         }
5812         this.src.buffer = this.buffer;
5813         this._gain.gain.value = this._volume;
5814 
5815         this.src.connect(this._gain);
5816         this._gain.connect(this.connectTarget);
5817         if (this.src.start != null) {
5818             this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7);
5819         } else {
5820             this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7);
5821         }
5822         this._startTime = actx.currentTime - this._currentTime;
5823         this._state = 1;
5824     },
5825     /**
5826      * Pause playback.
5827      */
5828     pause: function() {
5829         var currentTime = this.currentTime;
5830         if (currentTime === this.duration) {
5831             return;
5832         }
5833         if (this.src.stop != null) {
5834             this.src.stop(0);
5835         } else {
5836             this.src.noteOff(0);
5837         }
5838         this._currentTime = currentTime;
5839         this._state = 2;
5840     },
5841     /**
5842      * Stop playing.
5843      */
5844     stop: function() {
5845         if (this.src.stop != null) {
5846             this.src.stop(0);
5847         } else {
5848             this.src.noteOff(0);
5849         }
5850         this._state = 0;
5851     },
5852     /**
5853      * Create a copy of this Sound object.
5854      * @return {enchant.WebAudioSound} Copied Sound.
5855      */
5856     clone: function() {
5857         var sound = new enchant.WebAudioSound();
5858         sound.buffer = this.buffer;
5859         return sound;
5860     },
5861     /**
5862      * Sound file duration (seconds).
5863      * @type Number
5864      */
5865     duration: {
5866         get: function() {
5867             if (this.buffer) {
5868                 return this.buffer.duration;
5869             } else {
5870                 return 0;
5871             }
5872         }
5873     },
5874     /**
5875      * Volume. 0 (muted) ~ 1 (full volume).
5876      * @type Number
5877      */
5878     volume: {
5879         get: function() {
5880             return this._volume;
5881         },
5882         set: function(volume) {
5883             volume = Math.max(0, Math.min(1, volume));
5884             this._volume = volume;
5885             if (this.src) {
5886                 this._gain.gain.value = volume;
5887             }
5888         }
5889     },
5890     /**
5891      * Current playback position (seconds).
5892      * @type Number
5893      */
5894     currentTime: {
5895         get: function() {
5896             return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime));
5897         },
5898         set: function(time) {
5899             this._currentTime = time;
5900             if (this._state !== 2) {
5901                 this.play(false);
5902             }
5903         }
5904     }
5905 });
5906 
5907 /**
5908  * Loads an audio file and creates WebAudioSound object.
5909  * @param {String} src Path of the audio file to be loaded.
5910  * @param {String} [type] MIME Type of the audio file.
5911  * @param {Function} [callback] on load callback.
5912  * @param {Function} [onerror] on error callback.
5913  * @return {enchant.WebAudioSound} WebAudioSound
5914  * @static
5915  */
5916 enchant.WebAudioSound.load = function(src, type, callback, onerror) {
5917     var canPlay = (new Audio()).canPlayType(type);
5918     var sound = new enchant.WebAudioSound();
5919     callback = callback || function() {};
5920     onerror = onerror || function() {};
5921     sound.addEventListener(enchant.Event.LOAD, callback);
5922     sound.addEventListener(enchant.Event.ERROR, onerror);
5923     function dispatchErrorEvent() {
5924         var e = new enchant.Event(enchant.Event.ERROR);
5925         e.message = 'Cannot load an asset: ' + src;
5926         enchant.Core.instance.dispatchEvent(e);
5927         sound.dispatchEvent(e);
5928     }
5929     var actx, xhr;
5930     if (canPlay === 'maybe' || canPlay === 'probably') {
5931         actx = enchant.WebAudioSound.audioContext;
5932         xhr = new XMLHttpRequest();
5933         xhr.open('GET', src, true);
5934         xhr.responseType = 'arraybuffer';
5935         xhr.onload = function() {
5936             actx.decodeAudioData(xhr.response, function(buffer) {
5937                 sound.buffer = buffer;
5938                 sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
5939             }, dispatchErrorEvent);
5940         };
5941         xhr.onerror = dispatchErrorEvent;
5942         xhr.send(null);
5943     } else {
5944         setTimeout(dispatchErrorEvent,  50);
5945     }
5946     return sound;
5947 };
5948 
5949 enchant.Sound = window.AudioContext && enchant.ENV.USE_WEBAUDIO ? enchant.WebAudioSound : enchant.DOMSound;
5950 
5951 /*
5952  * ============================================================================================
5953  * Easing Equations v2.0
5954  * September 1, 2003
5955  * (c) 2003 Robert Penner, all rights reserved.
5956  * This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html.
5957  * ============================================================================================
5958  */
5959 
5960 /**
5961  * @namespace
5962  * JavaScript translation of Robert Penner's "Easing Equations" library which is widely used in ActionScript.
5963  * 
5964  * @param [t] the current time
5965  * @param [b] the property's initial value
5966  * @param [c] how much the value should change
5967  * @param [d] how much time should elapse before value is changed
5968  * 
5969  * @return {Number}
5970  * <br/>
5971  * See: <a href="http://www.robertpenner.com/easing/">
5972  * http://www.robertpenner.com/easing/</a>
5973  * <br/>
5974  * See: <a href="http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf">
5975  * http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf</a>
5976  */
5977 enchant.Easing = {
5978     LINEAR: function(t, b, c, d) {
5979         return c * t / d + b;
5980     },
5981 
5982     SWING: function(t, b, c, d) {
5983         return c * (0.5 - Math.cos(((t / d) * Math.PI)) / 2) + b;
5984     },
5985 
5986     // *** quad
5987     QUAD_EASEIN: function(t, b, c, d) {
5988         return c * (t /= d) * t + b;
5989     },
5990 
5991     QUAD_EASEOUT: function(t, b, c, d) {
5992         return -c * (t /= d) * (t - 2) + b;
5993     },
5994 
5995     QUAD_EASEINOUT: function(t, b, c, d) {
5996         if ((t /= d / 2) < 1) {
5997             return c / 2 * t * t + b;
5998         }
5999         return -c / 2 * ((--t) * (t - 2) - 1) + b;
6000     },
6001 
6002     // *** cubic
6003     CUBIC_EASEIN: function(t, b, c, d) {
6004         return c * (t /= d) * t * t + b;
6005     },
6006 
6007     CUBIC_EASEOUT: function(t, b, c, d) {
6008         return c * ((t = t / d - 1) * t * t + 1) + b;
6009     },
6010 
6011     CUBIC_EASEINOUT: function(t, b, c, d) {
6012         if ((t /= d / 2) < 1) {
6013             return c / 2 * t * t * t + b;
6014         }
6015         return c / 2 * ((t -= 2) * t * t + 2) + b;
6016     },
6017 
6018     // *** quart
6019     QUART_EASEIN: function(t, b, c, d) {
6020         return c * (t /= d) * t * t * t + b;
6021     },
6022 
6023     QUART_EASEOUT: function(t, b, c, d) {
6024         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
6025     },
6026 
6027     QUART_EASEINOUT: function(t, b, c, d) {
6028         if ((t /= d / 2) < 1) {
6029             return c / 2 * t * t * t * t + b;
6030         }
6031         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
6032     },
6033 
6034     // *** quint
6035     QUINT_EASEIN: function(t, b, c, d) {
6036         return c * (t /= d) * t * t * t * t + b;
6037     },
6038 
6039     QUINT_EASEOUT: function(t, b, c, d) {
6040         return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
6041     },
6042 
6043     QUINT_EASEINOUT: function(t, b, c, d) {
6044         if ((t /= d / 2) < 1) {
6045             return c / 2 * t * t * t * t * t + b;
6046         }
6047         return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
6048     },
6049 
6050     // *** sin
6051     SIN_EASEIN: function(t, b, c, d) {
6052         return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
6053     },
6054 
6055     SIN_EASEOUT: function(t, b, c, d) {
6056         return c * Math.sin(t / d * (Math.PI / 2)) + b;
6057     },
6058 
6059     SIN_EASEINOUT: function(t, b, c, d) {
6060         return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
6061     },
6062 
6063     // *** circ
6064     CIRC_EASEIN: function(t, b, c, d) {
6065         return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
6066     },
6067 
6068     CIRC_EASEOUT: function(t, b, c, d) {
6069         return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
6070     },
6071 
6072     CIRC_EASEINOUT: function(t, b, c, d) {
6073         if ((t /= d / 2) < 1) {
6074             return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
6075         }
6076         return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
6077     },
6078 
6079     // *** elastic
6080     ELASTIC_EASEIN: function(t, b, c, d, a, p) {
6081         if (t === 0) {
6082             return b;
6083         }
6084         if ((t /= d) === 1) {
6085             return b + c;
6086         }
6087 
6088         if (!p) {
6089             p = d * 0.3;
6090         }
6091 
6092         var s;
6093         if (!a || a < Math.abs(c)) {
6094             a = c;
6095             s = p / 4;
6096         } else {
6097             s = p / (2 * Math.PI) * Math.asin(c / a);
6098         }
6099         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
6100     },
6101 
6102     ELASTIC_EASEOUT: function(t, b, c, d, a, p) {
6103         if (t === 0) {
6104             return b;
6105         }
6106         if ((t /= d) === 1) {
6107             return b + c;
6108         }
6109         if (!p) {
6110             p = d * 0.3;
6111         }
6112         var s;
6113         if (!a || a < Math.abs(c)) {
6114             a = c;
6115             s = p / 4;
6116         } else {
6117             s = p / (2 * Math.PI) * Math.asin(c / a);
6118         }
6119         return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
6120     },
6121 
6122     ELASTIC_EASEINOUT: function(t, b, c, d, a, p) {
6123         if (t === 0) {
6124             return b;
6125         }
6126         if ((t /= d / 2) === 2) {
6127             return b + c;
6128         }
6129         if (!p) {
6130             p = d * (0.3 * 1.5);
6131         }
6132         var s;
6133         if (!a || a < Math.abs(c)) {
6134             a = c;
6135             s = p / 4;
6136         } else {
6137             s = p / (2 * Math.PI) * Math.asin(c / a);
6138         }
6139         if (t < 1) {
6140             return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
6141         }
6142         return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b;
6143     },
6144 
6145     // *** bounce
6146     BOUNCE_EASEOUT: function(t, b, c, d) {
6147         if ((t /= d) < (1 / 2.75)) {
6148             return c * (7.5625 * t * t) + b;
6149         } else if (t < (2 / 2.75)) {
6150             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
6151         } else if (t < (2.5 / 2.75)) {
6152             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
6153         } else {
6154             return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
6155         }
6156     },
6157 
6158     BOUNCE_EASEIN: function(t, b, c, d) {
6159         return c - enchant.Easing.BOUNCE_EASEOUT(d - t, 0, c, d) + b;
6160     },
6161 
6162     BOUNCE_EASEINOUT: function(t, b, c, d) {
6163         if (t < d / 2) {
6164             return enchant.Easing.BOUNCE_EASEIN(t * 2, 0, c, d) * 0.5 + b;
6165         } else {
6166             return enchant.Easing.BOUNCE_EASEOUT(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
6167         }
6168 
6169     },
6170 
6171     // *** back
6172     BACK_EASEIN: function(t, b, c, d, s) {
6173         if (s === undefined) {
6174             s = 1.70158;
6175         }
6176         return c * (t /= d) * t * ((s + 1) * t - s) + b;
6177     },
6178 
6179     BACK_EASEOUT: function(t, b, c, d, s) {
6180         if (s === undefined) {
6181             s = 1.70158;
6182         }
6183         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
6184     },
6185 
6186     BACK_EASEINOUT: function(t, b, c, d, s) {
6187         if (s === undefined) {
6188             s = 1.70158;
6189         }
6190         if ((t /= d / 2) < 1) {
6191             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
6192         }
6193         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
6194     },
6195 
6196     // *** expo
6197     EXPO_EASEIN: function(t, b, c, d) {
6198         return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
6199     },
6200 
6201     EXPO_EASEOUT: function(t, b, c, d) {
6202         return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
6203     },
6204 
6205     EXPO_EASEINOUT: function(t, b, c, d) {
6206         if (t === 0) {
6207             return b;
6208         }
6209         if (t === d) {
6210             return b + c;
6211         }
6212         if ((t /= d / 2) < 1) {
6213             return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
6214         }
6215         return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
6216     }
6217 };
6218 
6219 /**
6220  * @scope enchant.ActionEventTarget.prototype
6221  */
6222 enchant.ActionEventTarget = enchant.Class.create(enchant.EventTarget, {
6223     /**
6224      * @name enchant.ActionEventTarget
6225      * @class
6226      * EventTarget which can change the context of event listeners.
6227      * @constructs
6228      * @extends enchant.EventTarget
6229      */
6230     initialize: function() {
6231         enchant.EventTarget.apply(this, arguments);
6232     },
6233     dispatchEvent: function(e) {
6234         var target = this.node ? this.node : this;
6235 
6236         e.target = target;
6237         e.localX = e.x - target._offsetX;
6238         e.localY = e.y - target._offsetY;
6239 
6240         if (this['on' + e.type] != null) {
6241             this['on' + e.type].call(target, e);
6242         }
6243         var listeners = this._listeners[e.type];
6244         if (listeners != null) {
6245             listeners = listeners.slice();
6246             for (var i = 0, len = listeners.length; i < len; i++) {
6247                 listeners[i].call(target, e);
6248             }
6249         }
6250     }
6251 });
6252 
6253 /**
6254  * @scope enchant.Timeline.prototype
6255  */
6256 enchant.Timeline = enchant.Class.create(enchant.EventTarget, {
6257     /**
6258      * @name enchant.Timeline
6259      * @class
6260      * Time-line class.
6261      * Class for managing the action.
6262      *
6263      * For one node to manipulate the timeline of one must correspond.
6264      * Time-line class has a method to add a variety of actions to himself,
6265      * entities can be animated and various operations by using these briefly.
6266      * You can choose time based and frame based(default) animation.
6267      * @param {enchant.Node} node target node.
6268      * @constructs
6269      * @extends enchant.EventTarget
6270      */
6271     initialize: function(node) {
6272         enchant.EventTarget.call(this);
6273         this.node = node;
6274         this.queue = [];
6275         this.paused = false;
6276         this.looped = false;
6277         this.isFrameBased = true;
6278         this._parallel = null;
6279         this._activated = false;
6280         this.addEventListener(enchant.Event.ENTER_FRAME, this._onenterframe);
6281 
6282         var tl = this;
6283         this._nodeEventListener = function(e) {
6284             tl.dispatchEvent(e);
6285         };
6286     },
6287     /**
6288      * @private
6289      */
6290     _deactivateTimeline: function() {
6291         if (this._activated) {
6292             this._activated = false;
6293             this.node.removeEventListener('enterframe', this._nodeEventListener);
6294         }
6295     },
6296     /**
6297      * @private
6298      */
6299     _activateTimeline: function() {
6300         if (!this._activated && !this.paused) {
6301             this.node.addEventListener("enterframe", this._nodeEventListener);
6302             this._activated = true;
6303         }
6304     },
6305     /**
6306      * @private
6307      */
6308     _onenterframe: function(evt) {
6309         if (this.paused) {
6310             return;
6311         }
6312 
6313         this.tick(this.isFrameBased ? 1 : evt.elapsed);
6314     },
6315     /**
6316      */
6317     setFrameBased: function() {
6318         this.isFrameBased = true;
6319     },
6320     /**
6321      */
6322     setTimeBased: function() {
6323         this.isFrameBased = false;
6324     },
6325     /**
6326      */
6327     next: function(remainingTime) {
6328         var e, action = this.queue.shift();
6329 
6330         if (action) {
6331             e = new enchant.Event("actionend");
6332             e.timeline = this;
6333             action.dispatchEvent(e);
6334 
6335             e = new enchant.Event("removedfromtimeline");
6336             e.timeline = this;
6337             action.dispatchEvent(e);
6338 
6339             if (this.looped) {
6340                 this.add(action);
6341             }
6342         }
6343 
6344         if (this.queue.length === 0) {
6345             this._deactivateTimeline();
6346             return;
6347         }
6348 
6349         if (remainingTime > 0 || (this.queue[0] && this.queue[0].time === 0)) {
6350             var event = new enchant.Event("actiontick");
6351             event.elapsed = remainingTime;
6352             event.timeline = this;
6353             this.queue[0].dispatchEvent(event);
6354         }
6355     },
6356     /**
6357      * @param {Number} elapsed
6358      */
6359     tick: function(elapsed) {
6360         if (this.queue.length > 0) {
6361             var action = this.queue[0];
6362             if (action.frame === 0) {
6363                 var f;
6364                 f = new enchant.Event("actionstart");
6365                 f.timeline = this;
6366                 action.dispatchEvent(f);
6367             }
6368 
6369             var e = new enchant.Event("actiontick");
6370             e.timeline = this;
6371             e.elapsed = elapsed;
6372             action.dispatchEvent(e);
6373         }
6374     },
6375     /**
6376      * @param {enchant.Action} action
6377      * @return {enchant.Timeline}
6378      */
6379     add: function(action) {
6380         this._activateTimeline();
6381         if (this._parallel) {
6382             this._parallel.actions.push(action);
6383             this._parallel = null;
6384         } else {
6385             this.queue.push(action);
6386         }
6387         action.frame = 0;
6388 
6389         var e = new enchant.Event("addedtotimeline");
6390         e.timeline = this;
6391         action.dispatchEvent(e);
6392 
6393         e = new enchant.Event("actionadded");
6394         e.action = action;
6395         this.dispatchEvent(e);
6396 
6397         return this;
6398     },
6399     /**
6400      * @param {Object} params
6401      * @return {enchant.Timeline}
6402      */
6403     action: function(params) {
6404         return this.add(new enchant.Action(params));
6405     },
6406     /**
6407      * @param {Object} params
6408      * @return {enchant.Timeline}
6409      */
6410     tween: function(params) {
6411         return this.add(new enchant.Tween(params));
6412     },
6413     /**
6414      * @return {enchant.Timeline}
6415      */
6416     clear: function() {
6417         var e = new enchant.Event("removedfromtimeline");
6418         e.timeline = this;
6419 
6420         for (var i = 0, len = this.queue.length; i < len; i++) {
6421             this.queue[i].dispatchEvent(e);
6422         }
6423         this.queue = [];
6424         this._deactivateTimeline();
6425         return this;
6426     },
6427     /**
6428      * @param {Number} frames
6429      * @return {enchant.Timeline}
6430      */
6431     skip: function(frames) {
6432         var event = new enchant.Event("enterframe");
6433         if (this.isFrameBased) {
6434             event.elapsed = 1;
6435         } else {
6436             event.elapsed = frames;
6437             frames = 1;
6438         }
6439         while (frames--) {
6440             this.dispatchEvent(event);
6441         }
6442         return this;
6443     },
6444     /**
6445      * @return {enchant.Timeline}
6446      */
6447     pause: function() {
6448         if (!this.paused) {
6449             this.paused = true;
6450             this._deactivateTimeline();
6451         }
6452         return this;
6453     },
6454     /**
6455      * @return {enchant.Timeline}
6456      */
6457     resume: function() {
6458         if (this.paused) {
6459             this.paused = false;
6460             this._activateTimeline();
6461         }
6462         return this;
6463     },
6464     /**
6465      * @return {enchant.Timeline}
6466      */
6467     loop: function() {
6468         this.looped = true;
6469         return this;
6470     },
6471     /**
6472      * @return {enchant.Timeline}
6473      */
6474     unloop: function() {
6475         this.looped = false;
6476         return this;
6477     },
6478     /**
6479      * @param {Number} time
6480      * @return {enchant.Timeline}
6481      */
6482     delay: function(time) {
6483         return this.action({
6484             time: time
6485         });
6486     },
6487     /**
6488      * @ignore
6489      * @param {Number} time
6490      */
6491     wait: function(time) {
6492         // reserved
6493         return this;
6494     },
6495     /**
6496      * @param {Function} func
6497      * @return {enchant.Timeline}
6498      */
6499     then: function(func) {
6500         return this.action({
6501             onactiontick: function(evt) {
6502                 func.call(this);
6503             },
6504             // if time is 0, next action will be immediately executed
6505             time: 0
6506         });
6507     },
6508     /**
6509      * @param {Function} func
6510      * @return {enchant.Timeline}
6511      */
6512     exec: function(func) {
6513         return this.then(func);
6514     },
6515     /**
6516      * @param {Object} cue
6517      * @return {enchant.Timeline}
6518      */
6519     cue: function(cue) {
6520         var ptr = 0;
6521         for (var frame in cue) {
6522             if (cue.hasOwnProperty(frame)) {
6523                 this.delay(frame - ptr);
6524                 this.then(cue[frame]);
6525                 ptr = frame;
6526             }
6527         }
6528         return this;
6529     },
6530     /**
6531      * @param {Function} func
6532      * @param {Number} time
6533      * @return {enchant.Timeline}
6534      */
6535     repeat: function(func, time) {
6536         return this.action({
6537             onactiontick: function(evt) {
6538                 func.call(this);
6539             },
6540             time: time
6541         });
6542     },
6543     /**
6544      * @return {enchant.Timeline}
6545      */
6546     and: function() {
6547         var last = this.queue.pop();
6548         if (last instanceof enchant.ParallelAction) {
6549             this._parallel = last;
6550             this.queue.push(last);
6551         } else {
6552             var parallel = new enchant.ParallelAction();
6553             parallel.actions.push(last);
6554             this.queue.push(parallel);
6555             this._parallel = parallel;
6556         }
6557         return this;
6558     },
6559     /**
6560      * @ignore
6561      */
6562     or: function() {
6563         return this;
6564     },
6565     /**
6566      * @ignore
6567      */
6568     doAll: function(children) {
6569         return this;
6570     },
6571     /**
6572      * @ignore
6573      */
6574     waitAll: function() {
6575         return this;
6576     },
6577     /**
6578      * @param {Function} func
6579      * @return {enchant.Timeline}
6580      */
6581     waitUntil: function(func) {
6582         return this.action({
6583             onactiontick: function(evt) {
6584                 if (func.call(this)) {
6585                     evt.timeline.next();
6586                 }
6587             }
6588         });
6589     },
6590     /**
6591      * @param {Number} opacity
6592      * @param {Number} time
6593      * @param {Function} [easing=enchant.Easing.LINEAR]
6594      * @return {enchant.Timeline}
6595      */
6596     fadeTo: function(opacity, time, easing) {
6597         return this.tween({
6598             opacity: opacity,
6599             time: time,
6600             easing: easing
6601         });
6602     },
6603     /**
6604      * @param {Number} time
6605      * @param {Function} [easing=enchant.Easing.LINEAR]
6606      * @return {enchant.Timeline}
6607      */
6608     fadeIn: function(time, easing) {
6609         return this.fadeTo(1, time, easing);
6610     },
6611     /**
6612      * @param {Number} time
6613      * @param {Function} [easing=enchant.Easing.LINEAR]
6614      * @return {enchant.Timeline}
6615      */
6616     fadeOut: function(time, easing) {
6617         return this.fadeTo(0, time, easing);
6618     },
6619     /**
6620      * @param {Number} x
6621      * @param {Number} y
6622      * @param {Number} time
6623      * @param {Function} [easing=enchant.Easing.LINEAR]
6624      * @return {enchant.Timeline}
6625      */
6626     moveTo: function(x, y, time, easing) {
6627         return this.tween({
6628             x: x,
6629             y: y,
6630             time: time,
6631             easing: easing
6632         });
6633     },
6634     /**
6635      * @param {Number} x
6636      * @param {Number} time
6637      * @param {Function} [easing=enchant.Easing.LINEAR]
6638      * @return {enchant.Timeline}
6639      */
6640     moveX: function(x, time, easing) {
6641         return this.tween({
6642             x: x,
6643             time: time,
6644             easing: easing
6645         });
6646     },
6647     /**
6648      * @param {Number} y
6649      * @param {Number} time
6650      * @param {Function} [easing=enchant.Easing.LINEAR]
6651      * @return {enchant.Timeline}
6652      */
6653     moveY: function(y, time, easing) {
6654         return this.tween({
6655             y: y,
6656             time: time,
6657             easing: easing
6658         });
6659     },
6660     /**
6661      * @param {Number} x
6662      * @param {Number} y
6663      * @param {Number} time
6664      * @param {Function} [easing=enchant.Easing.LINEAR]
6665      * @return {enchant.Timeline}
6666      */
6667     moveBy: function(x, y, time, easing) {
6668         return this.tween({
6669             x: function() {
6670                 return this.x + x;
6671             },
6672             y: function() {
6673                 return this.y + y;
6674             },
6675             time: time,
6676             easing: easing
6677         });
6678     },
6679     /**
6680      * @return {enchant.Timeline}
6681      */
6682     hide: function() {
6683         return this.then(function() {
6684             this.opacity = 0;
6685         });
6686     },
6687     /**
6688      * @return {enchant.Timeline}
6689      */
6690     show: function() {
6691         return this.then(function() {
6692             this.opacity = 1;
6693         });
6694     },
6695     /**
6696      * @return {enchant.Timeline}
6697      */
6698     removeFromScene: function() {
6699         return this.then(function() {
6700             this.parentNode.removeChild(this);
6701         });
6702     },
6703     /**
6704      * @param {Number} scaleX
6705      * @param {Number} [scaleY]
6706      * @param {Number} time
6707      * @param {Function} [easing=enchant.Easing.LINEAR]
6708      * @return {enchant.Timeline}
6709      */
6710     scaleTo: function(scale, time, easing) {
6711         var scaleX, scaleY;
6712 
6713         if (typeof easing === "number") {
6714             scaleX = arguments[0];
6715             scaleY = arguments[1];
6716             time = arguments[2];
6717             easing = arguments[3];
6718         } else {
6719             scaleX = scaleY = scale;
6720         }
6721 
6722         return this.tween({
6723             scaleX: scaleX,
6724             scaleY: scaleY,
6725             time: time,
6726             easing: easing
6727         });
6728     },
6729     /**
6730      * @param {Number} scaleX
6731      * @param {Number} [scaleY]
6732      * @param {Number} time
6733      * @param {Function} [easing=enchant.Easing.LINEAR]
6734      * @return {enchant.Timeline}
6735      */
6736     scaleBy: function(scale, time, easing) {
6737         var scaleX, scaleY;
6738 
6739         if (typeof easing === "number") {
6740             scaleX = arguments[0];
6741             scaleY = arguments[1];
6742             time = arguments[2];
6743             easing = arguments[3];
6744         } else {
6745             scaleX = scaleY = scale;
6746         }
6747 
6748         return this.tween({
6749             scaleX: function() {
6750                 return this.scaleX * scaleX;
6751             },
6752             scaleY: function() {
6753                 return this.scaleY * scaleY;
6754             },
6755             time: time,
6756             easing: easing
6757         });
6758     },
6759     /**
6760      * @param {Number} deg
6761      * @param {Number} time
6762      * @param {Function} [easing=enchant.Easing.LINEAR]
6763      * @return {enchant.Timeline}
6764      */
6765     rotateTo: function(deg, time, easing) {
6766         return this.tween({
6767             rotation: deg,
6768             time: time,
6769             easing: easing
6770         });
6771     },
6772     /**
6773      * @param {Number} deg
6774      * @param {Number} time
6775      * @param {Function} [easing=enchant.Easing.LINEAR]
6776      * @return {enchant.Timeline}
6777      */
6778     rotateBy: function(deg, time, easing) {
6779         return this.tween({
6780             rotation: function() {
6781                 return this.rotation + deg;
6782             },
6783             time: time,
6784             easing: easing
6785         });
6786     }
6787 });
6788 
6789 /**
6790  * @scope enchant.Action.prototype
6791  */
6792 enchant.Action = enchant.Class.create(enchant.ActionEventTarget, {
6793     /**
6794      * @name enchant.Action
6795      * @class
6796      * Actions are units that make up the timeline.
6797      * It is a unit used to specify the action you want to perform.
6798      * 
6799      * Actions that have been added to the timeline are performed in sequential order.
6800      * The transition from one action to the next occurs automatically 
6801      * after the number of frames specified by the time parameter have elapsed.
6802      *
6803      * An actionstart event is fired when the action has started.
6804      * An actionend event is fired when the action has stopped.
6805      * For each frame that elapses, an actiontick event is fired.
6806      * 
6807      * You can specify a listener for these events to perform specific events when they occur.
6808      *
6809      * @param {Object} param
6810      * @param {Number} [param.time] The number of frames that the action will persist. For an infinite number set this to null.
6811      * @param {Function} [param.onactionstart] Event listener for when the action is initiated.
6812      * @param {Function} [param.onactiontick] Event listener for when the action has passed one frame.
6813      * @param {Function} [param.onactionend] Event listener for when the action is finished.
6814      * @constructs
6815      * @extends enchant.ActionEventTarget
6816      */
6817     initialize: function(param) {
6818         enchant.ActionEventTarget.call(this);
6819         this.time = null;
6820         this.frame = 0;
6821         for (var key in param) {
6822             if (param.hasOwnProperty(key)) {
6823                 if (param[key] != null) {
6824                     this[key] = param[key];
6825                 }
6826             }
6827         }
6828         var action = this;
6829 
6830         this.timeline = null;
6831         this.node = null;
6832 
6833         this.addEventListener(enchant.Event.ADDED_TO_TIMELINE, function(evt) {
6834             action.timeline = evt.timeline;
6835             action.node = evt.timeline.node;
6836             action.frame = 0;
6837         });
6838 
6839         this.addEventListener(enchant.Event.REMOVED_FROM_TIMELINE, function() {
6840             action.timeline = null;
6841             action.node = null;
6842             action.frame = 0;
6843         });
6844 
6845         this.addEventListener(enchant.Event.ACTION_TICK, function(evt) {
6846             var remaining = action.time - (action.frame + evt.elapsed);
6847             if (action.time != null && remaining <= 0) {
6848                 action.frame = action.time;
6849                 evt.timeline.next(-remaining);
6850             } else {
6851                 action.frame += evt.elapsed;
6852             }
6853         });
6854 
6855     }
6856 });
6857 
6858 /**
6859  * @scope enchant.ParallelAction.prototype
6860  */
6861 enchant.ParallelAction = enchant.Class.create(enchant.Action, {
6862     /**
6863      * @name enchant.ParallelAction
6864      * @class
6865      * Actions to be executed in parallel.
6866      * It's possible to have more than one child action.
6867      * @constructs
6868      * @extends enchant.Action
6869      */
6870     initialize: function(param) {
6871         enchant.Action.call(this, param);
6872         /**
6873          * Children Actions.
6874          * @type enchant.Action[]
6875          */
6876         this.actions = [];
6877         /**
6878          * Removed actions.
6879          * @type enchant.Action[]
6880          */
6881         this.endedActions = [];
6882         var that = this;
6883 
6884         this.addEventListener(enchant.Event.ACTION_START, function(evt) {
6885             for (var i = 0, len = that.actions.length; i < len; i++) {
6886                 that.actions[i].dispatchEvent(evt);
6887             }
6888         });
6889 
6890         this.addEventListener(enchant.Event.ACTION_TICK, function(evt) {
6891             var i, len, timeline = {
6892                 next: function(remaining) {
6893                     var action = that.actions[i];
6894                     that.actions.splice(i--, 1);
6895                     len = that.actions.length;
6896                     that.endedActions.push(action);
6897 
6898                     var e = new enchant.Event("actionend");
6899                     e.timeline = this;
6900                     action.dispatchEvent(e);
6901 
6902                     e = new enchant.Event("removedfromtimeline");
6903                     e.timeline = this;
6904                     action.dispatchEvent(e);
6905                 }
6906             };
6907 
6908             var e = new enchant.Event("actiontick");
6909             e.timeline = timeline;
6910             e.elapsed = evt.elapsed;
6911             for (i = 0, len = that.actions.length; i < len; i++) {
6912                 that.actions[i].dispatchEvent(e);
6913             }
6914 
6915             if (that.actions.length === 0) {
6916                 evt.timeline.next();
6917             }
6918         });
6919 
6920         this.addEventListener(enchant.Event.ADDED_TO_TIMELINE, function(evt) {
6921             for (var i = 0, len = that.actions.length; i < len; i++) {
6922                 that.actions[i].dispatchEvent(evt);
6923             }
6924         });
6925 
6926         this.addEventListener(enchant.Event.REMOVED_FROM_TIMELINE, function() {
6927             that.actions = that.endedActions;
6928             that.endedActions = [];
6929         });
6930 
6931     }
6932 });
6933 
6934 /**
6935  * @scope enchant.Tween.prototype
6936  */
6937 enchant.Tween = enchant.Class.create(enchant.Action, {
6938     /**
6939      * @name enchant.Tween
6940      * @class
6941      * @param {Object} params
6942      * @param {Number} params.time
6943      * @param {Function} [params.easing=enchant.Easing.LINEAR]
6944      * @constructs
6945      * @extends enchant.Action
6946      */
6947     initialize: function(params) {
6948         var origin = {};
6949         var target = {};
6950         enchant.Action.call(this, params);
6951 
6952         if (this.easing == null) {
6953             this.easing = enchant.Easing.LINEAR;
6954         }
6955 
6956         var tween = this;
6957         this.addEventListener(enchant.Event.ACTION_START, function() {
6958             // excepted property
6959             var excepted = ["frame", "time", "callback", "onactiontick", "onactionstart", "onactionend"];
6960             for (var prop in params) {
6961                 if (params.hasOwnProperty(prop)) {
6962                     // if function is used instead of numerical value, evaluate it
6963                     var target_val;
6964                     if (typeof params[prop] === "function") {
6965                         target_val = params[prop].call(tween.node);
6966                     } else {
6967                         target_val = params[prop];
6968                     }
6969 
6970                     if (excepted.indexOf(prop) === -1) {
6971                         origin[prop] = tween.node[prop];
6972                         target[prop] = target_val;
6973                     }
6974                 }
6975             }
6976         });
6977 
6978         this.addEventListener(enchant.Event.ACTION_TICK, function(evt) {
6979             // if time is 0, set property to target value immediately
6980             var ratio = tween.time === 0 ? 1 : tween.easing(Math.min(tween.time,tween.frame + evt.elapsed), 0, 1, tween.time) - tween.easing(tween.frame, 0, 1, tween.time);
6981 
6982             for (var prop in target){
6983                 if (target.hasOwnProperty(prop)) {
6984                     if (typeof this[prop] === "undefined"){
6985                         continue;
6986                     }
6987                     tween.node[prop] += (target[prop] - origin[prop]) * ratio;
6988                     if (Math.abs(tween.node[prop]) < 10e-8){
6989                         tween.node[prop] = 0;
6990                     }
6991                 }
6992             }
6993         });
6994     }
6995 });
6996 
6997 }(window));
6998