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