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  * グローバルにライブラリのクラスをエクスポートする.
109  *
110  * 引数に何も渡さない場合enchant.jsで定義されたクラス及びプラグインで定義されたクラス
111  * 全てがエクスポートされる. 引数が一つ以上の場合はenchant.jsで定義されたクラスのみ
112  * がデフォルトでエクスポートされ, プラグインのクラスをエクスポートしたい場合は明示的に
113  * プラグインの識別子を引数として渡す必要がある.
114  *
115  * @example
116  * enchant();     // 全てのクラスがエクスポートされる
117  * enchant('');   // enchant.js本体のクラスのみがエクスポートされる
118  * enchant('ui'); // enchant.js本体のクラスとui.enchant.jsのクラスがエクスポートされる
119  *
120  * @param {...String} [modules] エクスポートするモジュール. 複数指定できる.
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  * クラスのクラス.
205  * @param {Function} [superclass] 継承するクラス.
206  * @param {*} [definition] クラス定義.
207  * @constructor
208  */
209 enchant.Class = function(superclass, definition) {
210     return enchant.Class.create(superclass, definition);
211 };
212 
213 /**
214  * クラスを作成する.
215  *
216  * ほかのクラスを継承したクラスを作成する場合, コンストラクタはデフォルトで
217  * 継承元のクラスのものが使われる. コンストラクタをオーバーライドする場合継承元の
218  * コンストラクタを適用するには明示的に呼び出す必要がある.
219  *
220  * @example
221  * var Ball = Class.create({ // 何も継承しないクラスを作成する
222  *     initialize: function(radius) { ... }, // メソッド定義
223  *     fall: function() { ... }
224  * });
225  *
226  * var Ball = Class.create(Sprite);  // Spriteを継承したクラスを作成する
227  * var Ball = Class.create(Sprite, { // Spriteを継承したクラスを作成する
228  *     initialize: function(radius) { // コンストラクタを上書きする
229  *         Sprite.call(this, radius * 2, radius * 2); // 継承元のコンストラクタを適用する
230  *         this.image = core.assets['ball.gif'];
231  *     }
232  * });
233  *
234  * @param {Function} [superclass] 継承するクラス.
235  * @param {*} [definition] クラス定義.
236  * @static
237  */
238 enchant.Class.create = function(superclass, definition) {
239     if (superclass == null && definition) {
240         throw new Error("superclass is undefined (enchant.Class.create)");
241     } else if (superclass == null) {
242         throw new Error("definition is undefined (enchant.Class.create)");
243     }
244 
245     if (arguments.length === 0) {
246         return enchant.Class.create(Object, definition);
247     } else if (arguments.length === 1 && typeof arguments[0] !== 'function') {
248         return enchant.Class.create(Object, arguments[0]);
249     }
250 
251     for (var prop in definition) {
252         if (definition.hasOwnProperty(prop)) {
253             if (typeof definition[prop] === 'object' && definition[prop] !== null && Object.getPrototypeOf(definition[prop]) === Object.prototype) {
254                 if (!('enumerable' in definition[prop])) {
255                     definition[prop].enumerable = true;
256                 }
257             } else {
258                 definition[prop] = { value: definition[prop], enumerable: true, writable: true };
259             }
260         }
261     }
262     var Constructor = function() {
263         if (this instanceof Constructor) {
264             Constructor.prototype.initialize.apply(this, arguments);
265         } else {
266             return new Constructor();
267         }
268     };
269     Constructor.prototype = Object.create(superclass.prototype, definition);
270     Constructor.prototype.constructor = Constructor;
271     if (Constructor.prototype.initialize == null) {
272         Constructor.prototype.initialize = function() {
273             superclass.apply(this, arguments);
274         };
275     }
276 
277     var tree = this.getInheritanceTree(superclass);
278     for (var i = 0, l = tree.length; i < l; i++) {
279         if (typeof tree[i]._inherited === 'function') {
280             tree[i]._inherited(Constructor);
281             break;
282         }
283     }
284 
285     return Constructor;
286 };
287 
288 /**
289  * クラスの継承関係を取得する.
290  * @param {Function} コンストラクタ.
291  * @return {Function[]} 親のコンストラクタ.
292  */
293 enchant.Class.getInheritanceTree = function(Constructor) {
294     var ret = [];
295     var C = Constructor;
296     var proto = C.prototype;
297     while (C !== Object) {
298         ret.push(C);
299         proto = Object.getPrototypeOf(proto);
300         C = proto.constructor;
301     }
302     return ret;
303 };
304 
305 /**
306  * @namespace
307  * enchant.js の環境変数.
308  * new Core() を呼ぶ前に変更することで変更することで, 動作設定を変えることができる.
309  */
310 enchant.ENV = {
311     /**
312      * enchant.jsのバージョン.
313      * @type String
314      */
315     VERSION: '0.8.3',
316     /**
317      * 実行中のブラウザの種類.
318      * @type String
319      */
320     BROWSER: (function(ua) {
321         if (/Eagle/.test(ua)) {
322             return 'eagle';
323         } else if (/Opera/.test(ua)) {
324             return 'opera';
325         } else if (/MSIE|Trident/.test(ua)) {
326             return 'ie';
327         } else if (/Chrome/.test(ua)) {
328             return 'chrome';
329         } else if (/(?:Macintosh|Windows).*AppleWebKit/.test(ua)) {
330             return 'safari';
331         } else if (/(?:iPhone|iPad|iPod).*AppleWebKit/.test(ua)) {
332             return 'mobilesafari';
333         } else if (/Firefox/.test(ua)) {
334             return 'firefox';
335         } else if (/Android/.test(ua)) {
336             return 'android';
337         } else {
338             return '';
339         }
340     }(navigator.userAgent)),
341     /**
342      * 実行中のブラウザに対応するCSSのベンダープレフィックス.
343      * @type String
344      */
345     VENDOR_PREFIX: (function() {
346         var ua = navigator.userAgent;
347         if (ua.indexOf('Opera') !== -1) {
348             return 'O';
349         } else if (/MSIE|Trident/.test(ua)) {
350             return 'ms';
351         } else if (ua.indexOf('WebKit') !== -1) {
352             return 'webkit';
353         } else if (navigator.product === 'Gecko') {
354             return 'Moz';
355         } else {
356             return '';
357         }
358     }()),
359     /**
360      * ブラウザがタッチ入力をサポートしているかどうか.
361      * @type Boolean
362      */
363     TOUCH_ENABLED: (function() {
364         var div = document.createElement('div');
365         div.setAttribute('ontouchstart', 'return');
366         return typeof div.ontouchstart === 'function';
367     }()),
368     /**
369      * 実行中の環境がRetina DisplayのiPhoneかどうか.
370      * @type Boolean
371      */
372     RETINA_DISPLAY: (function() {
373         if (navigator.userAgent.indexOf('iPhone') !== -1 && window.devicePixelRatio === 2) {
374             var viewport = document.querySelector('meta[name="viewport"]');
375             if (viewport == null) {
376                 viewport = document.createElement('meta');
377                 document.head.appendChild(viewport);
378             }
379             viewport.setAttribute('content', 'width=640');
380             return true;
381         } else {
382             return false;
383         }
384     }()),
385     /**
386      * サウンドの再生にHTMLAudioElement/WebAudioの代わりにflashのプレーヤーを使うかどうか.
387      * @type Boolean
388      */
389     USE_FLASH_SOUND: (function() {
390         var ua = navigator.userAgent;
391         var vendor = navigator.vendor || "";
392         // non-local access, not on mobile mobile device, not on safari
393         return (location.href.indexOf('http') === 0 && ua.indexOf('Mobile') === -1 && vendor.indexOf('Apple') !== -1);
394     }()),
395     /**
396      * クリック/タッチ時の規定の動作を許可するhtmlタグ名.
397      * ここに追加したhtmlタグへのイベントはpreventDefaultされない.
398      * @type String[]
399      */
400     USE_DEFAULT_EVENT_TAGS: ['input', 'textarea', 'select', 'area'],
401     /**
402      * SurfaceのメソッドとしてアクセスできるようにするCanvasRenderingContext2Dのメソッド.
403      * @type String[]
404      */
405     CANVAS_DRAWING_METHODS: [
406         'putImageData', 'drawImage', 'drawFocusRing', 'fill', 'stroke',
407         'clearRect', 'fillRect', 'strokeRect', 'fillText', 'strokeText'
408     ],
409     /**
410      * キーバインドのテーブル.
411      * デフォルトで 'left, 'up', 'right', 'down' のイベントが使用可能.
412      * @example
413      * enchant.ENV.KEY_BIND_TABLE = {
414      *     37: 'left',
415      *     38: 'up',
416      *     39: 'right',
417      *     40: 'down',
418      *     32: 'a', //-> スペースキーをaボタンとして使う.
419      * };
420      * @type Object
421      */
422     KEY_BIND_TABLE: {
423         37: 'left',
424         38: 'up',
425         39: 'right',
426         40: 'down'
427     },
428     /**
429      * キー押下時の規定の動作を抑止するキーコード.
430      * ここに追加したキーによるイベントはpreventDefaultされる.
431      * @type Number[]
432      */
433     PREVENT_DEFAULT_KEY_CODES: [37, 38, 39, 40],
434     /**
435      * Mobile Safariでサウンドの再生を有効にするかどうか.
436      * @type Boolean
437      */
438     SOUND_ENABLED_ON_MOBILE_SAFARI: true,
439     /**
440      * "touch to start" のシーンを使用するかどうか.
441      * Mobile SafariでWebAudioのサウンドを再生するためには,
442      * 一度タッチイベントハンドラ内で音声を流す必要があるため,
443      * Mobile Safariでの実行時にはこのシーンが追加される.
444      * falseにすることで, このシーンを表示しないようにできるが,
445      * その場合は, 自身の責任でサウンドを有効化する必要がある.
446      * @type Boolean
447      */
448     USE_TOUCH_TO_START_SCENE: true,
449     /**
450      * WebAudioを有効にするどうか.
451      * trueならサウンドの再生の際HTMLAudioElementの代わりにWebAudioAPIを使用する.
452      * @type Boolean
453      */
454     USE_WEBAUDIO: (function() {
455         return location.protocol !== 'file:';
456     }()),
457     /**
458      * アニメーション機能を有効にするかどうか.
459      * trueだと, Node#tlにTimelineオブジェクトが作成される.
460      * @type Boolean
461      */
462     USE_ANIMATION: true,
463     /**
464      * タッチ位置の判定範囲.
465      * 判定範囲はCOLOR_DETECTION_LEVEL * 2 + 1の正方形になる.
466      * @type Boolean
467      */
468     COLOR_DETECTION_LEVEL: 2
469 };
470 
471 /**
472  * @scope enchant.Event.prototype
473  */
474 enchant.Event = enchant.Class.create({
475     /**
476      * @name enchant.Event
477      * @class
478      * DOM Event風味の独自イベント実装を行ったクラス.
479      * ただしフェーズの概念はなし.
480      * @param {String} type Eventのタイプ
481      * @constructs
482      */
483     initialize: function(type) {
484         /**
485          * イベントのタイプ.
486          * @type String
487          */
488         this.type = type;
489         /**
490          * イベントのターゲット.
491          * @type *
492          */
493         this.target = null;
494         /**
495          * イベント発生位置のx座標.
496          * @type Number
497          */
498         this.x = 0;
499         /**
500          * イベント発生位置のy座標.
501          * @type Number
502          */
503         this.y = 0;
504         /**
505          * イベントを発行したオブジェクトを基準とするイベント発生位置のx座標.
506          * @type Number
507          */
508         this.localX = 0;
509         /**
510          * イベントを発行したオブジェクトを基準とするイベント発生位置のy座標.
511          * @type Number
512          */
513         this.localY = 0;
514     },
515     _initPosition: function(pageX, pageY) {
516         var core = enchant.Core.instance;
517         this.x = this.localX = (pageX - core._pageX) / core.scale;
518         this.y = this.localY = (pageY - core._pageY) / core.scale;
519     }
520 });
521 
522 /**
523  * Coreのロード完了時に発生するイベント.
524  *
525  * 画像のプリロードを行う場合ロードが完了するのを待ってゲーム開始時の処理を行う必要がある.
526  * 発行するオブジェクト: {@link enchant.Core}
527  *
528  * @example
529  * var core = new Core(320, 320);
530  * core.preload('player.gif');
531  * core.onload = function() {
532  *     ... // ゲーム開始時の処理を記述
533  * };
534  * core.start();
535  * @type String
536  */
537 enchant.Event.LOAD = 'load';
538 
539 /**
540  * エラーの発生をCoreに伝える際に発生するイベント.
541  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Surface}, {@link enchant.WebAudioSound}, {@link enchant.DOMSound}
542  */
543 enchant.Event.ERROR = 'error';
544 
545 /**
546  * 表示サイズが変わったときに発生するイベント.
547  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
548  @type String
549  */
550 enchant.Event.CORE_RESIZE = 'coreresize';
551 
552 /**
553  * Coreのロード進行中に発生するイベント.
554  * プリロードする画像が一枚ロードされる度に発行される. 発行するオブジェクト: {@link enchant.LoadingScene}
555  * @type String
556  */
557 enchant.Event.PROGRESS = 'progress';
558 
559 /**
560  * フレーム開始時に発生するイベント.
561  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Node}
562  * @type String
563  */
564 enchant.Event.ENTER_FRAME = 'enterframe';
565 
566 /**
567  * フレーム終了時に発生するイベント.
568  * 発行するオブジェクト: {@link enchant.Core}
569  * @type String
570  */
571 enchant.Event.EXIT_FRAME = 'exitframe';
572 
573 /**
574  * Sceneが開始したとき発生するイベント.
575  * 発行するオブジェクト: {@link enchant.Scene}
576  * @type String
577  */
578 enchant.Event.ENTER = 'enter';
579 
580 /**
581  * Sceneが終了したとき発生するイベント.
582  * 発行するオブジェクト: {@link enchant.Scene}
583  * @type String
584  */
585 enchant.Event.EXIT = 'exit';
586 
587 /**
588  * Nodeに子が追加されたとき発生するイベント.
589  * 発行するオブジェクト: {@link enchant.Group}, {@link enchant.Scene}
590  * @type String
591  */
592 enchant.Event.CHILD_ADDED = 'childadded';
593 
594 /**
595  * NodeがGroupに追加されたとき発生するイベント.
596  * 発行するオブジェクト: {@link enchant.Node}
597  * @type String
598  */
599 enchant.Event.ADDED = 'added';
600 
601 /**
602  * NodeがSceneに追加されたとき発生するイベント.
603  * 発行するオブジェクト: {@link enchant.Node}
604  * @type String
605  */
606 enchant.Event.ADDED_TO_SCENE = 'addedtoscene';
607 
608 /**
609  * Nodeから子が削除されたとき発生するイベント.
610  * 発行するオブジェクト: {@link enchant.Group}, {@link enchant.Scene}
611  * @type String
612  * @type String
613  */
614 enchant.Event.CHILD_REMOVED = 'childremoved';
615 
616 /**
617  * NodeがGroupから削除されたとき発生するイベント.
618  * 発行するオブジェクト: {@link enchant.Node}
619  * @type String
620  */
621 enchant.Event.REMOVED = 'removed';
622 
623 /**
624  * NodeがSceneから削除されたとき発生するイベント.
625  * 発行するオブジェクト: {@link enchant.Node}
626  * @type String
627  */
628 enchant.Event.REMOVED_FROM_SCENE = 'removedfromscene';
629 
630 /**
631  * Nodeに対するタッチが始まったとき発生するイベント.
632  * クリックもタッチとして扱われる. 発行するオブジェクト: {@link enchant.Node}
633  * @type String
634  */
635 enchant.Event.TOUCH_START = 'touchstart';
636 
637 /**
638  * Nodeに対するタッチが移動したとき発生するイベント.
639  * クリックもタッチとして扱われる. 発行するオブジェクト: {@link enchant.Node}
640  * @type String
641  */
642 enchant.Event.TOUCH_MOVE = 'touchmove';
643 
644 /**
645  * Nodeに対するタッチが終了したとき発生するイベント.
646  * クリックもタッチとして扱われる. 発行するオブジェクト: {@link enchant.Node}
647  * @type String
648  */
649 enchant.Event.TOUCH_END = 'touchend';
650 
651 /**
652  * Entityがレンダリングされるときに発生するイベント.
653  * 発行するオブジェクト: {@link enchant.Entity}
654  * @type String
655  */
656 enchant.Event.RENDER = 'render';
657 
658 /**
659  * ボタン入力が始まったとき発生するイベント.
660  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
661  * @type String
662  */
663 enchant.Event.INPUT_START = 'inputstart';
664 
665 /**
666  * ボタン入力が変化したとき発生するイベント.
667  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
668  * @type String
669  */
670 enchant.Event.INPUT_CHANGE = 'inputchange';
671 
672 /**
673  * ボタン入力が終了したとき発生するイベント.
674  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
675  * @type String
676  */
677 enchant.Event.INPUT_END = 'inputend';
678 
679 /**
680  * 入力が変化したとき発生するイベント.
681  * ボタン入力が変化したとき発生する内部的なイベント.
682  * 発行するオブジェクト: {@link enchant.InputSource}
683  * @type String
684  */
685 enchant.Event.INPUT_STATE_CHANGED = 'inputstatechanged';
686 
687 /**
688  * leftボタンが押された発生するイベント.
689  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
690  * @type String
691  */
692 enchant.Event.LEFT_BUTTON_DOWN = 'leftbuttondown';
693 
694 /**
695  * leftボタンが離された発生するイベント.
696  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
697  * @type String
698  */
699 enchant.Event.LEFT_BUTTON_UP = 'leftbuttonup';
700 
701 /**
702  * rightボタンが押された発生するイベント.
703  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
704  * @type String
705  */
706 enchant.Event.RIGHT_BUTTON_DOWN = 'rightbuttondown';
707 
708 /**
709  * rightボタンが離された発生するイベント.
710  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
711  * @type String
712  */
713 enchant.Event.RIGHT_BUTTON_UP = 'rightbuttonup';
714 
715 /**
716  * upボタンが押された発生するイベント.
717  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
718  * @type String
719  */
720 enchant.Event.UP_BUTTON_DOWN = 'upbuttondown';
721 
722 /**
723  * upボタンが離された発生するイベント.
724  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
725  * @type String
726  */
727 enchant.Event.UP_BUTTON_UP = 'upbuttonup';
728 
729 /**
730  * downボタンが離された発生するイベント.
731  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
732  * @type String
733  */
734 enchant.Event.DOWN_BUTTON_DOWN = 'downbuttondown';
735 
736 /**
737  * downボタンが離された発生するイベント.
738  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
739  * @type String
740  */
741 enchant.Event.DOWN_BUTTON_UP = 'downbuttonup';
742 
743 /**
744  * aボタンが押された発生するイベント.
745  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
746  * @type String
747  */
748 enchant.Event.A_BUTTON_DOWN = 'abuttondown';
749 
750 /**
751  * aボタンが離された発生するイベント.
752  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
753  * @type String
754  */
755 enchant.Event.A_BUTTON_UP = 'abuttonup';
756 
757 /**
758  * bボタンが押された発生するイベント.
759  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
760  * @type String
761  */
762 enchant.Event.B_BUTTON_DOWN = 'bbuttondown';
763 
764 /**
765  * bボタンが離された発生するイベント.
766  * 発行するオブジェクト: {@link enchant.Core}, {@link enchant.Scene}
767  * @type String
768  */
769 enchant.Event.B_BUTTON_UP = 'bbuttonup';
770 
771 /**
772  * アクションがタイムラインに追加された時に発行されるイベント.
773  * looped が設定されている時も, アクションは一度タイムラインから削除されもう一度追加される.
774  * @type String
775  */
776 enchant.Event.ADDED_TO_TIMELINE = "addedtotimeline";
777 
778 /**
779  * アクションがタイムラインから削除された時に発行されるイベント.
780  * looped が設定されている時も, アクションは一度タイムラインから削除されもう一度追加される.
781  * @type String
782  */
783 enchant.Event.REMOVED_FROM_TIMELINE = "removedfromtimeline";
784 
785 /**
786  * アクションが開始された時に発行されるイベント.
787  * @type String
788  */
789 enchant.Event.ACTION_START = "actionstart";
790 
791 /**
792  * アクションが終了するときに発行されるイベント.
793  * @type String
794  */
795 enchant.Event.ACTION_END = "actionend";
796 
797 /**
798  * アクションが1フレーム経過するときに発行されるイベント.
799  * @type String
800  */
801 enchant.Event.ACTION_TICK = "actiontick";
802 
803 /**
804  * アクションが追加された時に, タイムラインに対して発行されるイベント.
805  * @type String
806  */
807 enchant.Event.ACTION_ADDED = "actionadded";
808 
809 /**
810  * アクションが削除された時に, タイムラインに対して発行されるイベント.
811  * @type String
812  */
813 enchant.Event.ACTION_REMOVED = "actionremoved";
814 
815 /**
816  * フレームアニメーションが終了したときに発生するイベント. フレームの再生がnullに到達したことを意味する.
817  * @type String
818  */
819 enchant.Event.ANIMATION_END = "animationend";
820 
821 /**
822  * @scope enchant.EventTarget.prototype
823  */
824 enchant.EventTarget = enchant.Class.create({
825     /**
826      * @name enchant.EventTarget
827      * @class
828      * DOM Event風味の独自イベント実装を行ったクラス.
829      * ただしフェーズの概念はなし.
830      * @constructs
831      */
832     initialize: function() {
833         this._listeners = {};
834     },
835     /**
836      * イベントリスナを追加する.
837      * @param {String} type イベントのタイプ.
838      * @param {Function(e:enchant.Event)} listener 追加するイベントリスナ.
839      */
840     addEventListener: function(type, listener) {
841         var listeners = this._listeners[type];
842         if (listeners == null) {
843             this._listeners[type] = [listener];
844         } else if (listeners.indexOf(listener) === -1) {
845             listeners.unshift(listener);
846 
847         }
848     },
849     /**
850      * addEventListener と同じ.
851      * @param {String} type イベントのタイプ.
852      * @param {Function(e:enchant.Event)} listener 追加するイベントリスナ.
853      * @see enchant.EventTarget#addEventListener
854      */
855     on: function() {
856         this.addEventListener.apply(this, arguments);
857     },
858     /**
859      * イベントリスナを削除する.
860      * @param {String} type イベントのタイプ.
861      * @param {Function(e:enchant.Event)} listener 削除するイベントリスナ.
862      */
863     removeEventListener: function(type, listener) {
864         var listeners = this._listeners[type];
865         if (listeners != null) {
866             var i = listeners.indexOf(listener);
867             if (i !== -1) {
868                 listeners.splice(i, 1);
869             }
870         }
871     },
872     /**
873      * すべてのイベントリスナを削除する.
874      * @param {String} type イベントのタイプ.
875      */
876     clearEventListener: function(type) {
877         if (type != null) {
878             delete this._listeners[type];
879         } else {
880             this._listeners = {};
881         }
882     },
883     /**
884      * イベントを発行する.
885      * @param {enchant.Event} e 発行するイベント.
886      */
887     dispatchEvent: function(e) {
888         e.target = this;
889         e.localX = e.x - this._offsetX;
890         e.localY = e.y - this._offsetY;
891         if (this['on' + e.type] != null){
892             this['on' + e.type](e);
893         }
894         var listeners = this._listeners[e.type];
895         if (listeners != null) {
896             listeners = listeners.slice();
897             for (var i = 0, len = listeners.length; i < len; i++) {
898                 listeners[i].call(this, e);
899             }
900         }
901     }
902 });
903 
904 (function() {
905     var core;
906     /**
907      * @scope enchant.Core.prototype
908      */
909     enchant.Core = enchant.Class.create(enchant.EventTarget, {
910         /**
911          * @name enchant.Core
912          * @class
913          * アプリケーションのメインループ, シーンを管理するクラス.
914          *
915          * インスタンスは一つしか存在することができず, すでにインスタンスが存在する状態で
916          * コンストラクタを実行した場合既存のものが上書きされる. 存在するインスタンスには
917          * {@link enchant.Core.instance} からアクセスできる.
918          *
919          * @param {Number} [width=320] 画面の横幅.
920          * @param {Number} [height=320] 画面の高さ.
921          * @constructs
922          * @extends enchant.EventTarget
923          */
924         initialize: function(width, height) {
925             if (window.document.body === null) {
926                 // @TODO postpone initialization after window.onload
927                 throw new Error("document.body is null. Please excute 'new Core()' in window.onload.");
928             }
929 
930             enchant.EventTarget.call(this);
931             var initial = true;
932             if (core) {
933                 initial = false;
934                 core.stop();
935             }
936             core = enchant.Core.instance = this;
937 
938             this._calledTime = 0;
939             this._mousedownID = 0;
940             this._surfaceID = 0;
941             this._soundID = 0;
942 
943             this._scenes = [];
944 
945             width = width || 320;
946             height = height || 320;
947 
948             var stage = document.getElementById('enchant-stage');
949             var scale, sWidth, sHeight;
950             if (!stage) {
951                 stage = document.createElement('div');
952                 stage.id = 'enchant-stage';
953                 stage.style.position = 'absolute';
954 
955                 if (document.body.firstChild) {
956                     document.body.insertBefore(stage, document.body.firstChild);
957                 } else {
958                     document.body.appendChild(stage);
959                 }
960                 scale = Math.min(
961                     window.innerWidth / width,
962                     window.innerHeight / height
963                 );
964                 this._pageX = stage.getBoundingClientRect().left;
965                 this._pageY = stage.getBoundingClientRect().top;
966             } else {
967                 var style = window.getComputedStyle(stage);
968                 sWidth = parseInt(style.width, 10);
969                 sHeight = parseInt(style.height, 10);
970                 if (sWidth && sHeight) {
971                     scale = Math.min(
972                         sWidth / width,
973                         sHeight / height
974                     );
975                 } else {
976                     scale = 1;
977                 }
978                 while (stage.firstChild) {
979                     stage.removeChild(stage.firstChild);
980                 }
981                 stage.style.position = 'relative';
982 
983                 var bounding = stage.getBoundingClientRect();
984                 this._pageX = Math.round(window.scrollX || window.pageXOffset + bounding.left);
985                 this._pageY = Math.round(window.scrollY || window.pageYOffset + bounding.top);
986             }
987             stage.style.fontSize = '12px';
988             stage.style.webkitTextSizeAdjust = 'none';
989             stage.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)';
990             this._element = stage;
991 
992             this.addEventListener('coreresize', this._oncoreresize);
993 
994             this._width = width;
995             this._height = height;
996             this.scale = scale;
997 
998             /**
999              * フレームレート.
1000              * @type Number
1001              */
1002             this.fps = 30;
1003             /**
1004              * アプリの開始からのフレーム数.
1005              * @type Number
1006              */
1007             this.frame = 0;
1008             /**
1009              * アプリが実行可能な状態かどうか.
1010              * @type Boolean
1011              */
1012             this.ready = false;
1013             /**
1014              * アプリが実行状態かどうか.
1015              * @type Boolean
1016              */
1017             this.running = false;
1018             /**
1019              * ロードされた画像をパスをキーとして保存するオブジェクト.
1020              * @type Object
1021              */
1022             this.assets = {};
1023             var assets = this._assets = [];
1024             (function detectAssets(module) {
1025                 if (module.assets) {
1026                     enchant.Core.instance.preload(module.assets);
1027                 }
1028                 for (var prop in module) {
1029                     if (module.hasOwnProperty(prop)) {
1030                         if (typeof module[prop] === 'object' && module[prop] !== null && Object.getPrototypeOf(module[prop]) === Object.prototype) {
1031                             detectAssets(module[prop]);
1032                         }
1033                     }
1034                 }
1035             }(enchant));
1036 
1037             /**
1038              * 現在のScene. Sceneスタック中の一番上のScene.
1039              * @type enchant.Scene
1040              */
1041             this.currentScene = null;
1042             /**
1043              * ルートScene. Sceneスタック中の一番下のScene.
1044              * @type enchant.Scene
1045              */
1046             this.rootScene = new enchant.Scene();
1047             this.pushScene(this.rootScene);
1048             /**
1049              * ローディング時に表示されるScene.
1050              * @type enchant.Scene
1051              */
1052             this.loadingScene = new enchant.LoadingScene();
1053 
1054             /**
1055              * 一度でも {@link enchant.Core#start} が呼ばれたことがあるかどうか.
1056              [/lang:ja]
1057              * Indicates whether or not {@link enchant.Core#start} has been called.
1058              [/lang]
1059              * @type Boolean
1060              * @private
1061              */
1062             this._activated = false;
1063 
1064             this._offsetX = 0;
1065             this._offsetY = 0;
1066 
1067             /**
1068              * アプリに対する入力状態を保存するオブジェクト.
1069              * @type Object
1070              */
1071             this.input = {};
1072 
1073             this.keyboardInputManager = new enchant.KeyboardInputManager(window.document, this.input);
1074             this.keyboardInputManager.addBroadcastTarget(this);
1075             this._keybind = this.keyboardInputManager._binds;
1076 
1077             if (!enchant.ENV.KEY_BIND_TABLE) {
1078                 enchant.ENV.KEY_BIND_TABLE = {};
1079             }
1080 
1081             for (var prop in enchant.ENV.KEY_BIND_TABLE) {
1082                 this.keybind(prop, enchant.ENV.KEY_BIND_TABLE[prop]);
1083             }
1084 
1085             if (initial) {
1086                 stage = enchant.Core.instance._element;
1087                 var evt;
1088                 document.addEventListener('keydown', function(e) {
1089                     core.dispatchEvent(new enchant.Event('keydown'));
1090                     if (enchant.ENV.PREVENT_DEFAULT_KEY_CODES.indexOf(e.keyCode) !== -1) {
1091                         e.preventDefault();
1092                         e.stopPropagation();
1093                     }
1094                 }, true);
1095 
1096                 if (enchant.ENV.TOUCH_ENABLED) {
1097                     stage.addEventListener('touchstart', function(e) {
1098                         var tagName = (e.target.tagName).toLowerCase();
1099                         if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1100                             e.preventDefault();
1101                             if (!core.running) {
1102                                 e.stopPropagation();
1103                             }
1104                         }
1105                     }, true);
1106                     stage.addEventListener('touchmove', function(e) {
1107                         var tagName = (e.target.tagName).toLowerCase();
1108                         if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1109                             e.preventDefault();
1110                             if (!core.running) {
1111                                 e.stopPropagation();
1112                             }
1113                         }
1114                     }, true);
1115                     stage.addEventListener('touchend', function(e) {
1116                         var tagName = (e.target.tagName).toLowerCase();
1117                         if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1118                             e.preventDefault();
1119                             if (!core.running) {
1120                                 e.stopPropagation();
1121                             }
1122                         }
1123                     }, true);
1124                 }
1125                 stage.addEventListener('mousedown', function(e) {
1126                     var tagName = (e.target.tagName).toLowerCase();
1127                     if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1128                         e.preventDefault();
1129                         core._mousedownID++;
1130                         if (!core.running) {
1131                             e.stopPropagation();
1132                         }
1133                     }
1134                 }, true);
1135                 stage.addEventListener('mousemove', function(e) {
1136                     var tagName = (e.target.tagName).toLowerCase();
1137                     if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1138                         e.preventDefault();
1139                         if (!core.running) {
1140                             e.stopPropagation();
1141                         }
1142                     }
1143                 }, true);
1144                 stage.addEventListener('mouseup', function(e) {
1145                     var tagName = (e.target.tagName).toLowerCase();
1146                     if (enchant.ENV.USE_DEFAULT_EVENT_TAGS.indexOf(tagName) === -1) {
1147                         e.preventDefault();
1148                         if (!core.running) {
1149                             e.stopPropagation();
1150                         }
1151                     }
1152                 }, true);
1153                 core._touchEventTarget = {};
1154                 if (enchant.ENV.TOUCH_ENABLED) {
1155                     stage.addEventListener('touchstart', function(e) {
1156                         var core = enchant.Core.instance;
1157                         var evt = new enchant.Event(enchant.Event.TOUCH_START);
1158                         var touches = e.changedTouches;
1159                         var touch, target;
1160                         for (var i = 0, l = touches.length; i < l; i++) {
1161                             touch = touches[i];
1162                             evt._initPosition(touch.pageX, touch.pageY);
1163                             target = core.currentScene._determineEventTarget(evt);
1164                             core._touchEventTarget[touch.identifier] = target;
1165                             target.dispatchEvent(evt);
1166                         }
1167                     }, false);
1168                     stage.addEventListener('touchmove', function(e) {
1169                         var core = enchant.Core.instance;
1170                         var evt = new enchant.Event(enchant.Event.TOUCH_MOVE);
1171                         var touches = e.changedTouches;
1172                         var touch, target;
1173                         for (var i = 0, l = touches.length; i < l; i++) {
1174                             touch = touches[i];
1175                             target = core._touchEventTarget[touch.identifier];
1176                             if (target) {
1177                                 evt._initPosition(touch.pageX, touch.pageY);
1178                                 target.dispatchEvent(evt);
1179                             }
1180                         }
1181                     }, false);
1182                     stage.addEventListener('touchend', function(e) {
1183                         var core = enchant.Core.instance;
1184                         var evt = new enchant.Event(enchant.Event.TOUCH_END);
1185                         var touches = e.changedTouches;
1186                         var touch, target;
1187                         for (var i = 0, l = touches.length; i < l; i++) {
1188                             touch = touches[i];
1189                             target = core._touchEventTarget[touch.identifier];
1190                             if (target) {
1191                                 evt._initPosition(touch.pageX, touch.pageY);
1192                                 target.dispatchEvent(evt);
1193                                 delete core._touchEventTarget[touch.identifier];
1194                             }
1195                         }
1196                     }, false);
1197                 }
1198                 stage.addEventListener('mousedown', function(e) {
1199                     var core = enchant.Core.instance;
1200                     var evt = new enchant.Event(enchant.Event.TOUCH_START);
1201                     evt._initPosition(e.pageX, e.pageY);
1202                     var target = core.currentScene._determineEventTarget(evt);
1203                     core._touchEventTarget[core._mousedownID] = target;
1204                     target.dispatchEvent(evt);
1205                 }, false);
1206                 stage.addEventListener('mousemove', function(e) {
1207                     var core = enchant.Core.instance;
1208                     var evt = new enchant.Event(enchant.Event.TOUCH_MOVE);
1209                     evt._initPosition(e.pageX, e.pageY);
1210                     var target = core._touchEventTarget[core._mousedownID];
1211                     if (target) {
1212                         target.dispatchEvent(evt);
1213                     }
1214                 }, false);
1215                 stage.addEventListener('mouseup', function(e) {
1216                     var core = enchant.Core.instance;
1217                     var evt = new enchant.Event(enchant.Event.TOUCH_END);
1218                     evt._initPosition(e.pageX, e.pageY);
1219                     var target = core._touchEventTarget[core._mousedownID];
1220                     if (target) {
1221                         target.dispatchEvent(evt);
1222                     }
1223                     delete core._touchEventTarget[core._mousedownID];
1224                 }, false);
1225             }
1226         },
1227         /**
1228          * 画面の横幅.
1229          * @type Number
1230          */
1231         width: {
1232             get: function() {
1233                 return this._width;
1234             },
1235             set: function(w) {
1236                 this._width = w;
1237                 this._dispatchCoreResizeEvent();
1238             }
1239         },
1240         /**
1241          * 画面の高さ.
1242          * @type Number
1243          */
1244         height: {
1245             get: function() {
1246                 return this._height;
1247             },
1248             set: function(h) {
1249                 this._height = h;
1250                 this._dispatchCoreResizeEvent();
1251             }
1252         },
1253         /**
1254          * 画面の表示倍率.
1255          * @type Number
1256          */
1257         scale: {
1258             get: function() {
1259                 return this._scale;
1260             },
1261             set: function(s) {
1262                 this._scale = s;
1263                 this._dispatchCoreResizeEvent();
1264             }
1265         },
1266         _dispatchCoreResizeEvent: function() {
1267             var e = new enchant.Event('coreresize');
1268             e.width = this._width;
1269             e.height = this._height;
1270             e.scale = this._scale;
1271             this.dispatchEvent(e);
1272         },
1273         _oncoreresize: function(e) {
1274             this._element.style.width = Math.floor(this._width * this._scale) + 'px';
1275             this._element.style.height = Math.floor(this._height * this._scale) + 'px';
1276             var scene;
1277             for (var i = 0, l = this._scenes.length; i < l; i++) {
1278                 scene = this._scenes[i];
1279                 scene.dispatchEvent(e);
1280             }
1281         },
1282         /**
1283          * ファイルのプリロードを行う.
1284          *
1285          * プリロードを行うよう設定されたファイルは {@link enchant.Core#start} が実行されるとき
1286          * ロードが行われる. 全てのファイルのロードが完了したときはCoreオブジェクトからload
1287          * イベントが発行され, Coreオブジェクトのassetsプロパティから画像ファイルの場合は
1288          * Surfaceオブジェクトとして, 音声ファイルの場合はSoundオブジェクトとして,
1289          * その他の場合は文字列としてアクセスできるようになる.
1290          *
1291          * なおこのSurfaceオブジェクトは {@link enchant.Surface.load} を使って作成されたものである
1292          * ため直接画像操作を行うことはできない. enchant.Surface.loadの項を参照.
1293          *
1294          * @example
1295          * core.preload('player.gif');
1296          * core.onload = function() {
1297          *     var sprite = new Sprite(32, 32);
1298          *     sprite.image = core.assets['player.gif']; // パス名でアクセス
1299          *     ...
1300          * };
1301          * core.start();
1302          *
1303          * @param {...String|String[]} assets プリロードするファイルのパス. 複数指定できる.
1304          * @return {enchant.Core} this
1305          */
1306         preload: function(assets) {
1307             var a;
1308             if (!(assets instanceof Array)) {
1309                 if (typeof assets === 'object') {
1310                     a = [];
1311                     for (var name in assets) {
1312                         if (assets.hasOwnProperty(name)) {
1313                             a.push([ assets[name], name ]);
1314                         }
1315                     }
1316                     assets = a;
1317                 } else {
1318                     assets = Array.prototype.slice.call(arguments);
1319                 }
1320             }
1321             Array.prototype.push.apply(this._assets, assets);
1322             return this;
1323         },
1324         /**
1325          * ファイルのロードを行う.
1326          *
1327          * @param {String} src ロードするファイルのパス.
1328          * @param {String} [alias] ロードするファイルに設定したい名前.
1329          * @param {Function} [callback] ファイルのロードが完了したときに呼び出される関数.
1330          * @param {Function} [onerror] ファイルのロードに失敗したときに呼び出される関数.
1331          * @return {enchant.Deferred} ファイル読み込み後に起動するDeferredオブジェクト.
1332          */
1333         load: function(src, alias, callback, onerror) {
1334             var assetName;
1335             if (typeof arguments[1] === 'string') {
1336                 assetName = alias;
1337                 callback = callback || function() {};
1338                 onerror = onerror || function() {};
1339             } else {
1340                 assetName = src;
1341                 var tempCallback = callback;
1342                 callback = arguments[1] || function() {};
1343                 onerror = tempCallback || function() {};
1344             }
1345 
1346             var ext = enchant.Core.findExt(src);
1347 
1348             return enchant.Deferred.next(function() {
1349                 var d = new enchant.Deferred();
1350                 var _callback = function(e) {
1351                     d.call(e);
1352                     callback.call(this, e);
1353                 };
1354                 var _onerror = function(e) {
1355                     d.fail(e);
1356                     onerror.call(this, e);
1357                 };
1358                 if (enchant.Core._loadFuncs[ext]) {
1359                     enchant.Core.instance.assets[assetName] = enchant.Core._loadFuncs[ext](src, ext, _callback, _onerror);
1360                 } else {
1361                     var req = new XMLHttpRequest();
1362                     req.open('GET', src, true);
1363                     req.onreadystatechange = function() {
1364                         if (req.readyState === 4) {
1365                             if (req.status !== 200 && req.status !== 0) {
1366                                 // throw new Error(req.status + ': ' + 'Cannot load an asset: ' + src);
1367                                 var e = new enchant.Event('error');
1368                                 e.message = req.status + ': ' + 'Cannot load an asset: ' + src;
1369                                 _onerror.call(enchant.Core.instance, e);
1370                             }
1371 
1372                             var type = req.getResponseHeader('Content-Type') || '';
1373                             if (type.match(/^image/)) {
1374                                 core.assets[assetName] = enchant.Surface.load(src, _callback, _onerror);
1375                             } else if (type.match(/^audio/)) {
1376                                 core.assets[assetName] = enchant.Sound.load(src, type, _callback, _onerror);
1377                             } else {
1378                                 core.assets[assetName] = req.responseText;
1379                                 _callback.call(enchant.Core.instance, new enchant.Event('load'));
1380                             }
1381                         }
1382                     };
1383                     req.send(null);
1384                 }
1385                 return d;
1386             });
1387         },
1388         /**
1389          * アプリを起動する.
1390          *
1391          * enchant.Core#fpsで設定されたフレームレートに従って {@link enchant.Core#currentScene} の
1392          * フレームの更新が行われるようになる. プリロードする画像が存在する場合はロードが
1393          * 始まりローディング画面が表示される.
1394          * @return {enchant.Deferred} ローディング画面終了後に起動するDeferredオブジェクト.
1395          */
1396         start: function(deferred) {
1397             var onloadTimeSetter = function() {
1398                 this.frame = 0;
1399                 this.removeEventListener('load', onloadTimeSetter);
1400             };
1401             this.addEventListener('load', onloadTimeSetter);
1402 
1403             this.currentTime = window.getTime();
1404             this.running = true;
1405             this.ready = true;
1406 
1407             if (!this._activated) {
1408                 this._activated = true;
1409                 if (enchant.ENV.BROWSER === 'mobilesafari' &&
1410                     enchant.ENV.USE_WEBAUDIO &&
1411                     enchant.ENV.USE_TOUCH_TO_START_SCENE) {
1412                     var d = new enchant.Deferred();
1413                     var scene = this._createTouchToStartScene();
1414                     scene.addEventListener(enchant.Event.TOUCH_START, function waitTouch() {
1415                         this.removeEventListener(enchant.Event.TOUCH_START, waitTouch);
1416                         var a = new enchant.WebAudioSound();
1417                         a.buffer = enchant.WebAudioSound.audioContext.createBuffer(1, 1, 48000);
1418                         a.play();
1419                         core.removeScene(scene);
1420                         core.start(d);
1421                     }, false);
1422                     core.pushScene(scene);
1423                     return d;
1424                 }
1425             }
1426 
1427             this._requestNextFrame(0);
1428 
1429             var ret = this._requestPreload()
1430                 .next(function() {
1431                     enchant.Core.instance.loadingScene.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
1432                 });
1433 
1434             if (deferred) {
1435                 ret.next(function(arg) {
1436                     deferred.call(arg);
1437                 })
1438                 .error(function(arg) {
1439                     deferred.fail(arg);
1440                 });
1441             }
1442 
1443             return ret;
1444         },
1445         _requestPreload: function() {
1446             var o = {};
1447             var loaded = 0,
1448                 len = 0,
1449                 loadFunc = function() {
1450                     var e = new enchant.Event('progress');
1451                     e.loaded = ++loaded;
1452                     e.total = len;
1453                     core.loadingScene.dispatchEvent(e);
1454                 };
1455             this._assets
1456                 .reverse()
1457                 .forEach(function(asset) {
1458                     var src, name;
1459                     if (asset instanceof Array) {
1460                         src = asset[0];
1461                         name = asset[1];
1462                     } else {
1463                         src = name = asset;
1464                     }
1465                     if (!o[name]) {
1466                         o[name] = this.load(src, name, loadFunc);
1467                         len++;
1468                     }
1469                 }, this);
1470 
1471             this.pushScene(this.loadingScene);
1472             return enchant.Deferred.parallel(o);
1473         },
1474         _createTouchToStartScene: function() {
1475             var label = new enchant.Label('Touch to Start'),
1476                 size = Math.round(core.width / 10),
1477                 scene = new enchant.Scene();
1478 
1479             label.color = '#fff';
1480             label.font = (size - 1) + 'px bold Helvetica,Arial,sans-serif';
1481             label.textAlign = 'center';
1482             label.width = core.width;
1483             label.height = label._boundHeight;
1484             label.y = (core.height - label.height) / 2;
1485 
1486             scene.backgroundColor = '#000';
1487             scene.addChild(label);
1488 
1489             return scene;
1490         },
1491         /**
1492          * アプリをデバッグモードで開始する.
1493          *
1494          * {@link enchant.Core#_debug} フラグを true にすることでもデバッグモードをオンにすることができる
1495          * @return {enchant.Deferred} ローディング画面終了後に起動するDeferredオブジェクト.
1496          */
1497         debug: function() {
1498             this._debug = true;
1499             return this.start();
1500         },
1501         actualFps: {
1502             get: function() {
1503                 return this._actualFps || this.fps;
1504             }
1505         },
1506         /**
1507          * 次のフレームの実行を要求する.
1508          * @param {Number} delay requestAnimationFrameを呼び出すまでの遅延時間.
1509          * @private
1510          */
1511         _requestNextFrame: function(delay) {
1512             if (!this.ready) {
1513                 return;
1514             }
1515             if (this.fps >= 60 || delay <= 16) {
1516                 this._calledTime = window.getTime();
1517                 window.requestAnimationFrame(this._callTick);
1518             } else {
1519                 setTimeout(function() {
1520                     var core = enchant.Core.instance;
1521                     core._calledTime = window.getTime();
1522                     window.requestAnimationFrame(core._callTick);
1523                 }, Math.max(0, delay));
1524             }
1525         },
1526         /**
1527          * Core#_tickを呼び出す.
1528          * @param {Number} time 呼び出し時の時間.
1529          * @private
1530          */
1531         _callTick: function(time) {
1532             enchant.Core.instance._tick(time);
1533         },
1534         _tick: function(time) {
1535             var e = new enchant.Event('enterframe');
1536             var now = window.getTime();
1537             var elapsed = e.elapsed = now - this.currentTime;
1538             this.currentTime = now;
1539 
1540             this._actualFps = elapsed > 0 ? (1000 / elapsed) : 0;
1541 
1542             var nodes = this.currentScene.childNodes.slice();
1543             var push = Array.prototype.push;
1544             while (nodes.length) {
1545                 var node = nodes.pop();
1546                 node.age++;
1547                 node.dispatchEvent(e);
1548                 if (node.childNodes) {
1549                     push.apply(nodes, node.childNodes);
1550                 }
1551             }
1552 
1553             this.currentScene.age++;
1554             this.currentScene.dispatchEvent(e);
1555             this.dispatchEvent(e);
1556 
1557             this.dispatchEvent(new enchant.Event('exitframe'));
1558             this.frame++;
1559             now = window.getTime();
1560             
1561             this._requestNextFrame(1000 / this.fps - (now - this._calledTime));
1562         },
1563         getTime: function() {
1564             return window.getTime();
1565         },
1566         /**
1567          * アプリを停止する.
1568          *
1569          * フレームは更新されず, ユーザの入力も受け付けなくなる.
1570          * {@link enchant.Core#resume} で再開できる.
1571          */
1572         stop: function() {
1573             this.ready = false;
1574             this.running = false;
1575         },
1576         /**
1577          * アプリを一時停止する.
1578          *
1579          * フレームは更新されず, ユーザの入力は受け付ける.
1580          * {@link enchant.Core#resume} で再開できる.
1581          */
1582         pause: function() {
1583             this.ready = false;
1584         },
1585         /**
1586          * アプリを再開する.
1587          */
1588         resume: function() {
1589             if (this.ready) {
1590                 return;
1591             }
1592             this.currentTime = window.getTime();
1593             this.ready = true;
1594             this.running = true;
1595             this._requestNextFrame(0);
1596         },
1597 
1598         /**
1599          * 新しいSceneに移行する.
1600          *
1601          * Sceneはスタック状に管理されており, 表示順序もスタックに積み上げられた順に従う.
1602          * enchant.Core#pushSceneを行うとSceneをスタックの一番上に積むことができる. スタックの
1603          * 一番上のSceneに対してはフレームの更新が行われる.
1604          *
1605          * @param {enchant.Scene} scene 移行する新しいScene.
1606          * @return {enchant.Scene} 新しいScene.
1607          */
1608         pushScene: function(scene) {
1609             this._element.appendChild(scene._element);
1610             if (this.currentScene) {
1611                 this.currentScene.dispatchEvent(new enchant.Event('exit'));
1612             }
1613             this.currentScene = scene;
1614             this.currentScene.dispatchEvent(new enchant.Event('enter'));
1615             return this._scenes.push(scene);
1616         },
1617         /**
1618          * 現在のSceneを終了させ前のSceneに戻る.
1619          *
1620          * Sceneはスタック状に管理されており, 表示順序もスタックに積み上げられた順に従う.
1621          * enchant.Core#popSceneを行うとスタックの一番上のSceneを取り出すことができる.
1622          *
1623          * @return {enchant.Scene} 終了させたScene.
1624          */
1625         popScene: function() {
1626             if (this.currentScene === this.rootScene) {
1627                 return this.currentScene;
1628             }
1629             this._element.removeChild(this.currentScene._element);
1630             this.currentScene.dispatchEvent(new enchant.Event('exit'));
1631             this.currentScene = this._scenes[this._scenes.length - 2];
1632             this.currentScene.dispatchEvent(new enchant.Event('enter'));
1633             return this._scenes.pop();
1634         },
1635         /**
1636          * 現在のSceneを別のSceneにおきかえる.
1637          *
1638          * {@link enchant.Core#popScene}, {@link enchant.Core#pushScene}を同時に行う.
1639          *
1640          * @param {enchant.Scene} scene おきかえるScene.
1641          * @return {enchant.Scene} 新しいScene.
1642          */
1643         replaceScene: function(scene) {
1644             this.popScene();
1645             return this.pushScene(scene);
1646         },
1647         /**
1648          * Sceneを削除する.
1649          *
1650          * Sceneスタック中からSceneを削除する.
1651          *
1652          * @param {enchant.Scene} scene 削除するScene.
1653          * @return {enchant.Scene} 削除したScene.
1654          */
1655         removeScene: function(scene) {
1656             if (this.currentScene === scene) {
1657                 return this.popScene();
1658             } else {
1659                 var i = this._scenes.indexOf(scene);
1660                 if (i !== -1) {
1661                     this._scenes.splice(i, 1);
1662                     this._element.removeChild(scene._element);
1663                     return scene;
1664                 } else {
1665                     return null;
1666                 }
1667             }
1668         },
1669         _buttonListener: function(e) {
1670             this.currentScene.dispatchEvent(e);
1671         },
1672         /**
1673          * キーバインドを設定する.
1674          *
1675          * @param {Number} key キーバインドを設定するキーコード.
1676          * @param {String} button 割り当てるボタン.
1677          * @return {enchant.Core} this
1678          */
1679         keybind: function(key, button) {
1680             this.keyboardInputManager.keybind(key, button);
1681             this.addEventListener(button + 'buttondown', this._buttonListener);
1682             this.addEventListener(button + 'buttonup', this._buttonListener);
1683             return this;
1684         },
1685         /**
1686          * キーバインドを削除する.
1687          * @param {Number} key 削除するキーコード.
1688          * @return {enchant.Core} this
1689          */
1690         keyunbind: function(key) {
1691             var button = this._keybind[key];
1692             this.keyboardInputManager.keyunbind(key);
1693             this.removeEventListener(button + 'buttondown', this._buttonListener);
1694             this.removeEventListener(button + 'buttonup', this._buttonListener);
1695             return this;
1696         },
1697         changeButtonState: function(button, bool) {
1698             this.keyboardInputManager.changeState(button, bool);
1699         },
1700         /**
1701          * Core#startが呼ばれてから経過した時間を取得する.
1702          * @return {Number} 経過した時間 (秒)
1703          */
1704         getElapsedTime: function() {
1705             return this.frame / this.fps;
1706         }
1707     });
1708 
1709     /**
1710      * 拡張子に対応したアセットのロード関数.
1711      * ロード関数はファイルのパス, 拡張子, コールバックを引数に取り,
1712      * 対応したクラスのインスタンスを返す必要がある.
1713      * コールバックはEvent.LOADとEvent.ERRORでハンドルする.
1714      * @static
1715      * @private
1716      * @type Object
1717      */
1718     enchant.Core._loadFuncs = {};
1719     enchant.Core._loadFuncs['jpg'] =
1720         enchant.Core._loadFuncs['jpeg'] =
1721             enchant.Core._loadFuncs['gif'] =
1722                 enchant.Core._loadFuncs['png'] =
1723                     enchant.Core._loadFuncs['bmp'] = function(src, ext, callback, onerror) {
1724                         return enchant.Surface.load(src, callback, onerror);
1725                     };
1726     enchant.Core._loadFuncs['mp3'] =
1727         enchant.Core._loadFuncs['aac'] =
1728             enchant.Core._loadFuncs['m4a'] =
1729                 enchant.Core._loadFuncs['wav'] =
1730                     enchant.Core._loadFuncs['ogg'] = function(src, ext, callback, onerror) {
1731                         return enchant.Sound.load(src, 'audio/' + ext, callback, onerror);
1732                     };
1733 
1734     /**
1735      * ファイルパスを取り, 拡張子を返す.
1736      * @param {String} path ファイルパス.
1737      * @return {*}
1738      */
1739     enchant.Core.findExt = function(path) {
1740         var matched = path.match(/\.\w+$/);
1741         if (matched && matched.length > 0) {
1742             return matched[0].slice(1).toLowerCase();
1743         }
1744 
1745         // for data URI
1746         if (path.indexOf('data:') === 0) {
1747             return path.split(/[\/;]/)[1].toLowerCase();
1748         }
1749         return null;
1750     };
1751 
1752     /**
1753      * 現在のCoreインスタンス.
1754      * @type enchant.Core
1755      * @static
1756      */
1757     enchant.Core.instance = null;
1758 }());
1759 
1760 /**
1761  * @name enchant.Game
1762  * @class
1763  * enchant.Game is moved to {@link enchant.Core} from v0.6
1764  * @deprecated
1765  */
1766 enchant.Game = enchant.Core;
1767 
1768 /**
1769  * @scope enchant.InputManager.prototype
1770  */
1771 enchant.InputManager = enchant.Class.create(enchant.EventTarget, {
1772     /**
1773      * @name enchant.InputManager
1774      * @class
1775      * 入力を管理するためのクラス.
1776      * @param {*} valueStore 入力の状態を保持させるオブジェクト.
1777      * @param {*} [source=this] イベントに付加される入力のソース.
1778      * @constructs
1779      * @extends enchant.EventTarget
1780      */
1781     initialize: function(valueStore, source) {
1782         enchant.EventTarget.call(this);
1783 
1784         /**
1785          * 入力の変化を通知する対象を保持する配列.
1786          * @type enchant.EventTarget[]
1787          */
1788         this.broadcastTarget = [];
1789         /**
1790          * 入力の状態を保持する連想配列.
1791          * @type Object
1792          */
1793         this.valueStore = valueStore;
1794         /**
1795          * イベントに付加される入力のソース.
1796          * @type Object
1797          */
1798         this.source = source || this;
1799 
1800         this._binds = {};
1801 
1802         this._stateHandler = function(e) {
1803             var id = e.source.identifier;
1804             var name = this._binds[id];
1805             this.changeState(name, e.data);
1806         }.bind(this);
1807     },
1808     /**
1809      * 特定の入力に名前をつける.
1810      * 入力はフラグとイベントで監視できるようになる.
1811      * @param {enchant.InputSource} inputSource {@link enchant.InputSource} のインスタンス.
1812      * @param {String} name 入力につける名前.
1813      */
1814     bind: function(inputSource, name) {
1815         inputSource.addEventListener(enchant.Event.INPUT_STATE_CHANGED, this._stateHandler);
1816         this._binds[inputSource.identifier] = name;
1817     },
1818     /**
1819      * 入力のバインドを解除する.
1820      * @param {enchant.InputSource} inputSource {@link enchant.InputSource} のインスタンス.
1821      */
1822     unbind: function(inputSource) {
1823         inputSource.removeEventListener(enchant.Event.INPUT_STATE_CHANGED, this._stateHandler);
1824         delete this._binds[inputSource.identifier];
1825     },
1826     /**
1827      * 入力の変化を通知する対象を追加する.
1828      * @param {enchant.EventTarget} eventTarget イベントの通知を設定したい対象.
1829      */
1830     addBroadcastTarget: function(eventTarget) {
1831         var i = this.broadcastTarget.indexOf(eventTarget);
1832         if (i === -1) {
1833             this.broadcastTarget.push(eventTarget);
1834         }
1835     },
1836     /**
1837      * 入力の変化を通知する対象を削除する.
1838      * @param {enchant.EventTarget} eventTarget イベントの通知を削除したい対象.
1839      */
1840     removeBroadcastTarget: function(eventTarget) {
1841         var i = this.broadcastTarget.indexOf(eventTarget);
1842         if (i !== -1) {
1843             this.broadcastTarget.splice(i, 1);
1844         }
1845     },
1846     /**
1847      * イベントを {@link enchant.InputManager#broadcastTarget} に発行する.
1848      * @param {enchant.Event} e イベント.
1849      */
1850     broadcastEvent: function(e) {
1851         var target = this.broadcastTarget;
1852         for (var i = 0, l = target.length; i < l; i++) {
1853             target[i].dispatchEvent(e);
1854         }
1855     },
1856     /**
1857      * 入力の状態を変更する.
1858      * @param {String} name 入力の名前.
1859      * @param {*} data 入力の状態.
1860      */
1861     changeState: function(name, data) {
1862     }
1863 });
1864 
1865 /**
1866  * @scope enchant.InputSource.prototype
1867  */
1868 enchant.InputSource = enchant.Class.create(enchant.EventTarget, {
1869     /**
1870      * @name enchant.InputSource
1871      * @class
1872      * 任意の入力をラップするクラス.
1873      * @param {String} identifier 入力のid.
1874      * @constructs
1875      * @extends enchant.EventTarget
1876      */
1877     initialize: function(identifier) {
1878         enchant.EventTarget.call(this);
1879         /**
1880          * 入力のid.
1881          * @type String
1882          */
1883         this.identifier = identifier;
1884     },
1885     /**
1886      * 入力の状態変更をイベントで通知する.
1887      * @param {*} data 新しい状態.
1888      */
1889     notifyStateChange: function(data) {
1890         var e = new enchant.Event(enchant.Event.INPUT_STATE_CHANGED);
1891         e.data = data;
1892         e.source = this;
1893         this.dispatchEvent(e);
1894     }
1895 });
1896 
1897 /**
1898  * @scope enchant.BinaryInputManager.prototype
1899  */
1900 enchant.BinaryInputManager = enchant.Class.create(enchant.InputManager, {
1901     /**
1902      * @name enchant.BinaryInputManager
1903      * @class
1904      * 入力を管理するためのクラス.
1905      * @param {*} flagStore 入力のフラグを保持させるオブジェクト.
1906      * @param {String} activeEventNameSuffix イベント名の接尾辞.
1907      * @param {String} inactiveEventNameSuffix イベント名の接尾辞.
1908      * @param {*} [source=this] イベントに付加される入力のソース.
1909      * @constructs
1910      * @extends enchant.InputManager
1911      */
1912     initialize: function(flagStore, activeEventNameSuffix, inactiveEventNameSuffix, source) {
1913         enchant.InputManager.call(this, flagStore, source);
1914         /**
1915          * アクティブな入力の数.
1916          * @type Number
1917          */
1918         this.activeInputsNum = 0;
1919         /**
1920          * BinaryInputManagerが発行するイベント名の接尾辞.
1921          * @type String
1922          */
1923         this.activeEventNameSuffix = activeEventNameSuffix;
1924         /**
1925          * BinaryInputManagerが発行するイベント名の接尾辞.
1926          * @type String
1927          */
1928         this.inactiveEventNameSuffix = inactiveEventNameSuffix;
1929     },
1930     /**
1931      * 特定の入力に名前をつける.
1932      * @param {enchant.BinaryInputSource} inputSource {@link enchant.InputSource}のインスタンス.
1933      * @param {String} name 入力につける名前.
1934      * @see enchant.InputManager#bind
1935      */
1936     bind: function(binaryInputSource, name) {
1937         enchant.InputManager.prototype.bind.call(this, binaryInputSource, name);
1938         this.valueStore[name] = false;
1939     },
1940     /**
1941      * 入力のバインドを解除する.
1942      * @param {enchant.BinaryInputSource} inputSource {@link enchant.InputSource}のインスタンス.
1943      * @see enchant.InputManager#unbind
1944      */
1945     unbind: function(binaryInputSource) {
1946         var name = this._binds[binaryInputSource.identifier];
1947         enchant.InputManager.prototype.unbind.call(this, binaryInputSource);
1948         delete this.valueStore[name];
1949     },
1950     /**
1951      * 入力の状態を変更する.
1952      * @param {String} name 入力の名前.
1953      * @param {Boolean} bool 入力の状態.
1954      */
1955     changeState: function(name, bool) {
1956         if (bool) {
1957             this._down(name);
1958         } else {
1959             this._up(name);
1960         }
1961     },
1962     _down: function(name) {
1963         var inputEvent;
1964         if (!this.valueStore[name]) {
1965             this.valueStore[name] = true;
1966             inputEvent = new enchant.Event((this.activeInputsNum++) ? 'inputchange' : 'inputstart');
1967             inputEvent.source = this.source;
1968             this.broadcastEvent(inputEvent);
1969         }
1970         var downEvent = new enchant.Event(name + this.activeEventNameSuffix);
1971         downEvent.source = this.source;
1972         this.broadcastEvent(downEvent);
1973     },
1974     _up: function(name) {
1975         var inputEvent;
1976         if (this.valueStore[name]) {
1977             this.valueStore[name] = false;
1978             inputEvent = new enchant.Event((--this.activeInputsNum) ? 'inputchange' : 'inputend');
1979             inputEvent.source = this.source;
1980             this.broadcastEvent(inputEvent);
1981         }
1982         var upEvent = new enchant.Event(name + this.inactiveEventNameSuffix);
1983         upEvent.source = this.source;
1984         this.broadcastEvent(upEvent);
1985     }
1986 });
1987 
1988 /**
1989  * @scope enchant.BinaryInputSource.prototype
1990  */
1991 enchant.BinaryInputSource = enchant.Class.create(enchant.InputSource, {
1992     /**
1993      * @name enchant.BinaryInputSource
1994      * @class
1995      * 任意の2値入力をラップするクラス.
1996      * @param {String} identifier 入力のid.
1997      * @constructs
1998      * @extends enchant.InputSource
1999      */
2000     initialize: function(identifier) {
2001         enchant.InputSource.call(this, identifier);
2002     }
2003 });
2004 
2005 /**
2006  * @scope enchant.KeyboardInputManager.prototype
2007  */
2008 enchant.KeyboardInputManager = enchant.Class.create(enchant.BinaryInputManager, {
2009     /**
2010      * @name enchant.KeyboardInputManager
2011      * @class
2012      * キーボード入力を管理するためのクラス.
2013      * @param {HTMLElement} dom element that will be watched.
2014      * @param {*} flagStore object that store input flag.
2015      * @constructs
2016      * @extends enchant.BinaryInputManager
2017      */
2018     initialize: function(domElement, flagStore) {
2019         enchant.BinaryInputManager.call(this, flagStore, 'buttondown', 'buttonup');
2020         this._attachDOMEvent(domElement, 'keydown', true);
2021         this._attachDOMEvent(domElement, 'keyup', false);
2022     },
2023     /**
2024      * キーコードに対応したBinaryInputSourceを使って{@link enchant.BinaryInputManager#bind} を呼び出す.
2025      * @param {Number} keyCode キーコード.
2026      * @param {String} name 入力の名前.
2027      */
2028     keybind: function(keyCode, name) {
2029         this.bind(enchant.KeyboardInputSource.getByKeyCode('' + keyCode), name);
2030     },
2031     /**
2032      * キーコードに対応したBinaryInputSourceを使って{@link enchant.BinaryInputManager#unbind} を呼び出す.
2033      * @param {Number} keyCode キーコード.
2034      */
2035     keyunbind: function(keyCode) {
2036         this.unbind(enchant.KeyboardInputSource.getByKeyCode('' + keyCode));
2037     },
2038     _attachDOMEvent: function(domElement, eventType, state) {
2039         domElement.addEventListener(eventType, function(e) {
2040             var core = enchant.Core.instance;
2041             if (!core || !core.running) {
2042                 return;
2043             }
2044             var code = e.keyCode;
2045             var source = enchant.KeyboardInputSource._instances[code];
2046             if (source) {
2047                 source.notifyStateChange(state);
2048             }
2049         }, true);
2050     }
2051 });
2052 
2053 /**
2054  * @scope enchant.KeyboardInputSource.prototype
2055  */
2056 enchant.KeyboardInputSource = enchant.Class.create(enchant.BinaryInputSource, {
2057     /**
2058      * @name enchant.KeyboardInputSource
2059      * @class
2060      * キーボード入力をラップするBinaryInputSource.
2061      * キーコードをidとして持つ.
2062      * @param {String} keyCode キーコード.
2063      * @constructs
2064      * @extends enchant.BinaryInputSource
2065      */
2066     initialize: function(keyCode) {
2067         enchant.BinaryInputSource.call(this, keyCode);
2068     }
2069 });
2070 /**
2071  * @private
2072  */
2073 enchant.KeyboardInputSource._instances = {};
2074 /**
2075  * @static
2076  * キーコードに対応したインスタンスを取得する.
2077  * @param {Number} keyCode キーコード.
2078  * @return {enchant.KeyboardInputSource} instance.
2079  */
2080 enchant.KeyboardInputSource.getByKeyCode = function(keyCode) {
2081     if (!this._instances[keyCode]) {
2082         this._instances[keyCode] = new enchant.KeyboardInputSource(keyCode);
2083     }
2084     return this._instances[keyCode];
2085 };
2086 
2087 /**
2088  * @scope enchant.Node.prototype
2089  */
2090 enchant.Node = enchant.Class.create(enchant.EventTarget, {
2091     /**
2092      * @name enchant.Node
2093      * @class
2094      * Sceneをルートとした表示オブジェクトツリーに属するオブジェクトの基底クラス.
2095      * 直接使用することはない.
2096      * @constructs
2097      * @extends enchant.EventTarget
2098      */
2099     initialize: function() {
2100         enchant.EventTarget.call(this);
2101 
2102         this._dirty = false;
2103 
2104         this._matrix = [ 1, 0, 0, 1, 0, 0 ];
2105 
2106         this._x = 0;
2107         this._y = 0;
2108         this._offsetX = 0;
2109         this._offsetY = 0;
2110 
2111         /**
2112          * Nodeが画面に表示されてから経過したフレーム数.
2113          * {@link enchant.Event.ENTER_FRAME} イベントを受け取る前にインクリメントされる.
2114          * (ENTER_FRAME イベントのリスナが初めて実行される時に 1 となる.)
2115          * @type Number
2116          */
2117         this.age = 0;
2118 
2119         /**
2120          * Nodeの親Node.
2121          * @type enchant.Group
2122          */
2123         this.parentNode = null;
2124         /**
2125          * Nodeが属しているScene.
2126          * @type enchant.Scene
2127          */
2128         this.scene = null;
2129 
2130         this.addEventListener('touchstart', function(e) {
2131             if (this.parentNode) {
2132                 this.parentNode.dispatchEvent(e);
2133             }
2134         });
2135         this.addEventListener('touchmove', function(e) {
2136             if (this.parentNode) {
2137                 this.parentNode.dispatchEvent(e);
2138             }
2139         });
2140         this.addEventListener('touchend', function(e) {
2141             if (this.parentNode) {
2142                 this.parentNode.dispatchEvent(e);
2143             }
2144         });
2145 
2146         // Nodeが生成される際に, tl プロパティに Timeline オブジェクトを追加している.
2147         if (enchant.ENV.USE_ANIMATION) {
2148             this.tl = new enchant.Timeline(this);
2149         }
2150     },
2151     /**
2152      * Nodeを移動する.
2153      * @param {Number} x 移動先のx座標.
2154      * @param {Number} y 移動先のy座標.
2155      */
2156     moveTo: function(x, y) {
2157         this.x = x;
2158         this.y = y;
2159     },
2160     /**
2161      * Nodeを移動する.
2162      * @param {Number} x 移動するx軸方向の距離.
2163      * @param {Number} y 移動するy軸方向の距離.
2164      */
2165     moveBy: function(x, y) {
2166         this.x += x;
2167         this.y += y;
2168     },
2169     /**
2170      * Nodeのx座標.
2171      * @type Number
2172      */
2173     x: {
2174         get: function() {
2175             return this._x;
2176         },
2177         set: function(x) {
2178             if(this._x !== x) {
2179                 this._x = x;
2180                 this._dirty = true;
2181             }
2182         }
2183     },
2184     /**
2185      * Nodeのy座標.
2186      * @type Number
2187      */
2188     y: {
2189         get: function() {
2190             return this._y;
2191         },
2192         set: function(y) {
2193             if(this._y !== y) {
2194                 this._y = y;
2195                 this._dirty = true;
2196             }
2197         }
2198     },
2199     _updateCoordinate: function() {
2200         var node = this;
2201         var tree = [ node ];
2202         var parent = node.parentNode;
2203         var scene = this.scene;
2204         while (parent && node._dirty) {
2205             tree.unshift(parent);
2206             node = node.parentNode;
2207             parent = node.parentNode;
2208         }
2209         var matrix = enchant.Matrix.instance;
2210         var stack = matrix.stack;
2211         var mat = [];
2212         var newmat, ox, oy;
2213         stack.push(tree[0]._matrix);
2214         for (var i = 1, l = tree.length; i < l; i++) {
2215             node = tree[i];
2216             newmat = [];
2217             matrix.makeTransformMatrix(node, mat);
2218             matrix.multiply(stack[stack.length - 1], mat, newmat);
2219             node._matrix = newmat;
2220             stack.push(newmat);
2221             ox = (typeof node._originX === 'number') ? node._originX : node._width / 2 || 0;
2222             oy = (typeof node._originY === 'number') ? node._originY : node._height / 2 || 0;
2223             var vec = [ ox, oy ];
2224             matrix.multiplyVec(newmat, vec, vec);
2225             node._offsetX = vec[0] - ox;
2226             node._offsetY = vec[1] - oy;
2227             node._dirty = false;
2228         }
2229         matrix.reset();
2230     },
2231     remove: function() {
2232         if (this.parentNode) {
2233             this.parentNode.removeChild(this);
2234         }
2235         if (this.childNodes) {
2236             var childNodes = this.childNodes.slice();
2237             for(var i = childNodes.length-1; i >= 0; i--) {
2238                 childNodes[i].remove();
2239             }
2240         }
2241         
2242         this.clearEventListener();
2243     }
2244 });
2245 
2246 var _intersectBetweenClassAndInstance = function(Class, instance) {
2247     var ret = [];
2248     var c;
2249     for (var i = 0, l = Class.collection.length; i < l; i++) {
2250         c = Class.collection[i];
2251         if (instance._intersectOne(c)) {
2252             ret.push(c);
2253         }
2254     }
2255     return ret;
2256 };
2257 
2258 var _intersectBetweenClassAndClass = function(Class1, Class2) {
2259     var ret = [];
2260     var c1, c2;
2261     for (var i = 0, l = Class1.collection.length; i < l; i++) {
2262         c1 = Class1.collection[i];
2263         for (var j = 0, ll = Class2.collection.length; j < ll; j++) {
2264             c2 = Class2.collection[j];
2265             if (c1._intersectOne(c2)) {
2266                 ret.push([ c1, c2 ]);
2267             }
2268         }
2269     }
2270     return ret;
2271 };
2272 
2273 var _intersectStrictBetweenClassAndInstance = function(Class, instance) {
2274     var ret = [];
2275     var c;
2276     for (var i = 0, l = Class.collection.length; i < l; i++) {
2277         c = Class.collection[i];
2278         if (instance._intersectStrictOne(c)) {
2279             ret.push(c);
2280         }
2281     }
2282     return ret;
2283 };
2284 
2285 var _intersectStrictBetweenClassAndClass = function(Class1, Class2) {
2286     var ret = [];
2287     var c1, c2;
2288     for (var i = 0, l = Class1.collection.length; i < l; i++) {
2289         c1 = Class1.collection[i];
2290         for (var j = 0, ll = Class2.collection.length; j < ll; j++) {
2291             c2 = Class2.collection[j];
2292             if (c1._intersectStrictOne(c2)) {
2293                 ret.push([ c1, c2 ]);
2294             }
2295         }
2296     }
2297     return ret;
2298 };
2299 
2300 var _staticIntersect = function(other) {
2301     if (other instanceof enchant.Entity) {
2302         return _intersectBetweenClassAndInstance(this, other);
2303     } else if (typeof other === 'function' && other.collection) {
2304         return _intersectBetweenClassAndClass(this, other);
2305     }
2306     return false;
2307 };
2308 
2309 var _staticIntersectStrict = function(other) {
2310     if (other instanceof enchant.Entity) {
2311         return _intersectStrictBetweenClassAndInstance(this, other);
2312     } else if (typeof other === 'function' && other.collection) {
2313         return _intersectStrictBetweenClassAndClass(this, other);
2314     }
2315     return false;
2316 };
2317 
2318 var _nodePrototypeClearEventListener = enchant.Node.prototype.clearEventListener;
2319 
2320 /**
2321  * @scope enchant.Entity.prototype
2322  */
2323 enchant.Entity = enchant.Class.create(enchant.Node, {
2324     /**
2325      * @name enchant.Entity
2326      * @class
2327      * DOM上で表示する実体を持ったクラス. 直接使用することはない.
2328      * @constructs
2329      * @extends enchant.Node
2330      */
2331     initialize: function() {
2332         var core = enchant.Core.instance;
2333         enchant.Node.call(this);
2334 
2335         this._rotation = 0;
2336         this._scaleX = 1;
2337         this._scaleY = 1;
2338 
2339         this._touchEnabled = true;
2340         this._clipping = false;
2341 
2342         this._originX = null;
2343         this._originY = null;
2344 
2345         this._width = 0;
2346         this._height = 0;
2347         this._backgroundColor = null;
2348         this._debugColor = '#0000ff';
2349         this._opacity = 1;
2350         this._visible = true;
2351         this._buttonMode = null;
2352 
2353         this._style = {};
2354         this.__styleStatus = {};
2355 
2356         this._isContainedInCollection = false;
2357 
2358         /**
2359          * Entityを描画する際の合成処理を設定する.
2360          * Canvas上に描画する際のみ有効.
2361          * CanvasのコンテキストのglobalCompositeOperationにセットされる.
2362          * @type String
2363          */
2364         this.compositeOperation = null;
2365 
2366         /**
2367          * Entityにボタンの機能を設定する.
2368          * Entityに対するタッチ, クリックをleft, right, up, down, a, bいずれかの
2369          * ボタン入力として割り当てる.
2370          * @type String
2371          */
2372         this.buttonMode = null;
2373         /**
2374          * Entityが押されているかどうか.
2375          * {@link enchant.Entity.buttonMode} が設定されているときだけ機能する.
2376          * @type Boolean
2377          */
2378         this.buttonPressed = false;
2379         this.addEventListener('touchstart', function() {
2380             if (!this.buttonMode) {
2381                 return;
2382             }
2383             this.buttonPressed = true;
2384             this.dispatchEvent(new enchant.Event(this.buttonMode + 'buttondown'));
2385             core.changeButtonState(this.buttonMode, true);
2386         });
2387         this.addEventListener('touchend', function() {
2388             if (!this.buttonMode) {
2389                 return;
2390             }
2391             this.buttonPressed = false;
2392             this.dispatchEvent(new enchant.Event(this.buttonMode + 'buttonup'));
2393             core.changeButtonState(this.buttonMode, false);
2394         });
2395 
2396         this.enableCollection();
2397     },
2398     /**
2399      * Entityの横幅.
2400      * @type Number
2401      */
2402     width: {
2403         get: function() {
2404             return this._width;
2405         },
2406         set: function(width) {
2407             if(this._width !== width) {
2408                 this._width = width;
2409                 this._dirty = true;
2410             }
2411         }
2412     },
2413     /**
2414      * Entityの高さ.
2415      * @type Number
2416      */
2417     height: {
2418         get: function() {
2419             return this._height;
2420         },
2421         set: function(height) {
2422             if(this._height !== height) {
2423                 this._height = height;
2424                 this._dirty = true;
2425             }
2426         }
2427     },
2428     /**
2429      * Entityの背景色.
2430      * CSSの'color'プロパティと同様の形式で指定できる.
2431      * @type String
2432      */
2433     backgroundColor: {
2434         get: function() {
2435             return this._backgroundColor;
2436         },
2437         set: function(color) {
2438             this._backgroundColor = color;
2439         }
2440     },
2441     /**
2442      * Entityのデバッグの枠色.
2443      * CSSの'color'プロパティと同様の形式で指定できる.
2444      * @type String
2445      */
2446     debugColor: {
2447         get: function() {
2448             return this._debugColor;
2449         },
2450         set: function(color) {
2451             this._debugColor = color;
2452         }
2453     },
2454     /**
2455      * Entityの透明度.
2456      * 0から1までの値を設定する(0が完全な透明, 1が完全な不透明).
2457      * @type Number
2458      */
2459     opacity: {
2460         get: function() {
2461             return this._opacity;
2462         },
2463         set: function(opacity) {
2464             this._opacity = parseFloat(opacity);
2465         }
2466     },
2467     /**
2468      * Entityを表示するかどうかを指定する.
2469      * @type Boolean
2470      */
2471     visible: {
2472         get: function() {
2473             return this._visible;
2474         },
2475         set: function(visible) {
2476             this._visible = visible;
2477         }
2478     },
2479     /**
2480      * Entityのタッチを有効にするかどうかを指定する.
2481      * @type Boolean
2482      */
2483     touchEnabled: {
2484         get: function() {
2485             return this._touchEnabled;
2486         },
2487         set: function(enabled) {
2488             this._touchEnabled = enabled;
2489             if (enabled) {
2490                 this._style.pointerEvents = 'all';
2491             } else {
2492                 this._style.pointerEvents = 'none';
2493             }
2494         }
2495     },
2496     /**
2497      * Entityの矩形が交差しているかどうかにより衝突判定を行う.
2498      * @param {*} other 衝突判定を行うEntityなどx, y, width, heightプロパティを持ったObject.
2499      * @return {Boolean} 衝突判定の結果.
2500      */
2501     intersect: function(other) {
2502         if (other instanceof enchant.Entity) {
2503             return this._intersectOne(other);
2504         } else if (typeof other === 'function' && other.collection) {
2505             return _intersectBetweenClassAndInstance(other, this);
2506         }
2507         return false;
2508     },
2509     _intersectOne: function(other) {
2510         if (this._dirty) {
2511             this._updateCoordinate();
2512         } if (other._dirty) {
2513             other._updateCoordinate();
2514         }
2515         return this._offsetX < other._offsetX + other.width && other._offsetX < this._offsetX + this.width &&
2516             this._offsetY < other._offsetY + other.height && other._offsetY < this._offsetY + this.height;
2517     },
2518     intersectStrict: function(other) {
2519         if (other instanceof enchant.Entity) {
2520             return this._intersectStrictOne(other);
2521         } else if (typeof other === 'function' && other.collection) {
2522             return _intersectStrictBetweenClassAndInstance(other, this);
2523         }
2524         return false;
2525     },
2526     _intersectStrictOne: function(other) {
2527         if (this._dirty) {
2528             this._updateCoordinate();
2529         } if (other._dirty) {
2530             other._updateCoordinate();
2531         }
2532         var rect1 = this.getOrientedBoundingRect(),
2533             rect2 = other.getOrientedBoundingRect(),
2534             lt1 = rect1.leftTop, rt1 = rect1.rightTop,
2535             lb1 = rect1.leftBottom, rb1 = rect1.rightBottom,
2536             lt2 = rect2.leftTop, rt2 = rect2.rightTop,
2537             lb2 = rect2.leftBottom, rb2 = rect2.rightBottom,
2538             ltx1 = lt1[0], lty1 = lt1[1], rtx1 = rt1[0], rty1 = rt1[1],
2539             lbx1 = lb1[0], lby1 = lb1[1], rbx1 = rb1[0], rby1 = rb1[1],
2540             ltx2 = lt2[0], lty2 = lt2[1], rtx2 = rt2[0], rty2 = rt2[1],
2541             lbx2 = lb2[0], lby2 = lb2[1], rbx2 = rb2[0], rby2 = rb2[1],
2542             t1 = [ rtx1 - ltx1, rty1 - lty1 ],
2543             r1 = [ rbx1 - rtx1, rby1 - rty1 ],
2544             b1 = [ lbx1 - rbx1, lby1 - rby1 ],
2545             l1 = [ ltx1 - lbx1, lty1 - lby1 ],
2546             t2 = [ rtx2 - ltx2, rty2 - lty2 ],
2547             r2 = [ rbx2 - rtx2, rby2 - rty2 ],
2548             b2 = [ lbx2 - rbx2, lby2 - rby2 ],
2549             l2 = [ ltx2 - lbx2, lty2 - lby2 ],
2550             cx1 = (ltx1 + rtx1 + lbx1 + rbx1) >> 2,
2551             cy1 = (lty1 + rty1 + lby1 + rby1) >> 2,
2552             cx2 = (ltx2 + rtx2 + lbx2 + rbx2) >> 2,
2553             cy2 = (lty2 + rty2 + lby2 + rby2) >> 2,
2554             i, j, poss1, poss2, dirs1, dirs2, pos1, pos2, dir1, dir2,
2555             px1, py1, px2, py2, dx1, dy1, dx2, dy2, vx, vy, c, c1, c2;
2556         if (t1[0] * (cy2 - lty1) - t1[1] * (cx2 - ltx1) > 0 &&
2557             r1[0] * (cy2 - rty1) - r1[1] * (cx2 - rtx1) > 0 &&
2558             b1[0] * (cy2 - rby1) - b1[1] * (cx2 - rbx1) > 0 &&
2559             l1[0] * (cy2 - lby1) - l1[1] * (cx2 - lbx1) > 0) {
2560             return true;
2561         } else if (t2[0] * (cy1 - lty2) - t2[1] * (cx1 - ltx2) > 0 &&
2562             r2[0] * (cy1 - rty2) - r2[1] * (cx1 - rtx2) > 0 &&
2563             b2[0] * (cy1 - rby2) - b2[1] * (cx1 - rbx2) > 0 &&
2564             l2[0] * (cy1 - lby2) - l2[1] * (cx1 - lbx2) > 0) {
2565             return true;
2566         } else {
2567             poss1 = [ lt1, rt1, rb1, lb1 ];
2568             poss2 = [ lt2, rt2, rb2, lb2 ];
2569             dirs1 = [ t1, r1, b1, l1 ];
2570             dirs2 = [ t2, r2, b2, l2 ];
2571             for (i = 0; i < 4; i++) {
2572                 pos1 = poss1[i];
2573                 px1 = pos1[0]; py1 = pos1[1];
2574                 dir1 = dirs1[i];
2575                 dx1 = dir1[0]; dy1 = dir1[1];
2576                 for (j = 0; j < 4; j++) {
2577                     pos2 = poss2[j];
2578                     px2 = pos2[0]; py2 = pos2[1];
2579                     dir2 = dirs2[j];
2580                     dx2 = dir2[0]; dy2 = dir2[1];
2581                     c = dx1 * dy2 - dy1 * dx2;
2582                     if (c !== 0) {
2583                         vx = px2 - px1;
2584                         vy = py2 - py1;
2585                         c1 = (vx * dy1 - vy * dx1) / c;
2586                         c2 = (vx * dy2 - vy * dx2) / c;
2587                         if (0 < c1 && c1 < 1 && 0 < c2 && c2 < 1) {
2588                             return true;
2589                         }
2590                     }
2591                 }
2592             }
2593             return false;
2594         }
2595     },
2596     /**
2597      * Entityの中心点どうしの距離により衝突判定を行う.
2598      * @param {*} other 衝突判定を行うEntityなどx, y, width, heightプロパティを持ったObject.
2599      * @param {Number} [distance] 衝突したと見なす最大の距離. デフォルト値は二つのEntityの横幅と高さの平均.
2600      * @return {Boolean} 衝突判定の結果.
2601      */
2602     within: function(other, distance) {
2603         if (this._dirty) {
2604             this._updateCoordinate();
2605         } if (other._dirty) {
2606             other._updateCoordinate();
2607         }
2608         if (distance == null) {
2609             distance = (this.width + this.height + other.width + other.height) / 4;
2610         }
2611         var _;
2612         return (_ = this._offsetX - other._offsetX + (this.width - other.width) / 2) * _ +
2613             (_ = this._offsetY - other._offsetY + (this.height - other.height) / 2) * _ < distance * distance;
2614     },
2615     /**
2616      * Entityを拡大縮小する.
2617      * @param {Number} x 拡大するx軸方向の倍率.
2618      * @param {Number} [y] 拡大するy軸方向の倍率.
2619      */
2620     scale: function(x, y) {
2621         this._scaleX *= x;
2622         this._scaleY *= (y != null) ? y : x;
2623         this._dirty = true;
2624     },
2625     /**
2626      * Entityを回転する.
2627      * @param {Number} deg 回転する角度 (度数法).
2628      */
2629     rotate: function(deg) {
2630         this.rotation += deg;
2631     },
2632     /**
2633      * Entityのx軸方向の倍率.
2634      * @type Number
2635      */
2636     scaleX: {
2637         get: function() {
2638             return this._scaleX;
2639         },
2640         set: function(scaleX) {
2641             if(this._scaleX !== scaleX) {
2642                 this._scaleX = scaleX;
2643                 this._dirty = true;
2644             }
2645         }
2646     },
2647     /**
2648      * Entityのy軸方向の倍率.
2649      * @type Number
2650      */
2651     scaleY: {
2652         get: function() {
2653             return this._scaleY;
2654         },
2655         set: function(scaleY) {
2656             if(this._scaleY !== scaleY) {
2657                 this._scaleY = scaleY;
2658                 this._dirty = true;
2659             }
2660         }
2661     },
2662     /**
2663      * Entityの回転角 (度数法).
2664      * @type Number
2665      */
2666     rotation: {
2667         get: function() {
2668             return this._rotation;
2669         },
2670         set: function(rotation) {
2671             if(this._rotation !== rotation) {
2672                 this._rotation = rotation;
2673                 this._dirty = true;
2674             }
2675         }
2676     },
2677     /**
2678      * 回転・拡大縮小の基準点のX座標
2679      * @type Number
2680      */
2681     originX: {
2682         get: function() {
2683             return this._originX;
2684         },
2685         set: function(originX) {
2686             if(this._originX !== originX) {
2687                 this._originX = originX;
2688                 this._dirty = true;
2689             }
2690         }
2691     },
2692     /**
2693      * 回転・拡大縮小の基準点のY座標
2694      * @type Number
2695      */
2696     originY: {
2697         get: function() {
2698             return this._originY;
2699         },
2700         set: function(originY) {
2701             if(this._originY !== originY) {
2702                 this._originY = originY;
2703                 this._dirty = true;
2704             }
2705         }
2706     },
2707     /**
2708      * インスタンスをコレクションの対象にする.
2709      * デフォルトで呼び出される.
2710      */
2711     enableCollection: function() {
2712         this.addEventListener('addedtoscene', this._addSelfToCollection);
2713         this.addEventListener('removedfromscene', this._removeSelfFromCollection);
2714         if (this.scene) {
2715             this._addSelfToCollection();
2716         }
2717     },
2718     /**
2719      * インスタンスをコレクションの対象から除外する.
2720      */
2721     disableCollection: function() {
2722         this.removeEventListener('addedtoscene', this._addSelfToCollection);
2723         this.removeEventListener('removedfromscene', this._removeSelfFromCollection);
2724         if (this.scene) {
2725             this._removeSelfFromCollection();
2726         }
2727     },
2728     /**#nocode+*/
2729     clearEventListener: function() {
2730         _nodePrototypeClearEventListener.apply(this,arguments);
2731         if (this.scene) {
2732             this._removeSelfFromCollection();
2733         }
2734     },
2735     /**#nocode-*/
2736     _addSelfToCollection: function() {
2737         if (this._isContainedInCollection) {
2738             return;
2739         }
2740 
2741         var Constructor = this.getConstructor();
2742         Constructor._collectionTarget.forEach(function(C) {
2743             C.collection.push(this);
2744         }, this);
2745 
2746         this._isContainedInCollection = true;
2747     },
2748     _removeSelfFromCollection: function() {
2749         if (!this._isContainedInCollection) {
2750             return;
2751         }
2752 
2753         var Constructor = this.getConstructor();
2754         Constructor._collectionTarget.forEach(function(C) {
2755             var i = C.collection.indexOf(this);
2756             if (i !== -1) {
2757                 C.collection.splice(i, 1);
2758             }
2759         }, this);
2760 
2761         this._isContainedInCollection = false;
2762     },
2763     getBoundingRect: function() {
2764         var w = this.width || 0;
2765         var h = this.height || 0;
2766         var mat = this._matrix;
2767         var m11w = mat[0] * w, m12w = mat[1] * w,
2768             m21h = mat[2] * h, m22h = mat[3] * h,
2769             mdx = mat[4], mdy = mat[5];
2770         var xw = [ mdx, m11w + mdx, m21h + mdx, m11w + m21h + mdx ].sort(function(a, b) { return a - b; });
2771         var yh = [ mdy, m12w + mdy, m22h + mdy, m12w + m22h + mdy ].sort(function(a, b) { return a - b; });
2772 
2773         return {
2774             left: xw[0],
2775             top: yh[0],
2776             width: xw[3] - xw[0],
2777             height: yh[3] - yh[0]
2778         };
2779     },
2780     getOrientedBoundingRect: function() {
2781         var w = this.width || 0;
2782         var h = this.height || 0;
2783         var mat = this._matrix;
2784         var m11w = mat[0] * w, m12w = mat[1] * w,
2785             m21h = mat[2] * h, m22h = mat[3] * h,
2786             mdx = mat[4], mdy = mat[5];
2787 
2788         return {
2789             leftTop: [ mdx, mdy ],
2790             rightTop: [ m11w + mdx, m12w + mdy ],
2791             leftBottom: [ m21h + mdx, m22h + mdy ],
2792             rightBottom: [ m11w + m21h + mdx, m12w + m22h + mdy ]
2793         };
2794     },
2795     getConstructor: function() {
2796         return Object.getPrototypeOf(this).constructor;
2797     }
2798 });
2799 
2800 var _collectizeConstructor = function(Constructor) {
2801     if (Constructor._collective) {
2802         return;
2803     }
2804     var rel = enchant.Class.getInheritanceTree(Constructor);
2805     var i = rel.indexOf(enchant.Entity);
2806     if (i !== -1) {
2807         Constructor._collectionTarget = rel.splice(0, i + 1);
2808     } else {
2809         Constructor._collectionTarget = [];
2810     }
2811     Constructor.intersect = _staticIntersect;
2812     Constructor.intersectStrict = _staticIntersectStrict;
2813     Constructor.collection = [];
2814     Constructor._collective = true;
2815 };
2816 
2817 _collectizeConstructor(enchant.Entity);
2818 
2819 enchant.Entity._inherited = function(subclass) {
2820     _collectizeConstructor(subclass);
2821 };
2822 
2823 /**
2824  * @scope enchant.Sprite.prototype
2825  */
2826 enchant.Sprite = enchant.Class.create(enchant.Entity, {
2827     /**
2828      * @name enchant.Sprite
2829      * @class
2830      * 画像表示機能を持ったクラス. Entity を継承している.
2831      * @param {Number} width Spriteの横幅.
2832      * @param {Number} height Spriteの高さ.
2833      *
2834      * @example
2835      * var bear = new Sprite(32, 32);
2836      * bear.image = core.assets['chara1.gif'];
2837      *
2838      * @constructs
2839      * @extends enchant.Entity
2840      */
2841     initialize: function(width, height) {
2842         enchant.Entity.call(this);
2843 
2844         this.width = width;
2845         this.height = height;
2846         this._image = null;
2847         this._debugColor = '#ff0000';
2848         this._frameLeft = 0;
2849         this._frameTop = 0;
2850         this._frame = 0;
2851         this._frameSequence = null;
2852     },
2853     /**
2854      * Spriteで表示する画像.
2855      * @type enchant.Surface
2856      */
2857     image: {
2858         get: function() {
2859             return this._image;
2860         },
2861         set: function(image) {
2862             if (image === undefined) {
2863                 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.');
2864             }
2865             if (image === this._image) {
2866                 return;
2867             }
2868             this._image = image;
2869             this._computeFramePosition();
2870         }
2871     },
2872     /**
2873      * 表示するフレームのインデックス.
2874      * Spriteと同じ横幅と高さを持ったフレームが{@link enchant.Sprite#image}プロパティの画像に左上から順に
2875      * 配列されていると見て, 0から始まるインデックスを指定することでフレームを切り替える.
2876      *
2877      * 数値の配列が指定された場合, それらを毎フレーム順に切り替える.
2878      * ループするが, null値が含まれているとそこでループをストップする.
2879      *
2880      * @example
2881      * var sprite = new Sprite(32, 32);
2882      * sprite.frame = [0, 1, 0, 2]
2883      * //-> 0, 1, 0, 2, 0, 1, 0, 2,..
2884      * sprite.frame = [0, 1, 0, 2, null]
2885      * //-> 0, 1, 0, 2, (2, 2,.. :stop)
2886      *
2887      * @type Number|Array
2888      */
2889     frame: {
2890         get: function() {
2891             return this._frame;
2892         },
2893         set: function(frame) {
2894             if (((this._frameSequence == null) && (this._frame === frame)) || (this._deepCompareToPreviousFrame(frame))) {
2895                 return;
2896             }
2897             if (frame instanceof Array) {
2898                 this._frameSequence = frame;
2899             } else {
2900                 this._frameSequence = null;
2901                 this._frame = frame;
2902                 this._computeFramePosition();
2903             }
2904         }
2905     },
2906     _frameSequence: {
2907         get: function() {
2908             return this.__frameSequence;
2909         },
2910         set: function(frameSequence) {
2911             if(frameSequence && !this.__frameSequence) {
2912                 this.addEventListener(enchant.Event.ENTER_FRAME, this._rotateFrameSequence);
2913             } else if(!frameSequence && this.__frameSequence) {
2914                 this.removeEventListener(enchant.Event.ENTER_FRAME, this._rotateFrameSequence);
2915             }
2916             if(frameSequence) {
2917                 this.__frameSequence = frameSequence.slice();
2918                 this._originalFrameSequence = frameSequence.slice();
2919                 this._rotateFrameSequence();
2920             } else {
2921                 this.__frameSequence = null;
2922                 this._originalFrameSequence = null;
2923             }
2924         }
2925     },
2926     /**
2927      * @private
2928      */
2929     _deepCompareToPreviousFrame: function(frameArray) {
2930         if (frameArray === this._originalFrameSequence) {
2931             return true;
2932         }
2933         if (frameArray == null || this._originalFrameSequence == null) {
2934             return false;
2935         }
2936         if (!(frameArray instanceof Array)) {
2937             return false;
2938         }
2939         if (frameArray.length !== this._originalFrameSequence.length) {
2940             return false;
2941         }
2942         for (var i = 0; i < frameArray.length; ++i) {
2943             if (frameArray[i] !== this._originalFrameSequence[i]){
2944                 return false;
2945             }
2946         }
2947         return true;
2948     },
2949     /**
2950      * 0 <= frame
2951      * 0以下の動作は未定義.
2952      * @private
2953      */
2954     _computeFramePosition: function() {
2955         var image = this._image;
2956         var row;
2957         if (image != null) {
2958             row = image.width / this._width | 0;
2959             this._frameLeft = (this._frame % row | 0) * this._width;
2960             this._frameTop = (this._frame / row | 0) * this._height % image.height;
2961         }
2962     },
2963     _rotateFrameSequence: function() {
2964         var frameSequence = this._frameSequence;
2965         if (frameSequence && frameSequence.length !== 0) {
2966             var nextFrame = frameSequence.shift();
2967             if (nextFrame === null) {
2968                 this._frameSequence = null;
2969                 this.dispatchEvent(new enchant.Event(enchant.Event.ANIMATION_END));
2970             } else {
2971                 this._frame = nextFrame;
2972                 this._computeFramePosition();
2973                 frameSequence.push(nextFrame);
2974             }
2975         }
2976     },
2977     /**#nocode+*/
2978     width: {
2979         get: function() {
2980             return this._width;
2981         },
2982         set: function(width) {
2983             this._width = width;
2984             this._computeFramePosition();
2985             this._dirty = true;
2986         }
2987     },
2988     height: {
2989         get: function() {
2990             return this._height;
2991         },
2992         set: function(height) {
2993             this._height = height;
2994             this._computeFramePosition();
2995             this._dirty = true;
2996         }
2997     },
2998     /**#nocode-*/
2999     cvsRender: function(ctx) {
3000         var image = this._image,
3001             w = this._width, h = this._height,
3002             iw, ih, elem, sx, sy, sw, sh;
3003         if (image && w !== 0 && h !== 0) {
3004             iw = image.width;
3005             ih = image.height;
3006             if (iw < w || ih < h) {
3007                 ctx.fillStyle = enchant.Surface._getPattern(image);
3008                 ctx.fillRect(0, 0, w, h);
3009             } else {
3010                 elem = image._element;
3011                 sx = this._frameLeft;
3012                 sy = Math.min(this._frameTop, ih - h);
3013                 // IE9 doesn't allow for negative or 0 widths/heights when drawing on the CANVAS element
3014                 sw = Math.max(0.01, Math.min(iw - sx, w));
3015                 sh = Math.max(0.01, Math.min(ih - sy, h));
3016                 ctx.drawImage(elem, sx, sy, sw, sh, 0, 0, w, h);
3017             }
3018         }
3019     },
3020     domRender: (function() {
3021         if (enchant.ENV.VENDOR_PREFIX === 'ms') {
3022             return function(element) {
3023                 if (this._image) {
3024                     if (this._image._css) {
3025                         this._style['background-image'] = this._image._css;
3026                         this._style['background-position'] =
3027                             -this._frameLeft + 'px ' +
3028                             -this._frameTop + 'px';
3029                     } else if (this._image._element) {
3030                     }
3031                 }
3032             };
3033         } else {
3034             return function(element) {
3035                 if (this._image) {
3036                     if (this._image._css) {
3037                         this._style['background-image'] = this._image._css;
3038                         this._style['background-position'] =
3039                             -this._frameLeft + 'px ' +
3040                             -this._frameTop + 'px';
3041                     } else if (this._image._element) {
3042                     }
3043                 }
3044             };
3045         }
3046     }())
3047 });
3048 
3049 /**
3050  * @scope enchant.Label.prototype
3051  */
3052 enchant.Label = enchant.Class.create(enchant.Entity, {
3053     /**
3054      * @name enchant.Label
3055      * @class
3056      * Label クラス.
3057      * @constructs
3058      * @extends enchant.Entity
3059      */
3060     initialize: function(text) {
3061         enchant.Entity.call(this);
3062 
3063         this.text = text || '';
3064         this.width = 300;
3065         this.font = '14px serif';
3066         this.textAlign = 'left';
3067 
3068         this._debugColor = '#ff0000';
3069     },
3070     /**#nocode+*/
3071     width: {
3072         get: function() {
3073             return this._width;
3074         },
3075         set: function(width) {
3076             this._width = width;
3077             this._dirty = true;
3078             // issue #164
3079             this.updateBoundArea();
3080         }
3081     },
3082     /**#nocode-*/
3083     /**
3084      * 表示するテキスト.
3085      * DOM レンダラを利用している場合 (DOMScene 以下にある場合) 改行タグ (br) も利用できますが,
3086      * ユーザから入力したり, サーバから取得した文字列を表示する場合, XSS 脆弱性などに注意してください.
3087      * Canvas レンダラを利用できる場合でも, 改行タグ (br, BR) は改行に変換されます.
3088      * @type String
3089      */
3090     text: {
3091         get: function() {
3092             return this._text;
3093         },
3094         set: function(text) {
3095             text = '' + text;
3096             if(this._text === text) {
3097                 return;
3098             }
3099             this._text = text;
3100             text = text.replace(/<br ?\/?>/gi, '<br/>');
3101             this._splitText = text.split('<br/>');
3102             this.updateBoundArea();
3103             for (var i = 0, l = this._splitText.length; i < l; i++) {
3104                 text = this._splitText[i];
3105                 var metrics = this.getMetrics(text);
3106                 this._splitText[i] = {};
3107                 this._splitText[i].text = text;
3108                 this._splitText[i].height = metrics.height;
3109                 this._splitText[i].width = metrics.width;
3110             }
3111         }
3112     },
3113     /**
3114      * テキストの水平位置の指定.
3115      * CSSの'text-align'プロパティと同様の形式で指定できる.
3116      * @type String
3117      */
3118     textAlign: {
3119         get: function() {
3120             return this._style['text-align'];
3121         },
3122         set: function(textAlign) {
3123             this._style['text-align'] = textAlign;
3124             this.updateBoundArea();
3125         }
3126     },
3127     /**
3128      * フォントの指定.
3129      * CSSの'font'プロパティと同様の形式で指定できる.
3130      * @type String
3131      */
3132     font: {
3133         get: function() {
3134             return this._style.font;
3135         },
3136         set: function(font) {
3137             this._style.font = font;
3138             this.updateBoundArea();
3139         }
3140     },
3141     /**
3142      * 文字色の指定.
3143      * CSSの'color'プロパティと同様の形式で指定できる.
3144      * @type String
3145      */
3146     color: {
3147         get: function() {
3148             return this._style.color;
3149         },
3150         set: function(color) {
3151             this._style.color = color;
3152         }
3153     },
3154     cvsRender: function(ctx) {
3155         var x, y = 0;
3156         var labelWidth = this.width;
3157         var charWidth, amount, line, text, c, buf, increase, length;
3158         var bufWidth;
3159         if (this._splitText) {
3160             ctx.textBaseline = 'top';
3161             ctx.font = this.font;
3162             ctx.fillStyle = this.color || '#000000';
3163             charWidth = ctx.measureText(' ').width;
3164             amount = labelWidth / charWidth;
3165             for (var i = 0, l = this._splitText.length; i < l; i++) {
3166                 line = this._splitText[i];
3167                 text = line.text;
3168                 c = 0;
3169                 while (text.length > c + amount || ctx.measureText(text.slice(c, c + amount)).width > labelWidth) {
3170                     buf = '';
3171                     increase = amount;
3172                     length = 0;
3173                     while (increase > 0) {
3174                         if (ctx.measureText(buf).width < labelWidth) {
3175                             length += increase;
3176                             buf = text.slice(c, c + length);
3177                         } else {
3178                             length -= increase;
3179                             buf = text.slice(c, c + length);
3180                         }
3181                         increase = increase / 2 | 0;
3182                     }
3183                     ctx.fillText(buf, 0, y);
3184                     y += line.height - 1;
3185                     c += length;
3186                 }
3187                 buf = text.slice(c, c + text.length);
3188                 if (this.textAlign === 'right') {
3189                     x = labelWidth - ctx.measureText(buf).width;
3190                 } else if (this.textAlign === 'center') {
3191                     x = (labelWidth - ctx.measureText(buf).width) / 2;
3192                 } else {
3193                     x = 0;
3194                 }
3195                 ctx.fillText(buf, x, y);
3196                 y += line.height - 1;
3197             }
3198         }
3199     },
3200     domRender: function(element) {
3201         if (element.innerHTML !== this._text) {
3202             element.innerHTML = this._text;
3203         }
3204     },
3205     detectRender: function(ctx) {
3206         ctx.fillRect(this._boundOffset, 0, this._boundWidth, this._boundHeight);
3207     },
3208     updateBoundArea: function() {
3209         var metrics = this.getMetrics();
3210         this._boundWidth = metrics.width;
3211         this._boundHeight = metrics.height;
3212         if (this.textAlign === 'right') {
3213             this._boundOffset = this.width - this._boundWidth;
3214         } else if (this.textAlign === 'center') {
3215             this._boundOffset = (this.width - this._boundWidth) / 2;
3216         } else {
3217             this._boundOffset = 0;
3218         }
3219     },
3220     getMetrics: function(text) {
3221         var ret = {};
3222         var div, width, height;
3223         if (document.body) {
3224             div = document.createElement('div');
3225             for (var prop in this._style) {
3226                 if(prop !== 'width' && prop !== 'height') {
3227                     div.style[prop] = this._style[prop];
3228                 }
3229             }
3230             text = text || this._text;
3231             div.innerHTML = text.replace(/ /g, ' ');
3232             div.style.whiteSpace = 'noWrap';
3233             div.style.lineHeight = 1;
3234             document.body.appendChild(div);
3235             var computedStyle = getComputedStyle(div);
3236             ret.height = parseInt(computedStyle.height, 10) + 1;
3237             div.style.position = 'absolute';
3238             ret.width = parseInt(computedStyle.width, 10) + 1;
3239             document.body.removeChild(div);
3240         } else {
3241             ret.width = this.width;
3242             ret.height = this.height;
3243         }
3244         return ret;
3245     }
3246 });
3247 
3248 /**
3249  * @scope enchant.Map.prototype
3250  */
3251 enchant.Map = enchant.Class.create(enchant.Entity, {
3252     /**
3253      * @name enchant.Map
3254      * @class
3255      * タイルセットからマップを生成して表示するクラス.
3256      * @param {Number} tileWidth タイルの横幅.
3257      * @param {Number} tileHeight タイルの高さ.
3258      * @constructs
3259      * @extends enchant.Entity
3260      */
3261     initialize: function(tileWidth, tileHeight) {
3262         var core = enchant.Core.instance;
3263 
3264         enchant.Entity.call(this);
3265 
3266         var surface = new enchant.Surface(core.width, core.height);
3267         this._surface = surface;
3268         var canvas = surface._element;
3269         canvas.style.position = 'absolute';
3270         if (enchant.ENV.RETINA_DISPLAY && core.scale === 2) {
3271             canvas.width = core.width * 2;
3272             canvas.height = core.height * 2;
3273             this._style.webkitTransformOrigin = '0 0';
3274             this._style.webkitTransform = 'scale(0.5)';
3275         } else {
3276             canvas.width = core.width;
3277             canvas.height = core.height;
3278         }
3279         this._context = canvas.getContext('2d');
3280 
3281         this._tileWidth = tileWidth || 0;
3282         this._tileHeight = tileHeight || 0;
3283         this._image = null;
3284         this._data = [
3285             [
3286                 []
3287             ]
3288         ];
3289         this._dirty = false;
3290         this._tight = false;
3291 
3292         this.touchEnabled = false;
3293 
3294         /**
3295          * タイルが衝突判定を持つかを表す値の二元配列.
3296          * @type Number[][]
3297          */
3298         this.collisionData = null;
3299 
3300         this._listeners['render'] = null;
3301         this.addEventListener('render', function() {
3302             if(this._dirty) {
3303                 this._previousOffsetX = this._previousOffsetY = null;
3304             }
3305         });
3306     },
3307     /**
3308      * データを設定する.
3309      * タイルががimageプロパティの画像に左上から順に配列されていると見て, 0から始まる
3310      * インデックスの二元配列を設定する.複数指定された場合は後のものから順に表示される.
3311      * @param {...Number[][]} data タイルのインデックスの二元配列. 複数指定できる.
3312      */
3313     loadData: function(data) {
3314         this._data = Array.prototype.slice.apply(arguments);
3315         this._dirty = true;
3316 
3317         this._tight = false;
3318         for (var i = 0, len = this._data.length; i < len; i++) {
3319             var c = 0;
3320             data = this._data[i];
3321             for (var y = 0, l = data.length; y < l; y++) {
3322                 for (var x = 0, ll = data[y].length; x < ll; x++) {
3323                     if (data[y][x] >= 0) {
3324                         c++;
3325                     }
3326                 }
3327             }
3328             if (c / (data.length * data[0].length) > 0.2) {
3329                 this._tight = true;
3330                 break;
3331             }
3332         }
3333     },
3334     /**
3335      * ある座標のタイルが何か調べる.
3336      * @param {Number} x マップ上の点のx座標.
3337      * @param {Number} y マップ上の点のy座標.
3338      * @return {*} ある座標のタイルのデータ.
3339      */
3340     checkTile: function(x, y) {
3341         if (x < 0 || this.width <= x || y < 0 || this.height <= y) {
3342             return false;
3343         }
3344         var width = this._image.width;
3345         var height = this._image.height;
3346         var tileWidth = this._tileWidth || width;
3347         var tileHeight = this._tileHeight || height;
3348         x = x / tileWidth | 0;
3349         y = y / tileHeight | 0;
3350         //		return this._data[y][x];
3351         var data = this._data[0];
3352         return data[y][x];
3353     },
3354     /**
3355      * Map上に障害物があるかどうかを判定する.
3356      * @param {Number} x 判定を行うマップ上の点のx座標.
3357      * @param {Number} y 判定を行うマップ上の点のy座標.
3358      * @return {Boolean} 障害物があるかどうか.
3359      */
3360     hitTest: function(x, y) {
3361         if (x < 0 || this.width <= x || y < 0 || this.height <= y) {
3362             return false;
3363         }
3364         var width = this._image.width;
3365         var height = this._image.height;
3366         var tileWidth = this._tileWidth || width;
3367         var tileHeight = this._tileHeight || height;
3368         x = x / tileWidth | 0;
3369         y = y / tileHeight | 0;
3370         if (this.collisionData != null) {
3371             return this.collisionData[y] && !!this.collisionData[y][x];
3372         } else {
3373             for (var i = 0, len = this._data.length; i < len; i++) {
3374                 var data = this._data[i];
3375                 var n;
3376                 if (data[y] != null && (n = data[y][x]) != null &&
3377                     0 <= n && n < (width / tileWidth | 0) * (height / tileHeight | 0)) {
3378                     return true;
3379                 }
3380             }
3381             return false;
3382         }
3383     },
3384     /**
3385      * Mapで表示するタイルセット画像.
3386      * @type enchant.Surface
3387      */
3388     image: {
3389         get: function() {
3390             return this._image;
3391         },
3392         set: function(image) {
3393             var core = enchant.Core.instance;
3394 
3395             this._image = image;
3396             if (enchant.ENV.RETINA_DISPLAY && core.scale === 2) {
3397                 var img = new enchant.Surface(image.width * 2, image.height * 2);
3398                 var tileWidth = this._tileWidth || image.width;
3399                 var tileHeight = this._tileHeight || image.height;
3400                 var row = image.width / tileWidth | 0;
3401                 var col = image.height / tileHeight | 0;
3402                 for (var y = 0; y < col; y++) {
3403                     for (var x = 0; x < row; x++) {
3404                         img.draw(image, x * tileWidth, y * tileHeight, tileWidth, tileHeight,
3405                             x * tileWidth * 2, y * tileHeight * 2, tileWidth * 2, tileHeight * 2);
3406                     }
3407                 }
3408                 this._doubledImage = img;
3409             }
3410             this._dirty = true;
3411         }
3412     },
3413     /**
3414      * Mapのタイルの横幅.
3415      * @type Number
3416      */
3417     tileWidth: {
3418         get: function() {
3419             return this._tileWidth;
3420         },
3421         set: function(tileWidth) {
3422             if(this._tileWidth !== tileWidth) {
3423                 this._tileWidth = tileWidth;
3424                 this._dirty = true;
3425             }
3426         }
3427     },
3428     /**
3429      * Mapのタイルの高さ.
3430      * @type Number
3431      */
3432     tileHeight: {
3433         get: function() {
3434             return this._tileHeight;
3435         },
3436         set: function(tileHeight) {
3437             if(this._tileHeight !== tileHeight) {
3438                 this._tileHeight = tileHeight;
3439                 this._dirty = true;
3440             }
3441         }
3442     },
3443     /**
3444      * @private
3445      */
3446     width: {
3447         get: function() {
3448             return this._tileWidth * this._data[0][0].length;
3449         }
3450     },
3451     /**
3452      * @private
3453      */
3454     height: {
3455         get: function() {
3456             return this._tileHeight * this._data[0].length;
3457         }
3458     },
3459     /**
3460      * @private
3461      */
3462     redraw: function(x, y, width, height) {
3463         if (this._image == null) {
3464             return;
3465         }
3466 
3467         var image, tileWidth, tileHeight, dx, dy;
3468         if (this._doubledImage) {
3469             image = this._doubledImage;
3470             tileWidth = this._tileWidth * 2;
3471             tileHeight = this._tileHeight * 2;
3472             dx = -this._offsetX * 2;
3473             dy = -this._offsetY * 2;
3474             x *= 2;
3475             y *= 2;
3476             width *= 2;
3477             height *= 2;
3478         } else {
3479             image = this._image;
3480             tileWidth = this._tileWidth;
3481             tileHeight = this._tileHeight;
3482             dx = -this._offsetX;
3483             dy = -this._offsetY;
3484         }
3485         var row = image.width / tileWidth | 0;
3486         var col = image.height / tileHeight | 0;
3487         var left = Math.max((x + dx) / tileWidth | 0, 0);
3488         var top = Math.max((y + dy) / tileHeight | 0, 0);
3489         var right = Math.ceil((x + dx + width) / tileWidth);
3490         var bottom = Math.ceil((y + dy + height) / tileHeight);
3491 
3492         var source = image._element;
3493         var context = this._context;
3494         var canvas = context.canvas;
3495         context.clearRect(x, y, width, height);
3496         for (var i = 0, len = this._data.length; i < len; i++) {
3497             var data = this._data[i];
3498             var r = Math.min(right, data[0].length);
3499             var b = Math.min(bottom, data.length);
3500             for (y = top; y < b; y++) {
3501                 for (x = left; x < r; x++) {
3502                     var n = data[y][x];
3503                     if (0 <= n && n < row * col) {
3504                         var sx = (n % row) * tileWidth;
3505                         var sy = (n / row | 0) * tileHeight;
3506                         context.drawImage(source, sx, sy, tileWidth, tileHeight,
3507                             x * tileWidth - dx, y * tileHeight - dy, tileWidth, tileHeight);
3508                     }
3509                 }
3510             }
3511         }
3512     },
3513     /**
3514      * @private
3515      */
3516     updateBuffer: function() {
3517         if (this._visible === undefined || this._visible) {
3518             var core = enchant.Core.instance;
3519             if (this._dirty || this._previousOffsetX == null) {
3520                 this.redraw(0, 0, core.width, core.height);
3521             } else if (this._offsetX !== this._previousOffsetX ||
3522                     this._offsetY !== this._previousOffsetY) {
3523                 if (this._tight) {
3524                     var x = -this._offsetX;
3525                     var y = -this._offsetY;
3526                     var px = -this._previousOffsetX;
3527                     var py = -this._previousOffsetY;
3528                     var w1 = x - px + core.width;
3529                     var w2 = px - x + core.width;
3530                     var h1 = y - py + core.height;
3531                     var h2 = py - y + core.height;
3532                     if (w1 > this._tileWidth && w2 > this._tileWidth &&
3533                             h1 > this._tileHeight && h2 > this._tileHeight) {
3534                         var sx, sy, dx, dy, sw, sh;
3535                         if (w1 < w2) {
3536                             sx = 0;
3537                             dx = px - x;
3538                             sw = w1;
3539                         } else {
3540                             sx = x - px;
3541                             dx = 0;
3542                             sw = w2;
3543                         }
3544                         if (h1 < h2) {
3545                             sy = 0;
3546                             dy = py - y;
3547                             sh = h1;
3548                         } else {
3549                             sy = y - py;
3550                             dy = 0;
3551                             sh = h2;
3552                         }
3553 
3554                         if (core._buffer == null) {
3555                             core._buffer = document.createElement('canvas');
3556                             core._buffer.width = this._context.canvas.width;
3557                             core._buffer.height = this._context.canvas.height;
3558                         }
3559                         var context = core._buffer.getContext('2d');
3560                         if (this._doubledImage) {
3561                             context.clearRect(0, 0, sw * 2, sh * 2);
3562                             context.drawImage(this._context.canvas,
3563                                     sx * 2, sy * 2, sw * 2, sh * 2, 0, 0, sw * 2, sh * 2);
3564                             context = this._context;
3565                             context.clearRect(dx * 2, dy * 2, sw * 2, sh * 2);
3566                             context.drawImage(core._buffer,
3567                                     0, 0, sw * 2, sh * 2, dx * 2, dy * 2, sw * 2, sh * 2);
3568                         } else {
3569                             context.clearRect(0, 0, sw, sh);
3570                             context.drawImage(this._context.canvas,
3571                                     sx, sy, sw, sh, 0, 0, sw, sh);
3572                             context = this._context;
3573                             context.clearRect(dx, dy, sw, sh);
3574                             context.drawImage(core._buffer,
3575                                     0, 0, sw, sh, dx, dy, sw, sh);
3576                         }
3577 
3578                         if (dx === 0) {
3579                             this.redraw(sw, 0, core.width - sw, core.height);
3580                         } else {
3581                             this.redraw(0, 0, core.width - sw, core.height);
3582                         }
3583                         if (dy === 0) {
3584                             this.redraw(0, sh, core.width, core.height - sh);
3585                         } else {
3586                             this.redraw(0, 0, core.width, core.height - sh);
3587                         }
3588                     } else {
3589                         this.redraw(0, 0, core.width, core.height);
3590                     }
3591                 } else {
3592                     this.redraw(0, 0, core.width, core.height);
3593                 }
3594             }
3595             this._previousOffsetX = this._offsetX;
3596             this._previousOffsetY = this._offsetY;
3597         }
3598     },
3599     cvsRender: function(ctx) {
3600         if (this.width !== 0 && this.height !== 0) {
3601             var core = enchant.Core.instance;
3602             this.updateBuffer();
3603             ctx.save();
3604             ctx.setTransform(1, 0, 0, 1, 0, 0);
3605             var cvs = this._context.canvas;
3606                 ctx.drawImage(cvs, 0, 0, core.width, core.height);
3607             ctx.restore();
3608         }
3609     },
3610     domRender: function(element) {
3611         if (this._image) {
3612             this.updateBuffer();
3613             this._style['background-image'] = this._surface._css;
3614             // bad performance
3615             this._style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'matrix(1, 0, 0, 1, 0, 0)';
3616         }
3617     }
3618 });
3619 
3620 
3621 /**
3622  * @scope enchant.Group.prototype
3623  */
3624 enchant.Group = enchant.Class.create(enchant.Node, {
3625     /**
3626      * @name enchant.Group
3627      * @class
3628      * 複数の {@link enchant.Node} を子に持つことができるクラス.
3629      *
3630      * @example
3631      * var stage = new Group();
3632      * stage.addChild(player);
3633      * stage.addChild(enemy);
3634      * stage.addChild(map);
3635      * stage.addEventListener('enterframe', function() {
3636      *     // playerの座標に従って全体をスクロールする
3637      *     if (this.x > 64 - player.x) {
3638      *         this.x = 64 - player.x;
3639      *     }
3640      * });
3641      * @constructs
3642      * @extends enchant.Node
3643      */
3644     initialize: function() {
3645         /**
3646          * 子のNode.
3647          * @type enchant.Node[]
3648          */
3649         this.childNodes = [];
3650 
3651         enchant.Node.call(this);
3652 
3653         this._rotation = 0;
3654         this._scaleX = 1;
3655         this._scaleY = 1;
3656 
3657         this._originX = null;
3658         this._originY = null;
3659 
3660         this.__dirty = false;
3661 
3662         [enchant.Event.ADDED_TO_SCENE, enchant.Event.REMOVED_FROM_SCENE]
3663             .forEach(function(event) {
3664                 this.addEventListener(event, function(e) {
3665                     this.childNodes.forEach(function(child) {
3666                         child.scene = this.scene;
3667                         child.dispatchEvent(e);
3668                     }, this);
3669                 });
3670             }, this);
3671     },
3672     /**
3673      * GroupにNodeを追加する.
3674      * @param {enchant.Node} node 追加するNode.
3675      */
3676     addChild: function(node) {
3677         if (node.parentNode) {
3678             node.parentNode.removeChild(node);
3679         }
3680         this.childNodes.push(node);
3681         node.parentNode = this;
3682         var childAdded = new enchant.Event('childadded');
3683         childAdded.node = node;
3684         childAdded.next = null;
3685         this.dispatchEvent(childAdded);
3686         node.dispatchEvent(new enchant.Event('added'));
3687         if (this.scene) {
3688             node.scene = this.scene;
3689             var addedToScene = new enchant.Event('addedtoscene');
3690             node.dispatchEvent(addedToScene);
3691         }
3692     },
3693     /**
3694      * GroupにNodeを挿入する.
3695      * @param {enchant.Node} node 挿入するNode.
3696      * @param {enchant.Node} reference 挿入位置の前にあるNode.
3697      */
3698     insertBefore: function(node, reference) {
3699         if (node.parentNode) {
3700             node.parentNode.removeChild(node);
3701         }
3702         var i = this.childNodes.indexOf(reference);
3703         if (i !== -1) {
3704             this.childNodes.splice(i, 0, node);
3705             node.parentNode = this;
3706             var childAdded = new enchant.Event('childadded');
3707             childAdded.node = node;
3708             childAdded.next = reference;
3709             this.dispatchEvent(childAdded);
3710             node.dispatchEvent(new enchant.Event('added'));
3711             if (this.scene) {
3712                 node.scene = this.scene;
3713                 var addedToScene = new enchant.Event('addedtoscene');
3714                 node.dispatchEvent(addedToScene);
3715             }
3716         } else {
3717             this.addChild(node);
3718         }
3719     },
3720     /**
3721      * GroupからNodeを削除する.
3722      * @param {enchant.Node} node 削除するNode.
3723      */
3724     removeChild: function(node) {
3725         var i;
3726         if ((i = this.childNodes.indexOf(node)) !== -1) {
3727             this.childNodes.splice(i, 1);
3728             node.parentNode = null;
3729             var childRemoved = new enchant.Event('childremoved');
3730             childRemoved.node = node;
3731             this.dispatchEvent(childRemoved);
3732             node.dispatchEvent(new enchant.Event('removed'));
3733             if (this.scene) {
3734                 node.scene = null;
3735                 var removedFromScene = new enchant.Event('removedfromscene');
3736                 node.dispatchEvent(removedFromScene);
3737             }
3738         }
3739     },
3740     /**
3741      * 最初の子Node.
3742      * @type enchant.Node
3743      */
3744     firstChild: {
3745         get: function() {
3746             return this.childNodes[0];
3747         }
3748     },
3749     /**
3750      * 最後の子Node.
3751      * @type enchant.Node
3752      */
3753     lastChild: {
3754         get: function() {
3755             return this.childNodes[this.childNodes.length - 1];
3756         }
3757     },
3758     /**
3759     * Groupの回転角 (度数法).
3760     * @type Number
3761     */
3762     rotation: {
3763         get: function() {
3764             return this._rotation;
3765         },
3766         set: function(rotation) {
3767             if(this._rotation !== rotation) {
3768                 this._rotation = rotation;
3769                 this._dirty = true;
3770             }
3771         }
3772     },
3773     /**
3774     * Groupのx軸方向の倍率.
3775     * @type Number
3776     * @see enchant.Group#originX
3777     * @see enchant.Group#originY
3778     */
3779     scaleX: {
3780         get: function() {
3781             return this._scaleX;
3782         },
3783         set: function(scale) {
3784             if(this._scaleX !== scale) {
3785                 this._scaleX = scale;
3786                 this._dirty = true;
3787             }
3788         }
3789     },
3790     /**
3791     * Groupのy軸方向の倍率.
3792     * @type Number
3793     * @see enchant.Group#originX
3794     * @see enchant.Group#originY
3795     */
3796     scaleY: {
3797         get: function() {
3798             return this._scaleY;
3799         },
3800         set: function(scale) {
3801             if(this._scaleY !== scale) {
3802                 this._scaleY = scale;
3803                 this._dirty = true;
3804             }
3805         }
3806     },
3807     /**
3808     * 回転・拡大縮小の基準点のX座標
3809     * @type Number
3810     */
3811     originX: {
3812         get: function() {
3813             return this._originX;
3814         },
3815         set: function(originX) {
3816             if(this._originX !== originX) {
3817                 this._originX = originX;
3818                 this._dirty = true;
3819             }
3820         }
3821     },
3822     /**
3823     * 回転・拡大縮小の基準点のX座標
3824     * @type Number
3825     */
3826     originY: {
3827         get: function() {
3828             return this._originY;
3829         },
3830         set: function(originY) {
3831             if(this._originY !== originY) {
3832                 this._originY = originY;
3833                 this._dirty = true;
3834             }
3835         }
3836     },
3837     /**#nocode+*/
3838     _dirty: {
3839         get: function() {
3840             return this.__dirty;
3841         },
3842         set: function(dirty) {
3843             dirty = !!dirty;
3844             this.__dirty = dirty;
3845             if (dirty) {
3846                 for (var i = 0, l = this.childNodes.length; i < l; i++) {
3847                     this.childNodes[i]._dirty = true;
3848                 }
3849             }
3850         }
3851     }
3852     /**#nocode-*/
3853 });
3854 
3855 enchant.Matrix = enchant.Class.create({
3856     initialize: function() {
3857         this.reset();
3858     },
3859     reset: function() {
3860         this.stack = [];
3861         this.stack.push([ 1, 0, 0, 1, 0, 0 ]);
3862     },
3863     makeTransformMatrix: function(node, dest) {
3864         var x = node._x;
3865         var y = node._y;
3866         var width = node.width || 0;
3867         var height = node.height || 0;
3868         var rotation = node._rotation || 0;
3869         var scaleX = (typeof node._scaleX === 'number') ? node._scaleX : 1;
3870         var scaleY = (typeof node._scaleY === 'number') ? node._scaleY : 1;
3871         var theta = rotation * Math.PI / 180;
3872         var tmpcos = Math.cos(theta);
3873         var tmpsin = Math.sin(theta);
3874         var w = (typeof node._originX === 'number') ? node._originX : width / 2;
3875         var h = (typeof node._originY === 'number') ? node._originY : height / 2;
3876         var a = scaleX * tmpcos;
3877         var b = scaleX * tmpsin;
3878         var c = scaleY * tmpsin;
3879         var d = scaleY * tmpcos;
3880         dest[0] = a;
3881         dest[1] = b;
3882         dest[2] = -c;
3883         dest[3] = d;
3884         dest[4] = (-a * w + c * h + x + w);
3885         dest[5] = (-b * w - d * h + y + h);
3886     },
3887     multiply: function(m1, m2, dest) {
3888         var a11 = m1[0], a21 = m1[2], adx = m1[4],
3889             a12 = m1[1], a22 = m1[3], ady = m1[5];
3890         var b11 = m2[0], b21 = m2[2], bdx = m2[4],
3891             b12 = m2[1], b22 = m2[3], bdy = m2[5];
3892 
3893         dest[0] = a11 * b11 + a21 * b12;
3894         dest[1] = a12 * b11 + a22 * b12;
3895         dest[2] = a11 * b21 + a21 * b22;
3896         dest[3] = a12 * b21 + a22 * b22;
3897         dest[4] = a11 * bdx + a21 * bdy + adx;
3898         dest[5] = a12 * bdx + a22 * bdy + ady;
3899     },
3900     multiplyVec: function(mat, vec, dest) {
3901         var x = vec[0], y = vec[1];
3902         var m11 = mat[0], m21 = mat[2], mdx = mat[4],
3903             m12 = mat[1], m22 = mat[3], mdy = mat[5];
3904         dest[0] = m11 * x + m21 * y + mdx;
3905         dest[1] = m12 * x + m22 * y + mdy;
3906     }
3907 });
3908 enchant.Matrix.instance = new enchant.Matrix();
3909 
3910 enchant.DetectColorManager = enchant.Class.create({
3911     initialize: function(reso, max) {
3912         this.reference = [];
3913         this.colorResolution = reso || 16;
3914         this.max = max || 1;
3915         this.capacity = Math.pow(this.colorResolution, 3);
3916         for (var i = 1, l = this.capacity; i < l; i++) {
3917             this.reference[i] = null;
3918         }
3919     },
3920     attachDetectColor: function(sprite) {
3921         var i = this.reference.indexOf(null);
3922         if (i === -1) {
3923             i = 1;
3924         }
3925         this.reference[i] = sprite;
3926         return this._getColor(i);
3927     },
3928     detachDetectColor: function(sprite) {
3929         var i = this.reference.indexOf(sprite);
3930         if (i !== -1) {
3931             this.reference[i] = null;
3932         }
3933     },
3934     _getColor: function(n) {
3935         var C = this.colorResolution;
3936         var d = C / this.max;
3937         return [
3938             parseInt((n / C / C) % C, 10) / d,
3939             parseInt((n / C) % C, 10) / d,
3940             parseInt(n % C, 10) / d,
3941             1.0
3942         ];
3943     },
3944     _decodeDetectColor: function(color, i) {
3945         i = i || 0;
3946         var C = this.colorResolution;
3947         return ~~(color[i] * C * C * C / 256) +
3948             ~~(color[i + 1] * C * C / 256) +
3949             ~~(color[i + 2] * C / 256);
3950     },
3951     getSpriteByColor: function(color) {
3952         return this.reference[this._decodeDetectColor(color)];
3953     },
3954     getSpriteByColors: function(rgba) {
3955         var i, l, id, result,
3956             score = 0,
3957             found = {};
3958 
3959         for (i = 0, l = rgba.length; i < l; i+= 4) {
3960             id = this._decodeDetectColor(rgba, i);
3961             found[id] = (found[id] || 0) + 1;
3962         }
3963         for (id in found) {
3964             if (found[id] > score) {
3965                 score = found[id];
3966                 result = id;
3967             }
3968         }
3969 
3970         return this.reference[result];
3971     }
3972 });
3973 
3974 enchant.DomManager = enchant.Class.create({
3975     initialize: function(node, elementDefinition) {
3976         var core = enchant.Core.instance;
3977         this.layer = null;
3978         this.targetNode = node;
3979         if (typeof elementDefinition === 'string') {
3980             this.element = document.createElement(elementDefinition);
3981         } else if (elementDefinition instanceof HTMLElement) {
3982             this.element = elementDefinition;
3983         }
3984         this.style = this.element.style;
3985         this.style.position = 'absolute';
3986         this.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0px 0px';
3987         if (core._debug) {
3988             this.style.border = '1px solid blue';
3989             this.style.margin = '-1px';
3990         }
3991 
3992         var manager = this;
3993         this._setDomTarget = function() {
3994             manager.layer._touchEventTarget = manager.targetNode;
3995         };
3996         this._attachEvent();
3997     },
3998     getDomElement: function() {
3999         return this.element;
4000     },
4001     getDomElementAsNext: function() {
4002         return this.element;
4003     },
4004     getNextManager: function(manager) {
4005         var i = this.targetNode.parentNode.childNodes.indexOf(manager.targetNode);
4006         if (i !== this.targetNode.parentNode.childNodes.length - 1) {
4007             return this.targetNode.parentNode.childNodes[i + 1]._domManager;
4008         } else {
4009             return null;
4010         }
4011     },
4012     addManager: function(childManager, nextManager) {
4013         var nextElement;
4014         if (nextManager) {
4015             nextElement = nextManager.getDomElementAsNext();
4016         }
4017         var element = childManager.getDomElement();
4018         if (element instanceof Array) {
4019             element.forEach(function(child) {
4020                 if (nextElement) {
4021                     this.element.insertBefore(child, nextElement);
4022                 } else {
4023                     this.element.appendChild(child);
4024                 }
4025             }, this);
4026         } else {
4027             if (nextElement) {
4028                 this.element.insertBefore(element, nextElement);
4029             } else {
4030                 this.element.appendChild(element);
4031             }
4032         }
4033         this.setLayer(this.layer);
4034     },
4035     removeManager: function(childManager) {
4036         if (childManager instanceof enchant.DomlessManager) {
4037             childManager._domRef.forEach(function(element) {
4038                 this.element.removeChild(element);
4039             }, this);
4040         } else {
4041             this.element.removeChild(childManager.element);
4042         }
4043         this.setLayer(this.layer);
4044     },
4045     setLayer: function(layer) {
4046         this.layer = layer;
4047         var node = this.targetNode;
4048         var manager;
4049         if (node.childNodes) {
4050             for (var i = 0, l = node.childNodes.length; i < l; i++) {
4051                 manager = node.childNodes[i]._domManager;
4052                 if (manager) {
4053                     manager.setLayer(layer);
4054                 }
4055             }
4056         }
4057     },
4058     render: function(inheritMat) {
4059         var node = this.targetNode;
4060         var matrix = enchant.Matrix.instance;
4061         var stack = matrix.stack;
4062         var dest = [];
4063         matrix.makeTransformMatrix(node, dest);
4064         matrix.multiply(stack[stack.length - 1], dest, dest);
4065         matrix.multiply(inheritMat, dest, inheritMat);
4066         node._matrix = inheritMat;
4067         var ox = (typeof node._originX === 'number') ? node._originX : node.width / 2 || 0;
4068         var oy = (typeof node._originY === 'number') ? node._originY : node.height / 2 || 0;
4069         var vec = [ ox, oy ];
4070         matrix.multiplyVec(dest, vec, vec);
4071 
4072         node._offsetX = vec[0] - ox;
4073         node._offsetY = vec[1] - oy;
4074         if(node.parentNode && !(node.parentNode instanceof enchant.Group)) {
4075             node._offsetX += node.parentNode._offsetX;
4076             node._offsetY += node.parentNode._offsetY;
4077         }
4078         if (node._dirty) {
4079             this.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'matrix(' +
4080                 dest[0].toFixed(10) + ',' +
4081                 dest[1].toFixed(10) + ',' +
4082                 dest[2].toFixed(10) + ',' +
4083                 dest[3].toFixed(10) + ',' +
4084                 dest[4].toFixed(10) + ',' +
4085                 dest[5].toFixed(10) +
4086             ')';
4087         }
4088         this.domRender();
4089     },
4090     domRender: function() {
4091         var node = this.targetNode;
4092         if(!node._style) {
4093             node._style = {};
4094         }
4095         if(!node.__styleStatus) {
4096             node.__styleStatus = {};
4097         }
4098         if (node.width !== null) {
4099             node._style.width = node.width + 'px';
4100         }
4101         if (node.height !== null) {
4102             node._style.height = node.height + 'px';
4103         }
4104         node._style.opacity = node._opacity;
4105         node._style['background-color'] = node._backgroundColor;
4106         if (typeof node._visible !== 'undefined') {
4107             node._style.display = node._visible ? 'block' : 'none';
4108         }
4109         if (typeof node.domRender === 'function') {
4110             node.domRender(this.element);
4111         }
4112         var value;
4113         for (var prop in node._style) {
4114             value = node._style[prop];
4115             if(node.__styleStatus[prop] !== value && value != null) {
4116                 this.style.setProperty(prop, '' + value);
4117                 node.__styleStatus[prop] = value;
4118             }
4119         }
4120     },
4121     _attachEvent: function() {
4122         if (enchant.ENV.TOUCH_ENABLED) {
4123             this.element.addEventListener('touchstart', this._setDomTarget, true);
4124         }
4125         this.element.addEventListener('mousedown', this._setDomTarget, true);
4126     },
4127     _detachEvent: function() {
4128         if (enchant.ENV.TOUCH_ENABLED) {
4129             this.element.removeEventListener('touchstart', this._setDomTarget, true);
4130         }
4131         this.element.removeEventListener('mousedown', this._setDomTarget, true);
4132     },
4133     remove: function() {
4134         this._detachEvent();
4135         this.element = this.style = this.targetNode = null;
4136     }
4137 });
4138 
4139 enchant.DomlessManager = enchant.Class.create({
4140     initialize: function(node) {
4141         this._domRef = [];
4142         this.targetNode = node;
4143     },
4144     _register: function(element, nextElement) {
4145         var i = this._domRef.indexOf(nextElement);
4146         if (element instanceof Array) {
4147             if (i === -1) {
4148                 Array.prototype.push.apply(this._domRef, element);
4149             } else {
4150                 Array.prototype.splice.apply(this._domRef, [i, 0].concat(element));
4151             }
4152         } else {
4153             if (i === -1) {
4154                 this._domRef.push(element);
4155             } else {
4156                 this._domRef.splice(i, 0, element);
4157             }
4158         }
4159     },
4160     getNextManager: function(manager) {
4161         var i = this.targetNode.parentNode.childNodes.indexOf(manager.targetNode);
4162         if (i !== this.targetNode.parentNode.childNodes.length - 1) {
4163             return this.targetNode.parentNode.childNodes[i + 1]._domManager;
4164         } else {
4165             return null;
4166         }
4167     },
4168     getDomElement: function() {
4169         var ret = [];
4170         this.targetNode.childNodes.forEach(function(child) {
4171             ret = ret.concat(child._domManager.getDomElement());
4172         });
4173         return ret;
4174     },
4175     getDomElementAsNext: function() {
4176         if (this._domRef.length) {
4177             return this._domRef[0];
4178         } else {
4179             var nextManager = this.getNextManager(this);
4180             if (nextManager) {
4181                 return nextManager.element;
4182             } else {
4183                 return null;
4184             }
4185         }
4186     },
4187     addManager: function(childManager, nextManager) {
4188         var parentNode = this.targetNode.parentNode;
4189         if (parentNode) {
4190             if (nextManager === null) {
4191                 nextManager = this.getNextManager(this);
4192             }
4193             if (parentNode instanceof enchant.Scene) {
4194                 parentNode._layers.Dom._domManager.addManager(childManager, nextManager);
4195             } else {
4196                 parentNode._domManager.addManager(childManager, nextManager);
4197             }
4198         }
4199         var nextElement = nextManager ? nextManager.getDomElementAsNext() : null;
4200         this._register(childManager.getDomElement(), nextElement);
4201         this.setLayer(this.layer);
4202     },
4203     removeManager: function(childManager) {
4204         var dom;
4205         var i = this._domRef.indexOf(childManager.element);
4206         if (i !== -1) {
4207             dom = this._domRef[i];
4208             dom.parentNode.removeChild(dom);
4209             this._domRef.splice(i, 1);
4210         }
4211         this.setLayer(this.layer);
4212     },
4213     setLayer: function(layer) {
4214         this.layer = layer;
4215         var node = this.targetNode;
4216         var manager;
4217         if (node.childNodes) {
4218             for (var i = 0, l = node.childNodes.length; i < l; i++) {
4219                 manager = node.childNodes[i]._domManager;
4220                 if (manager) {
4221                     manager.setLayer(layer);
4222                 }
4223             }
4224         }
4225     },
4226     render: function(inheritMat) {
4227         var matrix = enchant.Matrix.instance;
4228         var stack = matrix.stack;
4229         var node = this.targetNode;
4230         var dest = [];
4231         matrix.makeTransformMatrix(node, dest);
4232         matrix.multiply(stack[stack.length - 1], dest, dest);
4233         matrix.multiply(inheritMat, dest, inheritMat);
4234         node._matrix = inheritMat;
4235         var ox = (typeof node._originX === 'number') ? node._originX : node.width / 2 || 0;
4236         var oy = (typeof node._originY === 'number') ? node._originY : node.height / 2 || 0;
4237         var vec = [ ox, oy ];
4238         matrix.multiplyVec(dest, vec, vec);
4239         node._offsetX = vec[0] - ox;
4240         node._offsetY = vec[1] - oy;
4241         stack.push(dest);
4242     },
4243     remove: function() {
4244         this._domRef = [];
4245         this.targetNode = null;
4246     }
4247 });
4248 
4249 enchant.DomLayer = enchant.Class.create(enchant.Group, {
4250     initialize: function() {
4251         var core = enchant.Core.instance;
4252         enchant.Group.call(this);
4253 
4254         this._touchEventTarget = null;
4255 
4256         this._element = document.createElement('div');
4257         this._element.style.position = 'absolute';
4258 
4259         this._domManager = new enchant.DomManager(this, this._element);
4260         this._domManager.layer = this;
4261 
4262         this.width = core.width;
4263         this.height = core.height;
4264 
4265         var touch = [
4266             enchant.Event.TOUCH_START,
4267             enchant.Event.TOUCH_MOVE,
4268             enchant.Event.TOUCH_END
4269         ];
4270 
4271         touch.forEach(function(type) {
4272             this.addEventListener(type, function(e) {
4273                 if (this._scene) {
4274                     this._scene.dispatchEvent(e);
4275                 }
4276             });
4277         }, this);
4278 
4279         var __onchildadded = function(e) {
4280             var child = e.node;
4281             var next = e.next;
4282             var self = e.target;
4283             var nextManager = next ? next._domManager : null;
4284             enchant.DomLayer._attachDomManager(child, __onchildadded, __onchildremoved);
4285             self._domManager.addManager(child._domManager, nextManager);
4286             var render = new enchant.Event(enchant.Event.RENDER);
4287             child._dirty = true;
4288             self._domManager.layer._rendering(child, render);
4289         };
4290 
4291         var __onchildremoved = function(e) {
4292             var