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