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 child = e.node;
4293             var self = e.target;
4294             self._domManager.removeManager(child._domManager);
4295             enchant.DomLayer._detachDomManager(child, __onchildadded, __onchildremoved);
4296         };
4297 
4298         this.addEventListener('childremoved', __onchildremoved);
4299         this.addEventListener('childadded', __onchildadded);
4300 
4301     },
4302     width: {
4303         get: function() {
4304             return this._width;
4305         },
4306         set: function(width) {
4307             this._width = width;
4308             this._element.style.width = width + 'px';
4309         }
4310     },
4311     height: {
4312         get: function() {
4313             return this._height;
4314         },
4315         set: function(height) {
4316             this._height = height;
4317             this._element.style.height = height + 'px';
4318         }
4319     },
4320     addChild: function(node) {
4321         this.childNodes.push(node);
4322         node.parentNode = this;
4323         var childAdded = new enchant.Event('childadded');
4324         childAdded.node = node;
4325         childAdded.next = null;
4326         this.dispatchEvent(childAdded);
4327         node.dispatchEvent(new enchant.Event('added'));
4328         if (this.scene) {
4329             node.scene = this.scene;
4330             var addedToScene = new enchant.Event('addedtoscene');
4331             node.dispatchEvent(addedToScene);
4332         }
4333     },
4334     insertBefore: function(node, reference) {
4335         var i = this.childNodes.indexOf(reference);
4336         if (i !== -1) {
4337             this.childNodes.splice(i, 0, node);
4338             node.parentNode = this;
4339             var childAdded = new enchant.Event('childadded');
4340             childAdded.node = node;
4341             childAdded.next = reference;
4342             this.dispatchEvent(childAdded);
4343             node.dispatchEvent(new enchant.Event('added'));
4344             if (this.scene) {
4345                 node.scene = this.scene;
4346                 var addedToScene = new enchant.Event('addedtoscene');
4347                 node.dispatchEvent(addedToScene);
4348             }
4349         } else {
4350             this.addChild(node);
4351         }
4352     },
4353     _startRendering: function() {
4354         this.addEventListener('exitframe', this._onexitframe);
4355         this._onexitframe();
4356     },
4357     _stopRendering: function() {
4358         this.removeEventListener('exitframe', this._onexitframe);
4359         this._onexitframe();
4360     },
4361     _onexitframe: function() {
4362         this._rendering(this, new enchant.Event(enchant.Event.RENDER));
4363     },
4364     _rendering: function(node, e, inheritMat) {
4365         var child;
4366         if (!inheritMat) {
4367             inheritMat = [ 1, 0, 0, 1, 0, 0 ];
4368         }
4369         node.dispatchEvent(e);
4370         node._domManager.render(inheritMat);
4371         if (node.childNodes) {
4372             for (var i = 0, l = node.childNodes.length; i < l; i++) {
4373                 child = node.childNodes[i];
4374                 this._rendering(child, e, inheritMat.slice());
4375             }
4376         }
4377         if (node._domManager instanceof enchant.DomlessManager) {
4378             enchant.Matrix.instance.stack.pop();
4379         }
4380         node._dirty = false;
4381     },
4382     _determineEventTarget: function() {
4383         var target = this._touchEventTarget;
4384         this._touchEventTarget = null;
4385         return (target === this) ? null : target;
4386     }
4387 });
4388 
4389 enchant.DomLayer._attachDomManager = function(node, onchildadded, onchildremoved) {
4390     var child;
4391     if (!node._domManager) {
4392         node.addEventListener('childadded', onchildadded);
4393         node.addEventListener('childremoved', onchildremoved);
4394         if (node instanceof enchant.Group) {
4395             node._domManager = new enchant.DomlessManager(node);
4396         } else {
4397             if (node._element) {
4398                 node._domManager = new enchant.DomManager(node, node._element);
4399             } else {
4400                 node._domManager = new enchant.DomManager(node, 'div');
4401             }
4402         }
4403     }
4404     if (node.childNodes) {
4405         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4406             child = node.childNodes[i];
4407             enchant.DomLayer._attachDomManager(child, onchildadded, onchildremoved);
4408             node._domManager.addManager(child._domManager, null);
4409         }
4410     }
4411 };
4412 
4413 enchant.DomLayer._detachDomManager = function(node, onchildadded, onchildremoved) {
4414     var child;
4415     node.removeEventListener('childadded', onchildadded);
4416     node.removeEventListener('childremoved', onchildremoved);
4417     if (node.childNodes) {
4418         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4419             child = node.childNodes[i];
4420             node._domManager.removeManager(child._domManager, null);
4421             enchant.DomLayer._detachDomManager(child, onchildadded, onchildremoved);
4422         }
4423     }
4424     node._domManager.remove();
4425     delete node._domManager;
4426 };
4427 
4428 /**
4429  * @scope enchant.CanvasLayer.prototype
4430  */
4431 enchant.CanvasLayer = enchant.Class.create(enchant.Group, {
4432     /**
4433      * @name enchant.CanvasLayer
4434      * @class
4435      * Canvas を用いた描画を行うクラス.
4436      * 子を Canvas を用いた描画に切り替えるクラス.
4437      * @constructs
4438      * @extends enchant.Group
4439      */
4440     initialize: function() {
4441         var core = enchant.Core.instance;
4442 
4443         enchant.Group.call(this);
4444 
4445         this._cvsCache = {
4446             matrix: [1, 0, 0, 1, 0, 0],
4447             detectColor: '#000000'
4448         };
4449         this._cvsCache.layer = this;
4450 
4451         this._element = document.createElement('canvas');
4452         this._element.style.position = 'absolute';
4453         // issue 179
4454         this._element.style.left = this._element.style.top = '0px';
4455 
4456         this._detect = document.createElement('canvas');
4457         this._detect.style.position = 'absolute';
4458         this._lastDetected = 0;
4459 
4460         this.context = this._element.getContext('2d');
4461         this._dctx = this._detect.getContext('2d');
4462         this._setImageSmoothingEnable();
4463 
4464         this._colorManager = new enchant.DetectColorManager(16, 256);
4465 
4466         this.width = core.width;
4467         this.height = core.height;
4468 
4469         var touch = [
4470             enchant.Event.TOUCH_START,
4471             enchant.Event.TOUCH_MOVE,
4472             enchant.Event.TOUCH_END
4473         ];
4474 
4475         touch.forEach(function(type) {
4476             this.addEventListener(type, function(e) {
4477                 if (this._scene) {
4478                     this._scene.dispatchEvent(e);
4479                 }
4480             });
4481         }, this);
4482 
4483         var __onchildadded = function(e) {
4484             var child = e.node;
4485             var self = e.target;
4486             var layer;
4487             if (self instanceof enchant.CanvasLayer) {
4488                 layer = self._scene._layers.Canvas;
4489             } else {
4490                 layer = self.scene._layers.Canvas;
4491             }
4492             enchant.CanvasLayer._attachCache(child, layer, __onchildadded, __onchildremoved);
4493             var render = new enchant.Event(enchant.Event.RENDER);
4494             if (self._dirty) {
4495                 self._updateCoordinate();
4496             }
4497             child._dirty = true;
4498             enchant.Matrix.instance.stack.push(self._matrix);
4499             enchant.CanvasRenderer.instance.render(layer.context, child, render);
4500             enchant.Matrix.instance.stack.pop(self._matrix);
4501         };
4502 
4503         var __onchildremoved = function(e) {
4504             var child = e.node;
4505             var self = e.target;
4506             var layer;
4507             if (self instanceof enchant.CanvasLayer) {
4508                 layer = self._scene._layers.Canvas;
4509             } else {
4510                 layer = self.scene._layers.Canvas;
4511             }
4512             enchant.CanvasLayer._detachCache(child, layer, __onchildadded, __onchildremoved);
4513         };
4514 
4515         this.addEventListener('childremoved', __onchildremoved);
4516         this.addEventListener('childadded', __onchildadded);
4517 
4518     },
4519     /**
4520      * CanvasLayerの横幅.
4521      * @type Number
4522      */
4523     width: {
4524         get: function() {
4525             return this._width;
4526         },
4527         set: function(width) {
4528             this._width = width;
4529             this._element.width = this._detect.width = width;
4530             this._setImageSmoothingEnable();
4531         }
4532     },
4533     /**
4534      * CanvasLayerの高さ.
4535      * @type Number
4536      */
4537     height: {
4538         get: function() {
4539             return this._height;
4540         },
4541         set: function(height) {
4542             this._height = height;
4543             this._element.height = this._detect.height = height;
4544             this._setImageSmoothingEnable();
4545         }
4546     },
4547     addChild: function(node) {
4548         this.childNodes.push(node);
4549         node.parentNode = this;
4550         var childAdded = new enchant.Event('childadded');
4551         childAdded.node = node;
4552         childAdded.next = null;
4553         this.dispatchEvent(childAdded);
4554         node.dispatchEvent(new enchant.Event('added'));
4555     },
4556     insertBefore: function(node, reference) {
4557         var i = this.childNodes.indexOf(reference);
4558         if (i !== -1) {
4559             this.childNodes.splice(i, 0, node);
4560             node.parentNode = this;
4561             var childAdded = new enchant.Event('childadded');
4562             childAdded.node = node;
4563             childAdded.next = reference;
4564             this.dispatchEvent(childAdded);
4565             node.dispatchEvent(new enchant.Event('added'));
4566         } else {
4567             this.addChild(node);
4568         }
4569     },
4570     /**
4571      * レンダリングを開始する.
4572      * @private
4573      */
4574     _startRendering: function() {
4575         this.addEventListener('exitframe', this._onexitframe);
4576         this._onexitframe();
4577     },
4578     /**
4579      * レンダリングを停止する.
4580      * @private
4581      */
4582     _stopRendering: function() {
4583         this.removeEventListener('exitframe', this._onexitframe);
4584         this._onexitframe();
4585     },
4586     _onexitframe: function() {
4587         var core = enchant.Core.instance;
4588         var ctx = this.context;
4589         ctx.clearRect(0, 0, core.width, core.height);
4590         var render = new enchant.Event(enchant.Event.RENDER);
4591         enchant.CanvasRenderer.instance.render(ctx, this, render);
4592     },
4593     _determineEventTarget: function(e) {
4594         return this._getEntityByPosition(e.x, e.y);
4595     },
4596     _getEntityByPosition: function(x, y) {
4597         var core = enchant.Core.instance;
4598         var ctx = this._dctx;
4599         if (this._lastDetected < core.frame) {
4600             ctx.clearRect(0, 0, this.width, this.height);
4601             enchant.CanvasRenderer.instance.detectRender(ctx, this);
4602             this._lastDetected = core.frame;
4603         }
4604         var extra = enchant.ENV.COLOR_DETECTION_LEVEL - 1;
4605         var rgba = ctx.getImageData(x - extra, y - extra, 1 + extra * 2, 1 + extra * 2).data;
4606         return this._colorManager.getSpriteByColors(rgba);
4607     },
4608     _setImageSmoothingEnable: function() {
4609         this._dctx.imageSmoothingEnabled =
4610                 this._dctx.msImageSmoothingEnabled =
4611                 this._dctx.mozImageSmoothingEnabled =
4612                 this._dctx.webkitImageSmoothingEnabled = false;
4613     }
4614 });
4615 
4616 enchant.CanvasLayer._attachCache = function(node, layer, onchildadded, onchildremoved) {
4617     var child;
4618     if (!node._cvsCache) {
4619         node._cvsCache = {};
4620         node._cvsCache.matrix = [ 1, 0, 0, 1, 0, 0 ];
4621         node._cvsCache.detectColor = 'rgba(' + layer._colorManager.attachDetectColor(node) + ')';
4622         node.addEventListener('childadded', onchildadded);
4623         node.addEventListener('childremoved', onchildremoved);
4624     }
4625     if (node.childNodes) {
4626         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4627             child = node.childNodes[i];
4628             enchant.CanvasLayer._attachCache(child, layer, onchildadded, onchildremoved);
4629         }
4630     }
4631 };
4632 
4633 enchant.CanvasLayer._detachCache = function(node, layer, onchildadded, onchildremoved) {
4634     var child;
4635     if (node._cvsCache) {
4636         layer._colorManager.detachDetectColor(node);
4637         node.removeEventListener('childadded', onchildadded);
4638         node.removeEventListener('childremoved', onchildremoved);
4639         delete node._cvsCache;
4640     }
4641     if (node.childNodes) {
4642         for (var i = 0, l = node.childNodes.length; i < l; i++) {
4643             child = node.childNodes[i];
4644             enchant.CanvasLayer._detachCache(child, layer, onchildadded, onchildremoved);
4645         }
4646     }
4647 };
4648 
4649 enchant.CanvasRenderer = enchant.Class.create({
4650     render: function(ctx, node, e) {
4651         var width, height, child;
4652         ctx.save();
4653         node.dispatchEvent(e);
4654         // transform
4655         this.transform(ctx, node);
4656         if (typeof node._visible === 'undefined' || node._visible) {
4657             width = node.width;
4658             height = node.height;
4659             // composite
4660             if (node.compositeOperation) {
4661                 ctx.globalCompositeOperation = node.compositeOperation;
4662             }
4663             ctx.globalAlpha = (typeof node._opacity === 'number') ? node._opacity : 1.0;
4664             // render
4665             if (node._backgroundColor) {
4666                 ctx.fillStyle = node._backgroundColor;
4667                 ctx.fillRect(0, 0, width, height);
4668             }
4669 
4670             if (node.cvsRender) {
4671                 node.cvsRender(ctx);
4672             }
4673 
4674             if (enchant.Core.instance._debug && node._debugColor) {
4675                 ctx.strokeStyle = node._debugColor;
4676                 ctx.strokeRect(0, 0, width, height);
4677             }
4678             if (node._clipping) {
4679                 ctx.beginPath();
4680                 ctx.rect(0, 0, width, height);
4681                 ctx.clip();
4682             }
4683             if (node.childNodes) {
4684                 for (var i = 0, l = node.childNodes.length; i < l; i++) {
4685                     child = node.childNodes[i];
4686                     this.render(ctx, child, e);
4687                 }
4688             }
4689         }
4690         ctx.restore();
4691         enchant.Matrix.instance.stack.pop();
4692     },
4693     detectRender: function(ctx, node) {
4694         var width, height, child;
4695         if (typeof node._visible === 'undefined' || node._visible) {
4696             width = node.width;
4697             height = node.height;
4698             ctx.save();
4699             this.transform(ctx, node);
4700             ctx.fillStyle = node._cvsCache.detectColor;
4701             if (node._touchEnabled) {
4702                 if (node.detectRender) {
4703                     node.detectRender(ctx);
4704                 } else {
4705                     ctx.fillRect(0, 0, width, height);
4706                 }
4707             }
4708             if (node._clipping) {
4709                 ctx.beginPath();
4710                 ctx.rect(0, 0, width, height);
4711                 ctx.clip();
4712             }
4713             if (node.childNodes) {
4714                 for (var i = 0, l = node.childNodes.length; i < l; i++) {
4715                     child = node.childNodes[i];
4716                     this.detectRender(ctx, child);
4717                 }
4718             }
4719             ctx.restore();
4720             enchant.Matrix.instance.stack.pop();
4721         }
4722     },
4723     transform: function(ctx, node) {
4724         var matrix = enchant.Matrix.instance;
4725         var stack = matrix.stack;
4726         var newmat, ox, oy, vec;
4727         if (node._dirty) {
4728             matrix.makeTransformMatrix(node, node._cvsCache.matrix);
4729             newmat = [];
4730             matrix.multiply(stack[stack.length - 1], node._cvsCache.matrix, newmat);
4731             node._matrix = newmat;
4732             ox = (typeof node._originX === 'number') ? node._originX : node._width / 2 || 0;
4733             oy = (typeof node._originY === 'number') ? node._originY : node._height / 2 || 0;
4734             vec = [ ox, oy ];
4735             matrix.multiplyVec(newmat, vec, vec);
4736             node._offsetX = vec[0] - ox;
4737             node._offsetY = vec[1] - oy;
4738             node._dirty = false;
4739         } else {
4740             newmat = node._matrix;
4741         }
4742         stack.push(newmat);
4743         ctx.setTransform.apply(ctx, newmat);
4744     }
4745 });
4746 enchant.CanvasRenderer.instance = new enchant.CanvasRenderer();
4747 
4748 /**
4749  * @scope enchant.Scene.prototype
4750  */
4751 enchant.Scene = enchant.Class.create(enchant.Group, {
4752     /**
4753      * @name enchant.Scene
4754      * @class
4755      * 表示オブジェクトツリーのルートになるクラス.
4756      * シーンはレイヤーを持っていて, 子として追加されたオブジェクト ({@link Entity}) は描画方法に応じてレイヤーに振り分けられる.
4757      * Scene クラスは最も汎用的なシーンの実装で, ({@link enchant.DOMLayer} と {@link enchant.CanvasLayer}) を持っており,
4758      * それぞれ DOM, Canvas を用いて描画される. 描画順は DOM が手前, Canvas が奥で,
4759      * 各レイヤーの間では新しく追加されたオブジェクトほど手前に表示される.
4760      * Scene クラスを継承することで, 新しい種類の Layer を持つシーンクラスを作ることができる.
4761      *
4762      * @example
4763      * var scene = new Scene();
4764      * scene.addChild(player);
4765      * scene.addChild(enemy);
4766      * core.pushScene(scene);
4767      *
4768      * @constructs
4769      * @extends enchant.Group
4770      */
4771     initialize: function() {
4772         var core = enchant.Core.instance;
4773 
4774         // Call initialize method of enchant.Group
4775         enchant.Group.call(this);
4776 
4777         // All nodes (entities, groups, scenes) have reference to the scene that it belongs to.
4778         this.scene = this;
4779 
4780         this._backgroundColor = null;
4781 
4782         // Create div tag which possesses its layers
4783         this._element = document.createElement('div');
4784         this._element.style.position = 'absolute';
4785         this._element.style.overflow = 'hidden';
4786         this._element.style[enchant.ENV.VENDOR_PREFIX + 'TransformOrigin'] = '0 0';
4787 
4788         this._layers = {};
4789         this._layerPriority = [];
4790 
4791         this.addEventListener(enchant.Event.CHILD_ADDED, this._onchildadded);
4792         this.addEventListener(enchant.Event.CHILD_REMOVED, this._onchildremoved);
4793         this.addEventListener(enchant.Event.ENTER, this._onenter);
4794         this.addEventListener(enchant.Event.EXIT, this._onexit);
4795 
4796         var that = this;
4797         this._dispatchExitframe = function() {
4798             var layer;
4799             for (var prop in that._layers) {
4800                 layer = that._layers[prop];
4801                 layer.dispatchEvent(new enchant.Event(enchant.Event.EXIT_FRAME));
4802             }
4803         };
4804 
4805         this.addEventListener(enchant.Event.CORE_RESIZE, this._oncoreresize);
4806 
4807         this._oncoreresize(core);
4808     },
4809     /**#nocode+*/
4810     x: {
4811         get: function() {
4812             return this._x;
4813         },
4814         set: function(x) {
4815             this._x = x;
4816             for (var type in this._layers) {
4817                 this._layers[type].x = x;
4818             }
4819         }
4820     },
4821     y: {
4822         get: function() {
4823             return this._y;
4824         },
4825         set: function(y) {
4826             this._y = y;
4827             for (var type in this._layers) {
4828                 this._layers[type].y = y;
4829             }
4830         }
4831     },
4832     width: {
4833         get: function() {
4834             return this._width;
4835         },
4836         set: function(width) {
4837             this._width = width;
4838             for (var type in this._layers) {
4839                 this._layers[type].width = width;
4840             }
4841         }
4842     },
4843     height: {
4844         get: function() {
4845             return this._height;
4846         },
4847         set: function(height) {
4848             this._height = height;
4849             for (var type in this._layers) {
4850                 this._layers[type].height = height;
4851             }
4852         }
4853     },
4854     rotation: {
4855         get: function() {
4856             return this._rotation;
4857         },
4858         set: function(rotation) {
4859             this._rotation = rotation;
4860             for (var type in this._layers) {
4861                 this._layers[type].rotation = rotation;
4862             }
4863         }
4864     },
4865     scaleX: {
4866         get: function() {
4867             return this._scaleX;
4868         },
4869         set: function(scaleX) {
4870             this._scaleX = scaleX;
4871             for (var type in this._layers) {
4872                 this._layers[type].scaleX = scaleX;
4873             }
4874         }
4875     },
4876     scaleY: {
4877         get: function() {
4878             return this._scaleY;
4879         },
4880         set: function(scaleY) {
4881             this._scaleY = scaleY;
4882             for (var type in this._layers) {
4883                 this._layers[type].scaleY = scaleY;
4884             }
4885         }
4886     },
4887     backgroundColor: {
4888         get: function() {
4889             return this._backgroundColor;
4890         },
4891         set: function(color) {
4892             this._backgroundColor = this._element.style.backgroundColor = color;
4893         }
4894     },
4895     remove: function() {
4896         this.clearEventListener();
4897 
4898         while (this.childNodes.length > 0) {
4899             this.childNodes[0].remove();
4900         }
4901 
4902         return enchant.Core.instance.removeScene(this);
4903     },
4904     /**#nocode-*/
4905     _oncoreresize: function(e) {
4906         this._element.style.width = e.width + 'px';
4907         this.width = e.width;
4908         this._element.style.height = e.height + 'px';
4909         this.height = e.height;
4910         this._element.style[enchant.ENV.VENDOR_PREFIX + 'Transform'] = 'scale(' + e.scale + ')';
4911 
4912         for (var type in this._layers) {
4913             this._layers[type].dispatchEvent(e);
4914         }
4915     },
4916     addLayer: function(type, i) {
4917         var core = enchant.Core.instance;
4918         if (this._layers[type]) {
4919             return;
4920         }
4921         var layer = new enchant[type + 'Layer']();
4922         if (core.currentScene === this) {
4923             layer._startRendering();
4924         }
4925         this._layers[type] = layer;
4926         var element = layer._element;
4927         if (typeof i === 'number') {
4928             var nextSibling = this._element.childNodes[i];
4929             if (nextSibling) {
4930                 this._element.insertBefore(element, nextSibling);
4931             } else {
4932                 this._element.appendChild(element);
4933             }
4934             this._layerPriority.splice(i, 0, type);
4935         } else {
4936             this._element.appendChild(element);
4937             this._layerPriority.push(type);
4938         }
4939         layer._scene = this;
4940     },
4941     _determineEventTarget: function(e) {
4942         var layer, target;
4943         for (var i = this._layerPriority.length - 1; i >= 0; i--) {
4944             layer = this._layers[this._layerPriority[i]];
4945             target = layer._determineEventTarget(e);
4946             if (target) {
4947                 break;
4948             }
4949         }
4950         if (!target) {
4951             target = this;
4952         }
4953         return target;
4954     },
4955     _onchildadded: function(e) {
4956         var child = e.node;
4957         var next = e.next;
4958         var target, i;
4959         if (child._element) {
4960             target = 'Dom';
4961             i = 1;
4962         } else {
4963             target = 'Canvas';
4964             i = 0;
4965         }
4966         if (!this._layers[target]) {
4967             this.addLayer(target, i);
4968         }
4969         child._layer = this._layers[target];
4970         this._layers[target].insertBefore(child, next);
4971         child.parentNode = this;
4972     },
4973     _onchildremoved: function(e) {
4974         var child = e.node;
4975         child._layer.removeChild(child);
4976         child._layer = null;
4977     },
4978     _onenter: function() {
4979         for (var type in this._layers) {
4980             this._layers[type]._startRendering();
4981         }
4982         enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe);
4983     },
4984     _onexit: function() {
4985         for (var type in this._layers) {
4986             this._layers[type]._stopRendering();
4987         }
4988         enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe);
4989     }
4990 });
4991 
4992 /**
4993  * @scope enchant.LoadingScene.prototype
4994  */
4995 enchant.LoadingScene = enchant.Class.create(enchant.Scene, {
4996     /**
4997      * @name enchant.LoadingScene
4998      * @class
4999      * デフォルトのローディングシーン. ローディングアニメーションを書き換えたい場合は,
5000      * enchant.LoadingSceneを上書きする.
5001      *
5002      * @example
5003      * enchant.LoadingScene = enchant.Class.create(enchant.Scene, {
5004      *     initialize: function() {
5005      *         enchant.Scene.call(this);
5006      *         this.backgroundColor = 'red';
5007      *         // ...
5008      *         this.addEventListener('progress', function(e) {
5009      *             progress = e.loaded / e.total;
5010      *         });
5011      *         this.addEventListener('enterframe', function() {
5012      *             // animation
5013      *         });
5014      *     }
5015      * });
5016      * @constructs
5017      * @extends enchant.Scene
5018      */
5019     initialize: function() {
5020         enchant.Scene.call(this);
5021         this.backgroundColor = '#000';
5022         var barWidth = this.width * 0.4 | 0;
5023         var barHeight = this.width * 0.05 | 0;
5024         var border = barWidth * 0.03 | 0;
5025         var bar = new enchant.Sprite(barWidth, barHeight);
5026         bar.disableCollection();
5027         bar.x = (this.width - barWidth) / 2;
5028         bar.y = (this.height - barHeight) / 2;
5029         var image = new enchant.Surface(barWidth, barHeight);
5030         image.context.fillStyle = '#fff';
5031         image.context.fillRect(0, 0, barWidth, barHeight);
5032         image.context.fillStyle = '#000';
5033         image.context.fillRect(border, border, barWidth - border * 2, barHeight - border * 2);
5034         bar.image = image;
5035         var progress = 0, _progress = 0;
5036         this.addEventListener('progress', function(e) {
5037             // avoid #167 https://github.com/wise9/enchant.js/issues/177
5038             progress = e.loaded / e.total * 1.0;
5039         });
5040         bar.addEventListener('enterframe', function() {
5041             _progress *= 0.9;
5042             _progress += progress * 0.1;
5043             image.context.fillStyle = '#fff';
5044             image.context.fillRect(border, 0, (barWidth - border * 2) * _progress, barHeight);
5045         });
5046         this.addChild(bar);
5047         this.addEventListener('load', function(e) {
5048             var core = enchant.Core.instance;
5049             core.removeScene(core.loadingScene);
5050             core.dispatchEvent(e);
5051         });
5052     }
5053 });
5054 
5055 /**
5056  * @scope enchant.CanvasScene.prototype
5057  */
5058 enchant.CanvasScene = enchant.Class.create(enchant.Scene, {
5059     /**
5060      * @name enchant.CanvasScene
5061      * @class
5062      * すべての子をCanvasに描画するScene.
5063      * @constructs
5064      * @extends enchant.Scene
5065      */
5066     initialize: function() {
5067         enchant.Scene.call(this);
5068         this.addLayer('Canvas');
5069     },
5070     _determineEventTarget: function(e) {
5071         var target = this._layers.Canvas._determineEventTarget(e);
5072         if (!target) {
5073             target = this;
5074         }
5075         return target;
5076     },
5077     _onchildadded: function(e) {
5078         var child = e.node;
5079         var next = e.next;
5080         child._layer = this._layers.Canvas;
5081         this._layers.Canvas.insertBefore(child, next);
5082     },
5083     _onenter: function() {
5084         this._layers.Canvas._startRendering();
5085         enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe);
5086     },
5087     _onexit: function() {
5088         this._layers.Canvas._stopRendering();
5089         enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe);
5090     }
5091 });
5092 
5093 /**
5094  * @scope enchant.DOMScene.prototype
5095  */
5096 enchant.DOMScene = enchant.Class.create(enchant.Scene, {
5097     /**
5098      * @name enchant.DOMScene
5099      * @class
5100      * すべての子をDOMで描画するScene.
5101      * @constructs
5102      * @extends enchant.Scene
5103      */
5104     initialize: function() {
5105         enchant.Scene.call(this);
5106         this.addLayer('Dom');
5107     },
5108     _determineEventTarget: function(e) {
5109         var target = this._layers.Dom._determineEventTarget(e);
5110         if (!target) {
5111             target = this;
5112         }
5113         return target;
5114     },
5115     _onchildadded: function(e) {
5116         var child = e.node;
5117         var next = e.next;
5118         child._layer = this._layers.Dom;
5119         this._layers.Dom.insertBefore(child, next);
5120     },
5121     _onenter: function() {
5122         this._layers.Dom._startRendering();
5123         enchant.Core.instance.addEventListener('exitframe', this._dispatchExitframe);
5124     },
5125     _onexit: function() {
5126         this._layers.Dom._stopRendering();
5127         enchant.Core.instance.removeEventListener('exitframe', this._dispatchExitframe);
5128     }
5129 });
5130 
5131 /**
5132  * @scope enchant.Surface.prototype
5133  */
5134 enchant.Surface = enchant.Class.create(enchant.EventTarget, {
5135     /**
5136      * @name enchant.Surface
5137      * @class
5138      * canvas要素をラップしたクラス.
5139      *
5140      * {@link enchant.Sprite} や {@link enchant.Map} のimageプロパティに設定して表示させることができる.
5141      * Canvas APIにアクセスしたいときは {@link enchant.Surface#context} プロパティを用いる.
5142      *
5143      * @example
5144      * // 円を表示するSpriteを作成する
5145      * var ball = new Sprite(50, 50);
5146      * var surface = new Surface(50, 50);
5147      * surface.context.beginPath();
5148      * surface.context.arc(25, 25, 25, 0, Math.PI*2, true);
5149      * surface.context.fill();
5150      * ball.image = surface;
5151      *
5152      * @param {Number} width Surfaceの横幅.
5153      * @param {Number} height Surfaceの高さ.
5154      * @constructs
5155      * @extends enchant.EventTarget
5156      */
5157     initialize: function(width, height) {
5158         enchant.EventTarget.call(this);
5159 
5160         var core = enchant.Core.instance;
5161 
5162         /**
5163          * Surfaceの横幅.
5164          * @type Number
5165          */
5166         this.width = Math.ceil(width);
5167         /**
5168          * Surfaceの高さ.
5169          * @type Number
5170          */
5171         this.height = Math.ceil(height);
5172         /**
5173          * Surfaceの描画コンテクスト.
5174          * @type CanvasRenderingContext2D
5175          */
5176         this.context = null;
5177 
5178         var id = 'enchant-surface' + core._surfaceID++;
5179         if (document.getCSSCanvasContext) {
5180             this.context = document.getCSSCanvasContext('2d', id, width, height);
5181             this._element = this.context.canvas;
5182             this._css = '-webkit-canvas(' + id + ')';
5183             var context = this.context;
5184         } else if (document.mozSetImageElement) {
5185             this._element = document.createElement('canvas');
5186             this._element.width = width;
5187             this._element.height = height;
5188             this._css = '-moz-element(#' + id + ')';
5189             this.context = this._element.getContext('2d');
5190             document.mozSetImageElement(id, this._element);
5191         } else {
5192             this._element = document.createElement('canvas');
5193             this._element.width = width;
5194             this._element.height = height;
5195             this._element.style.position = 'absolute';
5196             this.context = this._element.getContext('2d');
5197 
5198             enchant.ENV.CANVAS_DRAWING_METHODS.forEach(function(name) {
5199                 var method = this.context[name];
5200                 this.context[name] = function() {
5201                     method.apply(this, arguments);
5202                     this._dirty = true;
5203                 };
5204             }, this);
5205         }
5206     },
5207     /**
5208      * Surfaceから1ピクセル取得する.
5209      * @param {Number} x 取得するピクセルのx座標.
5210      * @param {Number} y 取得するピクセルのy座標.
5211      * @return {Number[]} ピクセルの情報を[r, g, b, a]の形式で持つ配列.
5212      */
5213     getPixel: function(x, y) {
5214         return this.context.getImageData(x, y, 1, 1).data;
5215     },
5216     /**
5217      * Surfaceに1ピクセル設定する.
5218      * @param {Number} x 設定するピクセルのx座標.
5219      * @param {Number} y 設定するピクセルのy座標.
5220      * @param {Number} r 設定するピクセルのrの値.
5221      * @param {Number} g 設定するピクセルのgの値.
5222      * @param {Number} b 設定するピクセルのbの値.
5223      * @param {Number} a 設定するピクセルの透明度.
5224      */
5225     setPixel: function(x, y, r, g, b, a) {
5226         var pixel = this.context.createImageData(1, 1);
5227         pixel.data[0] = r;
5228         pixel.data[1] = g;
5229         pixel.data[2] = b;
5230         pixel.data[3] = a;
5231         this.context.putImageData(pixel, x, y);
5232     },
5233     /**
5234      * Surfaceの全ピクセルをクリアし透明度0の黒に設定する.
5235      */
5236     clear: function() {
5237         this.context.clearRect(0, 0, this.width, this.height);
5238     },
5239     /**
5240      * Surfaceに対して引数で指定されたSurfaceを描画する.
5241      *
5242      * Canvas APIのdrawImageをラップしており, 描画する矩形を同様の形式で指定できる.
5243      *
5244      * @example
5245      * var src = core.assets['src.gif'];
5246      * var dst = new Surface(100, 100);
5247      * dst.draw(src);         // ソースを(0, 0)に描画
5248      * dst.draw(src, 50, 50); // ソースを(50, 50)に描画
5249      * // ソースを(50, 50)に縦横30ピクセル分だけ描画
5250      * dst.draw(src, 50, 50, 30, 30);
5251      * // ソースの(10, 10)から縦横40ピクセルの領域を(50, 50)に縦横30ピクセルに縮小して描画
5252      * dst.draw(src, 10, 10, 40, 40, 50, 50, 30, 30);
5253      *
5254      * @param {enchant.Surface} image 描画に用いるSurface.
5255      */
5256     draw: function(image) {
5257         image = image._element;
5258         if (arguments.length === 1) {
5259             this.context.drawImage(image, 0, 0);
5260         } else {
5261             var args = arguments;
5262             args[0] = image;
5263             this.context.drawImage.apply(this.context, args);
5264         }
5265     },
5266     /**
5267      * Surfaceを複製する.
5268      * @return {enchant.Surface} 複製されたSurface.
5269      */
5270     clone: function() {
5271         var clone = new enchant.Surface(this.width, this.height);
5272         clone.draw(this);
5273         return clone;
5274     },
5275     /**
5276      * SurfaceからdataスキームのURLを生成する.
5277      * @return {String} Surfaceを表すdataスキームのURL.
5278      */
5279     toDataURL: function() {
5280         var src = this._element.src;
5281         if (src) {
5282             if (src.slice(0, 5) === 'data:') {
5283                 return src;
5284             } else {
5285                 return this.clone().toDataURL();
5286             }
5287         } else {
5288             return this._element.toDataURL();
5289         }
5290     }
5291 });
5292 
5293 /**
5294  * 画像ファイルを読み込んでSurfaceオブジェクトを作成する.
5295  *
5296  * このメソッドによって作成されたSurfaceはimg要素をラップしており {@link enchant.Surface#context} プロパティに
5297  * アクセスしたり {@link enchant.Surface#draw}, {@link enchant.Surface#clear}, {@link enchant.Surface#getPixel},
5298  * {@link enchant.Surface#setPixel} メソッドなどの呼び出しでCanvas APIを使った画像操作を行うことはできない.
5299  * ただし{@link enchant.Surface#draw} メソッドの引数とすることはでき,
5300  * ほかのSurfaceに描画した上で画像操作を行うことはできる(クロスドメインでロードした
5301  * 場合はピクセルを取得するなど画像操作の一部が制限される).
5302  *
5303  * @param {String} src ロードする画像ファイルのパス.
5304  * @param {Function} callback ロード完了時のコールバック.
5305  * @param {Function} [onerror] ロード失敗時のコールバック.
5306  * @static
5307  * @return {enchant.Surface} Surface
5308  */
5309 enchant.Surface.load = function(src, callback, onerror) {
5310     var image = new Image();
5311     var surface = Object.create(enchant.Surface.prototype, {
5312         context: { value: null },
5313         _css: { value: 'url(' + src + ')' },
5314         _element: { value: image }
5315     });
5316     enchant.EventTarget.call(surface);
5317     onerror = onerror || function() {};
5318     surface.addEventListener('load', callback);
5319     surface.addEventListener('error', onerror);
5320     image.onerror = function() {
5321         var e = new enchant.Event(enchant.Event.ERROR);
5322         e.message = 'Cannot load an asset: ' + image.src;
5323         enchant.Core.instance.dispatchEvent(e);
5324         surface.dispatchEvent(e);
5325     };
5326     image.onload = function() {
5327         surface.width = image.width;
5328         surface.height = image.height;
5329         surface.dispatchEvent(new enchant.Event('load'));
5330     };
5331     image.src = src;
5332     return surface;
5333 };
5334 enchant.Surface._staticCanvas2DContext = document.createElement('canvas').getContext('2d');
5335 
5336 enchant.Surface._getPattern = function(surface, force) {
5337     if (!surface._pattern || force) {
5338         surface._pattern = this._staticCanvas2DContext.createPattern(surface._element, 'repeat');
5339     }
5340     return surface._pattern;
5341 };
5342 
5343 if (window.Deferred) {
5344     enchant.Deferred = window.Deferred;
5345 } else {
5346     /**
5347      * @scope enchant.Deferred.prototype
5348      */
5349     enchant.Deferred = enchant.Class.create({
5350         /**
5351          * @name enchant.Deferred
5352          * @class
5353          * 非同期処理を扱うためのクラス.
5354          * jsdeferredのAPIを模倣している.
5355          * jQuery.Deferredとの互換性はない.
5356          * <br/>
5357          * See: <a href="http://cho45.stfuawsc.com/jsdeferred/">
5358          * http://cho45.stfuawsc.com/jsdeferred/</a>
5359          *
5360          * @example
5361          * enchant.Deferred
5362          *     .next(function() {
5363          *         return 42;
5364          *     })
5365          *     .next(function(n) {
5366          *         console.log(n); // 42
5367          *     })
5368          *     .next(function() {
5369          *         return core.load('img.png'); // wait loading
5370          *     })
5371          *     .next(function() {
5372          *         var img = core.assets['img.png'];
5373          *         console.log(img instanceof enchant.Surface); // true
5374          *         throw new Error('!!!');
5375          *     })
5376          *     .next(function() {
5377          *         // skip
5378          *     })
5379          *     .error(function(err) {
5380          *          console.log(err.message); // !!!
5381          *     });
5382          *
5383          * @constructs
5384          */
5385         initialize: function() {
5386             this._succ = this._fail = this._next = this._id = null;
5387             this._tail = this;
5388         },
5389         /**
5390          * 後続の処理を追加する.
5391          * @param {Function} func 追加する処理.
5392          */
5393         next: function(func) {
5394             var q = new enchant.Deferred();
5395             q._succ = func;
5396             return this._add(q);
5397         },
5398         /**
5399          * エラー処理を追加する.
5400          * @param {Function} func 追加するエラー処理.
5401          */
5402         error: function(func) {
5403             var q = new enchant.Deferred();
5404             q._fail = func;
5405             return this._add(q);
5406         },
5407         _add: function(queue) {
5408             this._tail._next = queue;
5409             this._tail = queue;
5410             return this;
5411         },
5412         /**
5413          * 値を伝播させる.
5414          * @param {*} arg 次の処理に渡す値.
5415          */
5416         call: function(arg) {
5417             var received;
5418             var queue = this;
5419             while (queue && !queue._succ) {
5420                 queue = queue._next;
5421             }
5422             if (!(queue instanceof enchant.Deferred)) {
5423                 return;
5424             }
5425             try {
5426                 received = queue._succ(arg);
5427             } catch (e) {
5428                 return queue.fail(e);
5429             }
5430             if (received instanceof enchant.Deferred) {
5431                 enchant.Deferred._insert(queue, received);
5432             } else if (queue._next instanceof enchant.Deferred) {
5433                 queue._next.call(received);
5434             }
5435         },
5436         /**
5437          * エラーを伝播させる.
5438          * @param {*} arg エラーとして伝播させる値.
5439          */
5440         fail: function(arg) {
5441             var result, err,
5442                 queue = this;
5443             while (queue && !queue._fail) {
5444                 queue = queue._next;
5445             }
5446             if (queue instanceof enchant.Deferred) {
5447                 result = queue._fail(arg);
5448                 queue.call(result);
5449             } else if (arg instanceof Error) {
5450                 throw arg;
5451             } else {
5452                 err = new Error('failed in Deferred');
5453                 err.arg = arg;
5454                 throw err;
5455             }
5456         }
5457     });
5458     enchant.Deferred._insert = function(queue, ins) {
5459         if (queue._next instanceof enchant.Deferred) {
5460             ins._tail._next = queue._next;
5461         }
5462         queue._next = ins;
5463     };
5464     /**
5465      * タイマーで起動するDeferredオブジェクトを生成する.
5466      * @param {Function} func
5467      * @return {enchant.Deferred} 生成されたDeferredオブジェクト.
5468      * @static
5469      */
5470     enchant.Deferred.next = function(func) {
5471         var q = new enchant.Deferred().next(func);
5472         q._id = setTimeout(function() { q.call(); }, 0);
5473         return q;
5474     };
5475     /**
5476      * 複数のDeferredオブジェクトを待つDeferredオブジェクトを生成する.
5477      * @param {Object|enchant.Deferred[]} arg
5478      * @return {enchant.Deferred} 生成されたDeferredオブジェクト.
5479      *
5480      * @example
5481      * // array
5482      * enchant.Deferred
5483      *     .parallel([
5484      *         enchant.Deferred.next(function() {
5485      *             return 24;
5486      *         }),
5487      *         enchant.Deferred.next(function() {
5488      *             return 42;
5489      *         })
5490      *     ])
5491      *     .next(function(arg) {
5492      *         console.log(arg); // [ 24, 42 ]
5493      *     });
5494      * // object
5495      * enchant.Deferred
5496      *     .parallel({
5497      *         foo: enchant.Deferred.next(function() {
5498      *             return 24;
5499      *         }),
5500      *         bar: enchant.Deferred.next(function() {
5501      *             return 42;
5502      *         })
5503      *     })
5504      *     .next(function(arg) {
5505      *         console.log(arg.foo); // 24
5506      *         console.log(arg.bar); // 42
5507      *     });
5508      *
5509      * @static
5510      */
5511     enchant.Deferred.parallel = function(arg) {
5512         var q = new enchant.Deferred();
5513         q._id = setTimeout(function() { q.call(); }, 0);
5514         var progress = 0;
5515         var ret = (arg instanceof Array) ? [] : {};
5516         var p = new enchant.Deferred();
5517         for (var prop in arg) {
5518             if (arg.hasOwnProperty(prop)) {
5519                 progress++;
5520                 /*jshint loopfunc:true */
5521                 (function(queue, name) {
5522                     queue.next(function(arg) {
5523                         progress--;
5524                         ret[name] = arg;
5525                         if (progress <= 0) {
5526                             p.call(ret);
5527                         }
5528                     })
5529                     .error(function(err) { p.fail(err); });
5530                     if (typeof queue._id === 'number') {
5531                         clearTimeout(queue._id);
5532                     }
5533                     queue._id = setTimeout(function() { queue.call(); }, 0);
5534                 }(arg[prop], prop));
5535             }
5536         }
5537         if (!progress) {
5538             p._id = setTimeout(function() { p.call(ret); }, 0);
5539         }
5540         return q.next(function() { return p; });
5541     };
5542 }
5543 
5544 /**
5545  * @scope enchant.DOMSound.prototype
5546  */
5547 enchant.DOMSound = enchant.Class.create(enchant.EventTarget, {
5548     /**
5549      * @name enchant.DOMSound
5550      * @class
5551      * audio要素をラップしたクラス.
5552      *
5553      * MP3ファイルの再生はSafari, Chrome, Firefox, Opera, IEが対応
5554      * (Firefox, OperaではFlashを経由して再生). WAVEファイルの再生は
5555      * Safari, Chrome, Firefox, Operaが対応している. ブラウザが音声ファイル
5556      * のコーデックに対応していない場合は再生されない.
5557      *
5558      * コンストラクタではなく {@link enchant.DOMSound.load} を通じてインスタンスを作成する.
5559      * @constructs
5560      * @extends enchant.EventTarget
5561      */
5562     initialize: function() {
5563         enchant.EventTarget.call(this);
5564         /**
5565          * Soundの再生時間 (秒).
5566          * @type Number
5567          */
5568         this.duration = 0;
5569         throw new Error("Illegal Constructor");
5570     },
5571     /**
5572      * 再生を開始する.
5573      */
5574     play: function() {
5575         if (this._element) {
5576             this._element.play();
5577         }
5578     },
5579     /**
5580      * 再生を中断する.
5581      */
5582     pause: function() {
5583         if (this._element) {
5584             this._element.pause();
5585         }
5586     },
5587     /**
5588      * 再生を停止する.
5589      */
5590     stop: function() {
5591         this.pause();
5592         this.currentTime = 0;
5593     },
5594     /**
5595      * Soundを複製する.
5596      * @return {enchant.DOMSound} 複製されたSound.
5597      */
5598     clone: function() {
5599         var clone;
5600         if (this._element instanceof Audio) {
5601             clone = Object.create(enchant.DOMSound.prototype, {
5602                 _element: { value: this._element.cloneNode(false) },
5603                 duration: { value: this.duration }
5604             });
5605         } else if (enchant.ENV.USE_FLASH_SOUND) {
5606             return this;
5607         } else {
5608             clone = Object.create(enchant.DOMSound.prototype);
5609         }
5610         enchant.EventTarget.call(clone);
5611         return clone;
5612     },
5613     /**
5614      * 現在の再生位置 (秒).
5615      * @type Number
5616      */
5617     currentTime: {
5618         get: function() {
5619             return this._element ? this._element.currentTime : 0;
5620         },
5621         set: function(time) {
5622             if (this._element) {
5623                 this._element.currentTime = time;
5624             }
5625         }
5626     },
5627     /**
5628      * ボリューム. 0 (無音) ~ 1 (フルボリューム).
5629      * @type Number
5630      */
5631     volume: {
5632         get: function() {
5633             return this._element ? this._element.volume : 1;
5634         },
5635         set: function(volume) {
5636             if (this._element) {
5637                 this._element.volume = volume;
5638             }
5639         }
5640     }
5641 });
5642 
5643 /**
5644  * 音声ファイルを読み込んでDOMSoundオブジェクトを作成する.
5645  * @param {String} src ロードする音声ファイルのパス.
5646  * @param {String} [type] 音声ファイルのMIME Type.
5647  * @param {Function} [callback] ロード完了時のコールバック.
5648  * @param {Function} [onerror] ロード失敗時のコールバック.
5649  * @return {enchant.DOMSound} DOMSound
5650  * @static
5651  */
5652 enchant.DOMSound.load = function(src, type, callback, onerror) {
5653     if (type == null) {
5654         var ext = enchant.Core.findExt(src);
5655         if (ext) {
5656             type = 'audio/' + ext;
5657         } else {
5658             type = '';
5659         }
5660     }
5661     type = type.replace('mp3', 'mpeg').replace('m4a', 'mp4');
5662     callback = callback || function() {};
5663     onerror = onerror || function() {};
5664 
5665     var sound = Object.create(enchant.DOMSound.prototype);
5666     enchant.EventTarget.call(sound);
5667     sound.addEventListener('load', callback);
5668     sound.addEventListener('error', onerror);
5669     var audio = new Audio();
5670     if (!enchant.ENV.SOUND_ENABLED_ON_MOBILE_SAFARI &&
5671         enchant.ENV.VENDOR_PREFIX === 'webkit' && enchant.ENV.TOUCH_ENABLED) {
5672         window.setTimeout(function() {
5673             sound.dispatchEvent(new enchant.Event('load'));
5674         }, 0);
5675     } else {
5676         if (!enchant.ENV.USE_FLASH_SOUND && audio.canPlayType(type)) {
5677             audio.addEventListener('canplaythrough', function canplay() {
5678                 sound.duration = audio.duration;
5679                 sound.dispatchEvent(new enchant.Event('load'));
5680                 audio.removeEventListener('canplaythrough', canplay);
5681             }, false);
5682             audio.src = src;
5683             audio.load();
5684             audio.autoplay = false;
5685             audio.onerror = function() {
5686                 var e = new enchant.Event(enchant.Event.ERROR);
5687                 e.message = 'Cannot load an asset: ' + audio.src;
5688                 enchant.Core.instance.dispatchEvent(e);
5689                 sound.dispatchEvent(e);
5690             };
5691             sound._element = audio;
5692         } else if (type === 'audio/mpeg') {
5693             var embed = document.createElement('embed');
5694             var id = 'enchant-audio' + enchant.Core.instance._soundID++;
5695             embed.width = embed.height = 1;
5696             embed.name = id;
5697             embed.src = 'sound.swf?id=' + id + '&src=' + src;
5698             embed.allowscriptaccess = 'always';
5699             embed.style.position = 'absolute';
5700             embed.style.left = '-1px';
5701             sound.addEventListener('load', function() {
5702                 Object.defineProperties(embed, {
5703                     currentTime: {
5704                         get: function() {
5705                             return embed.getCurrentTime();
5706                         },
5707                         set: function(time) {
5708                             embed.setCurrentTime(time);
5709                         }
5710                     },
5711                     volume: {
5712                         get: function() {
5713                             return embed.getVolume();
5714                         },
5715                         set: function(volume) {
5716                             embed.setVolume(volume);
5717                         }
5718                     }
5719                 });
5720                 sound._element = embed;
5721                 sound.duration = embed.getDuration();
5722             });
5723             enchant.Core.instance._element.appendChild(embed);
5724             enchant.DOMSound[id] = sound;
5725         } else {
5726             window.setTimeout(function() {
5727                 sound.dispatchEvent(new enchant.Event('load'));
5728             }, 0);
5729         }
5730     }
5731     return sound;
5732 };
5733 
5734 window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext;
5735 
5736 /**
5737  * @scope enchant.WebAudioSound.prototype
5738  */
5739 enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, {
5740     /**
5741      * @name enchant.WebAudioSound
5742      * @class
5743      * WebAudioをラップしたクラス.
5744      * @constructs
5745      * @extends enchant.EventTarget
5746      */
5747     initialize: function() {
5748         if (!window.AudioContext) {
5749             throw new Error("This browser does not support WebAudio API.");
5750         }
5751         enchant.EventTarget.call(this);
5752         if (!enchant.WebAudioSound.audioContext) {
5753           enchant.WebAudioSound.audioContext = new window.AudioContext();
5754           enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination;
5755         }
5756         this.context = enchant.WebAudioSound.audioContext;
5757         this.src = this.context.createBufferSource();
5758         this.buffer = null;
5759         this._volume = 1;
5760         this._currentTime = 0;
5761         this._state = 0;
5762         this.connectTarget = enchant.WebAudioSound.destination;
5763     },
5764     /**
5765      * 再生を開始する.
5766      * @param {Boolean} [dup=false] trueならオブジェクトの現在の再生を残したまま新しく音声を再生する.
5767      */
5768     play: function(dup) {
5769         if (this._state === 1 && !dup) {
5770             this.src.disconnect();
5771         }
5772         if (this._state !== 2) {
5773             this._currentTime = 0;
5774         }
5775         var offset = this._currentTime;
5776         var actx = this.context;
5777         this.src = actx.createBufferSource();
5778         if (actx.createGain != null) {
5779             this._gain = actx.createGain();
5780         } else {
5781             this._gain = actx.createGainNode();
5782         }
5783         this.src.buffer = this.buffer;
5784         this._gain.gain.value = this._volume;
5785 
5786         this.src.connect(this._gain);
5787         this._gain.connect(this.connectTarget);
5788         if (this.src.start != null) {
5789             this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7);
5790         } else {
5791             this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7);
5792         }
5793         this._startTime = actx.currentTime - this._currentTime;
5794         this._state = 1;
5795     },
5796     /**
5797      * 再生を中断する.
5798      */
5799     pause: function() {
5800         var currentTime = this.currentTime;
5801         if (currentTime === this.duration) {
5802             return;
5803         }
5804         if (this.src.stop != null) {
5805             this.src.stop(0);
5806         } else {
5807             this.src.noteOff(0);
5808         }
5809         this._currentTime = currentTime;
5810         this._state = 2;
5811     },
5812     /**
5813      * 再生を停止する.
5814      */
5815     stop: function() {
5816         if (this.src.stop != null) {
5817             this.src.stop(0);
5818         } else {
5819             this.src.noteOff(0);
5820         }
5821         this._state = 0;
5822     },
5823     /**
5824      * Soundを複製する.
5825      * @return {enchant.WebAudioSound} 複製されたSound.
5826      */
5827     clone: function() {
5828         var sound = new enchant.WebAudioSound();
5829         sound.buffer = this.buffer;
5830         return sound;
5831     },
5832     /**
5833      * Soundの再生時間 (秒).
5834      * @type Number
5835      */
5836     duration: {
5837         get: function() {
5838             if (this.buffer) {
5839                 return this.buffer.duration;
5840             } else {
5841                 return 0;
5842             }
5843         }
5844     },
5845     /**
5846      * ボリューム. 0 (無音) ~ 1 (フルボリューム).
5847      * @type Number
5848      */
5849     volume: {
5850         get: function() {
5851             return this._volume;
5852         },
5853         set: function(volume) {
5854             volume = Math.max(0, Math.min(1, volume));
5855             this._volume = volume;
5856             if (this.src) {
5857                 this._gain.gain.value = volume;
5858             }
5859         }
5860     },
5861     /**
5862      * 現在の再生位置 (秒).
5863      * @type Number
5864      */
5865     currentTime: {
5866         get: function() {
5867             return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime));
5868         },
5869         set: function(time) {
5870             this._currentTime = time;
5871             if (this._state !== 2) {
5872                 this.play(false);
5873             }
5874         }
5875     }
5876 });
5877 
5878 /**
5879  * 音声ファイルを読み込んでWebAudioSoundオブジェクトを作成する.
5880  * @param {String} src ロードする音声ファイルのパス.
5881  * @param {String} [type] 音声ファイルのMIME Type.
5882  * @param {Function} [callback] ロード完了時のコールバック.
5883  * @param {Function} [onerror] ロード失敗時のコールバック.
5884  * @return {enchant.WebAudioSound} WebAudioSound
5885  * @static
5886  */
5887 enchant.WebAudioSound.load = function(src, type, callback, onerror) {
5888     var canPlay = (new Audio()).canPlayType(type);
5889     var sound = new enchant.WebAudioSound();
5890     callback = callback || function() {};
5891     onerror = onerror || function() {};
5892     sound.addEventListener(enchant.Event.LOAD, callback);
5893     sound.addEventListener(enchant.Event.ERROR, onerror);
5894     function dispatchErrorEvent() {
5895         var e = new enchant.Event(enchant.Event.ERROR);
5896         e.message = 'Cannot load an asset: ' + src;
5897         enchant.Core.instance.dispatchEvent(e);
5898         sound.dispatchEvent(e);
5899     }
5900     var actx, xhr;
5901     if (canPlay === 'maybe' || canPlay === 'probably') {
5902         actx = enchant.WebAudioSound.audioContext;
5903         xhr = new XMLHttpRequest();
5904         xhr.open('GET', src, true);
5905         xhr.responseType = 'arraybuffer';
5906         xhr.onload = function() {
5907             actx.decodeAudioData(xhr.response, function(buffer) {
5908                 sound.buffer = buffer;
5909                 sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
5910             }, dispatchErrorEvent);
5911         };
5912         xhr.onerror = dispatchErrorEvent;
5913         xhr.send(null);
5914     } else {
5915         setTimeout(dispatchErrorEvent,  50);
5916     }
5917     return sound;
5918 };
5919 
5920 enchant.Sound = window.AudioContext && enchant.ENV.USE_WEBAUDIO ? enchant.WebAudioSound : enchant.DOMSound;
5921 
5922 /*
5923  * ============================================================================================
5924  * Easing Equations v2.0
5925  * September 1, 2003
5926  * (c) 2003 Robert Penner, all rights reserved.
5927  * This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html.
5928  * ============================================================================================
5929  */
5930 
5931 /**
5932  * @namespace
5933  * イージング関数ライブラリ.
5934  * {@link enchant.Easing} 以下にある関数は全て t(現在の時刻), b(初期値), c(変化後の値), d(値の変化にかける時間) の引数を取り, 指定した時刻に取る値を返す.
5935  * ActionScript で広く使われている Robert Penner による Easing Equations を JavaScript に移植した.
5936  * <br/>
5937  * See: <a href="http://www.robertpenner.com/easing/">
5938  * http://www.robertpenner.com/easing/</a>
5939  * <br/>
5940  * See: <a href="http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf">
5941  * http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf</a>
5942  */
5943 enchant.Easing = {
5944     LINEAR: function(t, b, c, d) {
5945         return c * t / d + b;
5946     },
5947 
5948     SWING: function(t, b, c, d) {
5949         return c * (0.5 - Math.cos(((t / d) * Math.PI)) / 2) + b;
5950     },
5951 
5952     // *** quad
5953     QUAD_EASEIN: function(t, b, c, d) {
5954         return c * (t /= d) * t + b;
5955     },
5956 
5957     QUAD_EASEOUT: function(t, b, c, d) {
5958         return -c * (t /= d) * (t - 2) + b;
5959     },
5960 
5961     QUAD_EASEINOUT: function(t, b, c, d) {
5962         if ((t /= d / 2) < 1) {
5963             return c / 2 * t * t + b;
5964         }
5965         return -c / 2 * ((--t) * (t - 2) - 1) + b;
5966     },
5967 
5968     // *** cubic
5969     CUBIC_EASEIN: function(t, b, c, d) {
5970         return c * (t /= d) * t * t + b;
5971     },
5972 
5973     CUBIC_EASEOUT: function(t, b, c, d) {
5974         return c * ((t = t / d - 1) * t * t + 1) + b;
5975     },
5976 
5977     CUBIC_EASEINOUT: function(t, b, c, d) {
5978         if ((t /= d / 2) < 1) {
5979             return c / 2 * t * t * t + b;
5980         }
5981         return c / 2 * ((t -= 2) * t * t + 2) + b;
5982     },
5983 
5984     // *** quart
5985     QUART_EASEIN: function(t, b, c, d) {
5986         return c * (t /= d) * t * t * t + b;
5987     },
5988 
5989     QUART_EASEOUT: function(t, b, c, d) {
5990         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
5991     },
5992 
5993     QUART_EASEINOUT: function(t, b, c, d) {
5994         if ((t /= d / 2) < 1) {
5995             return c / 2 * t * t * t * t + b;
5996         }
5997         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
5998     },
5999 
6000     // *** quint
6001     QUINT_EASEIN: function(t, b, c, d) {
6002         return c * (t /= d) * t * t * t * t + b;
6003     },
6004 
6005     QUINT_EASEOUT: function(t, b, c, d) {
6006         return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
6007     },
6008 
6009     QUINT_EASEINOUT: function(t, b, c, d) {
6010         if ((t /= d / 2) < 1) {
6011             return c / 2 * t * t * t * t * t + b;
6012         }
6013         return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
6014     },
6015 
6016     // *** sin
6017     SIN_EASEIN: function(t, b, c, d) {
6018         return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
6019     },
6020 
6021     SIN_EASEOUT: function(t, b, c, d) {
6022         return c * Math.sin(t / d * (Math.PI / 2)) + b;
6023     },
6024 
6025     SIN_EASEINOUT: function(t, b, c, d) {
6026         return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
6027     },
6028 
6029     // *** circ
6030     CIRC_EASEIN: function(t, b, c, d) {
6031         return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
6032     },
6033 
6034     CIRC_EASEOUT: function(t, b, c, d) {
6035         return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
6036     },
6037 
6038     CIRC_EASEINOUT: function(t, b, c, d) {
6039         if ((t /= d / 2) < 1) {
6040             return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
6041         }
6042         return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
6043     },
6044 
6045     // *** elastic
6046     ELASTIC_EASEIN: function(t, b, c, d, a, p) {
6047         if (t === 0) {
6048             return b;
6049         }
6050         if ((t /= d) === 1) {
6051             return b + c;
6052         }
6053 
6054         if (!p) {
6055             p = d * 0.3;
6056         }
6057 
6058         var s;
6059         if (!a || a < Math.abs(c)) {
6060             a = c;
6061             s = p / 4;
6062         } else {
6063             s = p / (2 * Math.PI) * Math.asin(c / a);
6064         }
6065         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
6066     },
6067 
6068     ELASTIC_EASEOUT: function(t, b, c, d, a, p) {
6069         if (t === 0) {
6070             return b;
6071         }
6072         if ((t /= d) === 1) {
6073             return b + c;
6074         }
6075         if (!p) {
6076             p = d * 0.3;
6077         }
6078         var s;
6079         if (!a || a < Math.abs(c)) {
6080             a = c;
6081             s = p / 4;
6082         } else {
6083             s = p / (2 * Math.PI) * Math.asin(c / a);
6084         }
6085         return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
6086     },
6087 
6088     ELASTIC_EASEINOUT: function(t, b, c, d, a, p) {
6089         if (t === 0) {
6090             return b;
6091         }
6092         if ((t /= d / 2) === 2) {
6093             return b + c;
6094         }
6095         if (!p) {
6096             p = d * (0.3 * 1.5);
6097         }
6098         var s;
6099         if (!a || a < Math.abs(c)) {
6100             a = c;
6101             s = p / 4;
6102         } else {
6103             s = p / (2 * Math.PI) * Math.asin(c / a);
6104         }
6105         if (t < 1) {
6106             return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
6107         }
6108         return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b;
6109     },
6110 
6111     // *** bounce
6112     BOUNCE_EASEOUT: function(t, b, c, d) {
6113         if ((t /= d) < (1 / 2.75)) {
6114             return c * (7.5625 * t * t) + b;
6115         } else if (t < (2 / 2.75)) {
6116             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b;
6117         } else if (t < (2.5 / 2.75)) {
6118             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b;
6119         } else {
6120             return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b;
6121         }
6122     },
6123 
6124     BOUNCE_EASEIN: function(t, b, c, d) {
6125         return c - enchant.Easing.BOUNCE_EASEOUT(d - t, 0, c, d) + b;
6126     },
6127 
6128     BOUNCE_EASEINOUT: function(t, b, c, d) {
6129         if (t < d / 2) {
6130             return enchant.Easing.BOUNCE_EASEIN(t * 2, 0, c, d) * 0.5 + b;
6131         } else {
6132             return enchant.Easing.BOUNCE_EASEOUT(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
6133         }
6134 
6135     },
6136 
6137     // *** back
6138     BACK_EASEIN: function(t, b, c, d, s) {
6139         if (s === undefined) {
6140             s = 1.70158;
6141         }
6142         return c * (t /= d) * t * ((s + 1) * t - s) + b;
6143     },
6144 
6145     BACK_EASEOUT: function(t, b, c, d, s) {
6146         if (s === undefined) {
6147             s = 1.70158;
6148         }
6149         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
6150     },
6151 
6152     BACK_EASEINOUT: function(t, b, c, d, s) {
6153         if (s === undefined) {
6154             s = 1.70158;
6155         }
6156         if ((t /= d / 2) < 1) {
6157             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
6158         }
6159         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
6160     },
6161 
6162     // *** expo
6163     EXPO_EASEIN: function(t, b, c, d) {
6164         return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
6165     },
6166 
6167     EXPO_EASEOUT: function(t, b, c, d) {
6168         return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
6169     },
6170 
6171     EXPO_EASEINOUT: function(t, b, c, d) {
6172         if (t === 0) {
6173             return b;
6174         }
6175         if (t === d) {
6176             return b + c;
6177         }
6178         if ((t /= d / 2) < 1) {
6179             return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
6180         }
6181         return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
6182     }
6183 };
6184 
6185 /**
6186  * @scope enchant.ActionEventTarget.prototype
6187  */
6188 enchant.ActionEventTarget = enchant.Class.create(enchant.EventTarget, {
6189     /**
6190      * @name enchant.ActionEventTarget
6191      * @class
6192      * timelineの {@link enchant.Action} クラス向けに拡張された {@link enchant.EventTarget} クラス.
6193      * @constructs
6194      * @extends enchant.EventTarget
6195      */
6196     initialize: function() {
6197         enchant.EventTarget.apply(this, arguments);
6198     },
6199     dispatchEvent: function(e) {
6200         var target = this.node ? this.node : this;
6201 
6202         e.target = target;
6203         e.localX = e.x - target._offsetX;
6204         e.localY = e.y - target._offsetY;
6205 
6206         if (this['on' + e.type] != null) {
6207             this['on' + e.type].call(target, e);
6208         }
6209         var listeners = this._listeners[e.type];
6210         if (listeners != null) {
6211             listeners = listeners.slice();
6212             for (var i = 0, len = listeners.length; i < len; i++) {
6213                 listeners[i].call(target, e);
6214             }
6215         }
6216     }
6217 });
6218 
6219 /**
6220  * @scope enchant.Timeline.prototype
6221  */
6222 enchant.Timeline = enchant.Class.create(enchant.EventTarget, {
6223     /**
6224      * @name enchant.Timeline
6225      * @class
6226      * アニメーションを管理するためのクラス.
6227      *
6228      * 操作するノードひとつに対して, 必ずひとつのタイムラインが対応する.
6229      * タイムラインクラスは, 自身に様々なアクションを追加するメソッドを持っており,
6230      * これらを使うことで簡潔にアニメーションや様々な操作をすることができる.
6231      * タイムラインクラスはフレームとタイムのアニメーションができる.
6232      * @param {enchant.Node} node 操作の対象となるノード.
6233      * @constructs
6234      * @extends enchant.EventTarget
6235      */
6236     initialize: function(node) {
6237         enchant.EventTarget.call(this);
6238         this.node = node;
6239         this.queue = [];
6240         this.paused = false;
6241         this.looped = false;
6242         this.isFrameBased = true;
6243         this._parallel = null;
6244         this._activated = false;
6245         this.addEventListener(enchant.Event.ENTER_FRAME, this._onenterframe);
6246 
6247         var tl = this;
6248         this._nodeEventListener = function(e) {
6249             tl.dispatchEvent(e);
6250         };
6251     },
6252     /**
6253      * @private
6254      */
6255     _deactivateTimeline: function() {
6256         if (this._activated) {
6257             this._activated = false;
6258             this.node.removeEventListener('enterframe', this._nodeEventListener);
6259         }
6260     },
6261     /**
6262      * @private
6263      */
6264     _activateTimeline: function() {
6265         if (!this._activated && !this.paused) {
6266             this.node.addEventListener("enterframe", this._nodeEventListener);
6267             this._activated = true;
6268         }
6269     },
6270     /**
6271      * @private
6272      */
6273     _onenterframe: function(evt) {
6274         if (this.paused) {
6275             return;
6276         }
6277 
6278         this.tick(this.isFrameBased ? 1 : evt.elapsed);
6279     },
6280     /**
6281      * 一つのenchant.Event.ENTER_FRAMEイベントはアニメーションに一つの時間単位になる. (デフォルト)
6282      */
6283     setFrameBased: function() {
6284         this.isFrameBased = true;
6285     },
6286     /**
6287      * 一つのenchant.Event.ENTER_FRAMEイベントはアニメーションに前のフレームから経過した時間になる.
6288      */
6289     setTimeBased: function() {
6290         this.isFrameBased = false;
6291     },
6292     /**
6293      * キューの先頭にあるアクションを終了し, 次のアクションへ移行する.
6294      * アクションの中から呼び出されるが, 外から呼び出すこともできる.
6295      *
6296      * アクション実行中に, アクションが終了した場合,
6297      * もう一度 tick() 関数が呼ばれるため, 1フレームに複数のアクションが処理される場合もある.
6298      *
6299      * @example
6300      * sprite.tl.then(function A(){ .. }).then(function B(){ .. });
6301      * // 最初のフレームで A・B の関数どちらも実行される.
6302      */
6303     next: function(remainingTime) {
6304         var e, action = this.queue.shift();
6305 
6306         if (action) {
6307             e = new enchant.Event("actionend");
6308             e.timeline = this;
6309             action.dispatchEvent(e);
6310 
6311             e = new enchant.Event("removedfromtimeline");
6312             e.timeline = this;
6313             action.dispatchEvent(e);
6314 
6315             if (this.looped) {
6316                 this.add(action);
6317             }
6318         }
6319 
6320         if (this.queue.length === 0) {
6321             this._deactivateTimeline();
6322             return;
6323         }
6324 
6325         if (remainingTime > 0 || (this.queue[0] && this.queue[0].time === 0)) {
6326             var event = new enchant.Event("actiontick");
6327             event.elapsed = remainingTime;
6328             event.timeline = this;
6329             this.queue[0].dispatchEvent(event);
6330         }
6331     },
6332     /**
6333      * Timelineの時間を進める.
6334      * (キューの先頭にあるアクションに対して, actionstart/actiontickイベントを発行する)
6335      * @param {Number} elapsed 経過させる時間.
6336      */
6337     tick: function(elapsed) {
6338         if (this.queue.length > 0) {
6339             var action = this.queue[0];
6340             if (action.frame === 0) {
6341                 var f;
6342                 f = new enchant.Event("actionstart");
6343                 f.timeline = this;
6344                 action.dispatchEvent(f);
6345             }
6346 
6347             var e = new enchant.Event("actiontick");
6348             e.timeline = this;
6349             e.elapsed = elapsed;
6350             action.dispatchEvent(e);
6351         }
6352     },
6353     /**
6354      * タイムラインにアクションを追加する.
6355      * @param {enchant.Action} action 追加するアクション.
6356      * @return {enchant.Timeline} 自身.
6357      */
6358     add: function(action) {
6359         this._activateTimeline();
6360         if (this._parallel) {
6361             this._parallel.actions.push(action);
6362             this._parallel = null;
6363         } else {
6364             this.queue.push(action);
6365         }
6366         action.frame = 0;
6367 
6368         var e = new enchant.Event("addedtotimeline");
6369         e.timeline = this;
6370         action.dispatchEvent(e);
6371 
6372         e = new enchant.Event("actionadded");
6373         e.action = action;
6374         this.dispatchEvent(e);
6375 
6376         return this;
6377     },
6378     /**
6379      * アクションを簡単に追加するためのメソッド.
6380      * 実体は {@link enchant.Timeline#add} のラッパ.
6381      * @param {Object} params アクションの設定オブジェクト.
6382      * @return {enchant.Timeline} 自身.
6383      */
6384     action: function(params) {
6385         return this.add(new enchant.Action(params));
6386     },
6387     /**
6388      * トゥイーンを簡単に追加するためのメソッド.
6389      * 実体は {@link enchant.Timeline#add} のラッパ.
6390      * @param {Object} params トゥイーンの設定オブジェクト.
6391      * @return {enchant.Timeline} 自身.
6392      */
6393     tween: function(params) {
6394         return this.add(new enchant.Tween(params));
6395     },
6396     /**
6397      * タイムラインのキューをすべて破棄する. 終了イベントは発行されない.
6398      * @return {enchant.Timeline} 自身.
6399      */
6400     clear: function() {
6401         var e = new enchant.Event("removedfromtimeline");
6402         e.timeline = this;
6403 
6404         for (var i = 0, len = this.queue.length; i < len; i++) {
6405             this.queue[i].dispatchEvent(e);
6406         }
6407         this.queue = [];
6408         this._deactivateTimeline();
6409         return this;
6410     },
6411     /**
6412      * タイムラインを早送りする.
6413      * 指定したフレーム数が経過したのと同様の処理を, 瞬時に実行する.
6414      * 巻き戻しはできない.
6415      * @param {Number} frames スキップするフレーム数.
6416      * @return {enchant.Timeline} 自身.
6417      */
6418     skip: function(frames) {
6419         var event = new enchant.Event("enterframe");
6420         if (this.isFrameBased) {
6421             event.elapsed = 1;
6422         } else {
6423             event.elapsed = frames;
6424             frames = 1;
6425         }
6426         while (frames--) {
6427             this.dispatchEvent(event);
6428         }
6429         return this;
6430     },
6431     /**
6432      * タイムラインの実行を一時停止する.
6433      * @return {enchant.Timeline} 自身.
6434      */
6435     pause: function() {
6436         if (!this.paused) {
6437             this.paused = true;
6438             this._deactivateTimeline();
6439         }
6440         return this;
6441     },
6442     /**
6443      * タイムラインの実行を再開する.
6444      * @return {enchant.Timeline} 自身.
6445      */
6446     resume: function() {
6447         if (this.paused) {
6448             this.paused = false;
6449             this._activateTimeline();
6450         }
6451         return this;
6452     },
6453     /**
6454      * タイムラインをループさせる.
6455      * ループしているときに終了したアクションは, タイムラインから取り除かれた後,
6456      * 再度タイムラインに追加される. このアクションは, ループが解除されても残る.
6457      * @return {enchant.Timeline} 自身.
6458      */
6459     loop: function() {
6460         this.looped = true;
6461         return this;
6462     },
6463     /**
6464      * タイムラインのループを解除する.
6465      * @return {enchant.Timeline} 自身.
6466      */
6467     unloop: function() {
6468         this.looped = false;
6469         return this;
6470     },
6471     /**
6472      * 指定したフレーム数だけ待ち, 何もしないアクションを追加する.
6473      * @param {Number} time 待機するフレーム数.
6474      * @return {enchant.Timeline} 自身.
6475      */
6476     delay: function(time) {
6477         return this.action({
6478             time: time
6479         });
6480     },
6481     /**
6482      * @ignore
6483      * @param {Number} time
6484      */
6485     wait: function(time) {
6486         // reserved
6487         return this;
6488     },
6489     /**
6490      * 関数を実行し, 即時に次のアクションに移るアクションを追加する.
6491      * @param {Function} func 実行する関数.
6492      * @return {enchant.Timeline} 自身.
6493      */
6494     then: function(func) {
6495         return this.action({
6496             onactiontick: function(evt) {
6497                 func.call(this);
6498             },
6499             // if time is 0, next action will be immediately executed
6500             time: 0
6501         });
6502     },
6503     /**
6504      * 関数を実行し, 即時に次のアクションに移るアクションを追加する.
6505      * {@link enchant.Timeline#then} のシノニム.
6506      * @param {Function} func 実行する関数.
6507      * @return {enchant.Timeline} 自身.
6508      */
6509     exec: function(func) {
6510         return this.then(func);
6511     },
6512     /**
6513      * 実行したい関数を, フレーム数をキーとした連想配列(オブジェクト)で複数指定し追加する.
6514      * 内部的には {@link enchant.Timeline#delay}, {@link enchant.Timeline#then} を用いている.
6515      *
6516      * @example
6517      * sprite.tl.cue({
6518      *     10: function() {}, // 10フレーム経過した後に実行される関数
6519      *     20: function() {}, // 20フレーム経過した後に実行される関数
6520      *     30: function() {}  // 30フレーム経過した後に実行される関数
6521      * });
6522      *
6523      * @param {Object} cue キューオブジェクト.
6524      * @return {enchant.Timeline} 自身.
6525      */
6526     cue: function(cue) {
6527         var ptr = 0;
6528         for (var frame in cue) {
6529             if (cue.hasOwnProperty(frame)) {
6530                 this.delay(frame - ptr);
6531                 this.then(cue[frame]);
6532                 ptr = frame;
6533             }
6534         }
6535         return this;
6536     },
6537     /**
6538      * ある関数を指定したフレーム数繰り返し実行するアクションを追加する.
6539      * @param {Function} func 実行したい関数.
6540      * @param {Number} time 持続フレーム数.
6541      * @return {enchant.Timeline} 自身.
6542      */
6543     repeat: function(func, time) {
6544         return this.action({
6545             onactiontick: function(evt) {
6546                 func.call(this);
6547             },
6548             time: time
6549         });
6550     },
6551     /**
6552      * 複数のアクションを並列で実行したいときに指定する.
6553      * and で結ばれたすべてのアクションが終了するまで次のアクションには移行しない.
6554      *
6555      * @example
6556      * sprite.tl.fadeIn(30).and().rotateBy(360, 30);
6557      *
6558      * // 30フレームでフェードインしながら360度回転する.
6559      * @return {enchant.Timeline} 自身.
6560      */
6561     and: function() {
6562         var last = this.queue.pop();
6563         if (last instanceof enchant.ParallelAction) {
6564             this._parallel = last;
6565             this.queue.push(last);
6566         } else {
6567             var parallel = new enchant.ParallelAction();
6568             parallel.actions.push(last);
6569             this.queue.push(parallel);
6570             this._parallel = parallel;
6571         }
6572         return this;
6573     },
6574     /**
6575      * @ignore
6576      */
6577     or: function() {
6578         return this;
6579     },
6580     /**
6581      * @ignore
6582      */
6583     doAll: function(children) {
6584         return this;
6585     },
6586     /**
6587      * @ignore
6588      */
6589     waitAll: function() {
6590         return this;
6591     },
6592     /**
6593      * trueが返るまで, 関数を毎フレーム実行するアクションを追加する.
6594      *
6595      * @example
6596      * sprite.tl.waitUntil(function() {
6597      *     return --this.x < 0;
6598      * }).then(function(){ .. });
6599      * // x座標が負になるまで毎フレームx座標を減算し続ける.
6600      *
6601      * @param {Function} func 条件とする関数.
6602      * @return {enchant.Timeline} 自身.
6603      */
6604     waitUntil: function(func) {
6605         return this.action({
6606             onactiontick: function(evt) {
6607                 if (func.call(this)) {
6608                     evt.timeline.next();
6609                 }
6610             }
6611         });
6612     },
6613     /**
6614      * Entityの不透明度をなめらかに変えるアクションを追加する.
6615      * @param {Number} opacity 目標の不透明度.
6616      * @param {Number} time フレーム数.
6617      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6618      * @return {enchant.Timeline} 自身.
6619      */
6620     fadeTo: function(opacity, time, easing) {
6621         return this.tween({
6622             opacity: opacity,
6623             time: time,
6624             easing: easing
6625         });
6626     },
6627     /**
6628      * Entityをフェードインするアクションを追加する.
6629      * fadeTo(1, time, easing) のエイリアス.
6630      * @param {Number} time フレーム数.
6631      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6632      * @return {enchant.Timeline} 自身.
6633      */
6634     fadeIn: function(time, easing) {
6635         return this.fadeTo(1, time, easing);
6636     },
6637     /**
6638      * Entityをフェードアウトするアクションを追加する.
6639      * fadeTo(1, time, easing) のエイリアス.
6640      * @param {Number} time フレーム数.
6641      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6642      * @return {enchant.Timeline} 自身.
6643      */
6644     fadeOut: function(time, easing) {
6645         return this.fadeTo(0, time, easing);
6646     },
6647     /**
6648      * Entityの位置をなめらかに移動させるアクションを追加する.
6649      * @param {Number} x 目標のx座標.
6650      * @param {Number} y 目標のy座標.
6651      * @param {Number} time フレーム数.
6652      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6653      * @return {enchant.Timeline} 自身.
6654      */
6655     moveTo: function(x, y, time, easing) {
6656         return this.tween({
6657             x: x,
6658             y: y,
6659             time: time,
6660             easing: easing
6661         });
6662     },
6663     /**
6664      * Entityのx座標をなめらかに移動させるアクションを追加する.
6665      * @param {Number} x 目標のx座標.
6666      * @param {Number} time フレーム数.
6667      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6668      * @return {enchant.Timeline} 自身.
6669      */
6670     moveX: function(x, time, easing) {
6671         return this.tween({
6672             x: x,
6673             time: time,
6674             easing: easing
6675         });
6676     },
6677     /**
6678      * Entityのy座標をなめらかに移動させるアクションを追加する.
6679      * @param {Number} y 目標のy座標.
6680      * @param {Number} time フレーム数.
6681      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6682      * @return {enchant.Timeline} 自身.
6683      */
6684     moveY: function(y, time, easing) {
6685         return this.tween({
6686             y: y,
6687             time: time,
6688             easing: easing
6689         });
6690     },
6691     /**
6692      * Entityの位置をなめらかに変化させるアクションを追加する.
6693      * 座標は, アクション開始時からの相対座標で指定する.
6694      * @param {Number} x x軸方向の移動量.
6695      * @param {Number} y y軸方向の移動量.
6696      * @param {Number} time フレーム数.
6697      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6698      * @return {enchant.Timeline} 自身.
6699      */
6700     moveBy: function(x, y, time, easing) {
6701         return this.tween({
6702             x: function() {
6703                 return this.x + x;
6704             },
6705             y: function() {
6706                 return this.y + y;
6707             },
6708             time: time,
6709             easing: easing
6710         });
6711     },
6712     /**
6713      * Entityの不透明度を0にする. (即時)
6714      * @return {enchant.Timeline} 自身.
6715      */
6716     hide: function() {
6717         return this.then(function() {
6718             this.opacity = 0;
6719         });
6720     },
6721     /**
6722      * Entityの不透明度を1にする. (即時)
6723      * @return {enchant.Timeline} 自身.
6724      */
6725     show: function() {
6726         return this.then(function() {
6727             this.opacity = 1;
6728         });
6729     },
6730     /**
6731      * Entityをシーンから削除する.
6732      * シーンから削除された場合,  enterframe イベントは呼ばれなくなるので,
6733      * タイムラインも止まることに注意.
6734      * これ以降のアクションは, 再度シーンに追加されるまで実行されない.
6735      * @return {enchant.Timeline} 自身.
6736      */
6737     removeFromScene: function() {
6738         return this.then(function() {
6739             this.parentNode.removeChild(this);
6740         });
6741     },
6742     /**
6743      * Entityをなめらかに拡大・縮小するアクションを追加する.
6744      * @param {Number} scaleX x軸方向の縮尺.
6745      * @param {Number} [scaleY] y軸方向の縮尺. 省略した場合 scaleX と同じ.
6746      * @param {Number} time フレーム数.
6747      * @param {Function} [easing=enchant.Easing.LINEAR]
6748      * @return {enchant.Timeline} 自身.
6749      */
6750     scaleTo: function(scale, time, easing) {
6751         var scaleX, scaleY;
6752 
6753         if (typeof easing === "number") {
6754             scaleX = arguments[0];
6755             scaleY = arguments[1];
6756             time = arguments[2];
6757             easing = arguments[3];
6758         } else {
6759             scaleX = scaleY = scale;
6760         }
6761 
6762         return this.tween({
6763             scaleX: scaleX,
6764             scaleY: scaleY,
6765             time: time,
6766             easing: easing
6767         });
6768     },
6769     /**
6770      * Entityをなめらかに拡大・縮小させるアクションを追加する.
6771      * 相対縮尺 (アクション開始時の縮尺のn倍) で指定する.
6772      * @param {Number} scaleX x軸方向の相対縮尺.
6773      * @param {Number} [scaleY] y軸方向の相対縮尺. 省略した場合 scaleX と同じ.
6774      * @param {Number} time フレーム数.
6775      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6776      * @return {enchant.Timeline} 自身.
6777      */
6778     scaleBy: function(scale, time, easing) {
6779         var scaleX, scaleY;
6780 
6781         if (typeof easing === "number") {
6782             scaleX = arguments[0];
6783             scaleY = arguments[1];
6784             time = arguments[2];
6785             easing = arguments[3];
6786         } else {
6787             scaleX = scaleY = scale;
6788         }
6789 
6790         return this.tween({
6791             scaleX: function() {
6792                 return this.scaleX * scaleX;
6793             },
6794             scaleY: function() {
6795                 return this.scaleY * scaleY;
6796             },
6797             time: time,
6798             easing: easing
6799         });
6800     },
6801     /**
6802      * Entityをなめらかに回転させるアクションを追加する.
6803      * @param {Number} deg 目標の回転角度. (度数法)
6804      * @param {Number} time フレーム数.
6805      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6806      * @return {enchant.Timeline} 自身.
6807      */
6808     rotateTo: function(deg, time, easing) {
6809         return this.tween({
6810             rotation: deg,
6811             time: time,
6812             easing: easing
6813         });
6814     },
6815     /**
6816      * Entityをなめらかに回転させるアクションを追加する.
6817      * 角度は相対角度 (アクション開始時の角度から更にn度) で指定する.
6818      * @param {Number} deg 目標の相対角度. (度数法)
6819      * @param {Number} time フレーム数.
6820      * @param {Function} [easing=enchant.Easing.LINEAR] イージング関数.
6821      * @return {enchant.Timeline} 自身.
6822      */
6823     rotateBy: function(deg, time, easing) {
6824         return this.tween({
6825             rotation: function() {
6826                 return this.rotation + deg;
6827             },
6828             time: time,
6829             easing: easing
6830         });
6831     }
6832 });
6833 
6834 /**
6835  * @scope enchant.Action.prototype
6836  */
6837 enchant.Action = enchant.Class.create(enchant.ActionEventTarget, {
6838     /**
6839      * @name enchant.Action
6840      * @class
6841      * アニメーションタイムラインを構成する, 実行したい処理を指定するためのクラス.
6842      *
6843      * タイムラインに追加されたアクションは順に実行される.
6844      * アクションが開始・終了された時に actionstart, actionend イベントが発行され,
6845      * また1フレーム経過した時には actiontick イベントが発行される.
6846      * これらのイベントのリスナとして実行したい処理を指定する.
6847      *
6848      * time で指定されたフレーム数が経過すると自動的に次のアクションに移行するが,
6849      * null が指定されると, タイムラインの next メソッドが呼ばれるまで移行しない.
6850      * @param {Object} param
6851      * @param {Number} [param.time] アクションが持続するフレーム数. null が指定されると無限長.
6852      * @param {Function} [param.onactionstart] アクションが開始される時のイベントリスナ.
6853      * @param {Function} [param.onactiontick] アクションが1フレーム経過するときのイベントリスナ.
6854      * @param {Function} [param.onactionend] アクションがが終了する時のイベントリスナ.
6855      * @constructs
6856      * @extends enchant.ActionEventTarget
6857      */
6858     initialize: function(param) {
6859         enchant.ActionEventTarget.call(this);
6860         this.time = null;
6861         this.frame = 0;
6862         for (var key in param) {
6863             if (param.hasOwnProperty(key)) {
6864                 if (param[key] != null) {
6865                     this[key] = param[key];
6866                 }
6867             }
6868         }
6869         var action = this;
6870 
6871         this.timeline = null;
6872         this.node = null;
6873 
6874         this.addEventListener(enchant.Event.ADDED_TO_TIMELINE, function(evt) {
6875             action.timeline = evt.timeline;
6876             action.node = evt.timeline.node;
6877             action.frame = 0;
6878         });
6879 
6880         this.addEventListener(enchant.Event.REMOVED_FROM_TIMELINE, function() {
6881             action.timeline = null;
6882             action.node = null;
6883             action.frame = 0;
6884         });
6885 
6886         this.addEventListener(enchant.Event.ACTION_TICK, function(evt) {
6887             var remaining = action.time - (action.frame + evt.elapsed);
6888             if (action.time != null && remaining <= 0) {
6889                 action.frame = action.time;
6890                 evt.timeline.next(-remaining);
6891             } else {
6892                 action.frame += evt.elapsed;
6893             }
6894         });
6895 
6896     }
6897 });
6898 
6899 /**
6900  * @scope enchant.ParallelAction.prototype
6901  */
6902 enchant.ParallelAction = enchant.Class.create(enchant.Action, {
6903     /**
6904      * @name enchant.ParallelAction
6905      * @class
6906      * アクションを並列で実行するためのアクション.
6907      * 子アクションを複数持つことができる.
6908      * @constructs
6909      * @extends enchant.Action
6910      */
6911     initialize: function(param) {
6912         enchant.Action.call(this, param);
6913         /**
6914          * 子アクション.
6915          * @type enchant.Action[]
6916          */
6917         this.actions = [];
6918         /**
6919          * 実行が終了したアクション.
6920          * @type enchant.Action[]
6921          */
6922         this.endedActions = [];
6923         var that = this;
6924 
6925         this.addEventListener(enchant.Event.ACTION_START, function(evt) {
6926             for (var i = 0, len = that.actions.length; i < len; i++) {
6927                 that.actions[i].dispatchEvent(evt);
6928             }
6929         });
6930 
6931         this.addEventListener(enchant.Event.ACTION_TICK, function(evt) {
6932             var i, len, timeline = {
6933                 next: function(remaining) {
6934                     var action = that.actions[i];
6935                     that.actions.splice(i--, 1);
6936                     len = that.actions.length;
6937                     that.endedActions.push(action);
6938 
6939                     var e = new enchant.Event("actionend");
6940                     e.timeline = this;
6941                     action.dispatchEvent(e);
6942 
6943                     e = new enchant.Event("removedfromtimeline");
6944                     e.timeline = this;
6945                     action.dispatchEvent(e);
6946                 }
6947             };
6948 
6949             var e = new enchant.Event("actiontick");
6950             e.timeline = timeline;
6951             e.elapsed = evt.elapsed;
6952             for (i = 0, len = that.actions.length; i < len; i++) {
6953                 that.actions[i].dispatchEvent(e);
6954             }
6955 
6956             if (that.actions.length === 0) {
6957                 evt.timeline.next();
6958             }
6959         });
6960 
6961         this.addEventListener(enchant.Event.ADDED_TO_TIMELINE, function(evt) {
6962             for (var i = 0, len = that.actions.length; i < len; i++) {
6963                 that.actions[i].dispatchEvent(evt);
6964             }
6965         });
6966 
6967         this.addEventListener(enchant.Event.REMOVED_FROM_TIMELINE, function() {
6968             that.actions = that.endedActions;
6969             that.endedActions = [];
6970         });
6971 
6972     }
6973 });
6974 
6975 /**
6976  * @scope enchant.Tween.prototype
6977  */
6978 enchant.Tween = enchant.Class.create(enchant.Action, {
6979     /**
6980      * @name enchant.Tween
6981      * @class
6982      * オブジェクトの特定のプロパティを, なめらかに変更したい時に用いるためのアクションクラス.
6983      * アクションを扱いやすく拡張したクラス.
6984      *
6985      * コンストラクタに渡す設定オブジェクトに, プロパティの目標値を指定すると,
6986      * アクションが実行された時に, 目標値までなめらかに値を変更するようなアクションを生成する.
6987      *
6988      * トゥイーンのイージングも, easing プロパティで指定できる.
6989      *
6990      * @param {Object} params
6991      * @param {Number} params.time アニメーションにかける時間.
6992      * @param {Function} [params.easing=enchant.Easing.LINEAR] イージング関数.
6993      * @constructs
6994      * @extends enchant.Action
6995      */
6996     initialize: function(params) {
6997         var origin = {};
6998         var target = {};
6999         enchant.Action.call(this, params);
7000 
7001         if (this.easing == null) {
7002             this.easing = enchant.Easing.LINEAR;
7003         }
7004 
7005         var tween = this;
7006         this.addEventListener(enchant.Event.ACTION_START, function() {
7007             // excepted property
7008             var excepted = ["frame", "time", "callback", "onactiontick", "onactionstart", "onactionend"];
7009             for (var prop in params) {
7010                 if (params.hasOwnProperty(prop)) {
7011                     // if function is used instead of numerical value, evaluate it
7012                     var target_val;
7013                     if (typeof params[prop] === "function") {
7014                         target_val = params[prop].call(tween.node);
7015                     } else {
7016                         target_val = params[prop];
7017                     }
7018 
7019                     if (excepted.indexOf(prop) === -1) {
7020                         origin[prop] = tween.node[prop];
7021                         target[prop] = target_val;
7022                     }
7023                 }
7024             }
7025         });
7026 
7027         this.addEventListener(enchant.Event.ACTION_TICK, function(evt) {
7028             // if time is 0, set property to target value immediately
7029             var ratio = tween.time === 0 ? 1 : tween.easing(Math.min(tween.time,tween.frame + evt.elapsed), 0, 1, tween.time) - tween.easing(tween.frame, 0, 1, tween.time);
7030 
7031             for (var prop in target){
7032                 if (target.hasOwnProperty(prop)) {
7033                     if (typeof this[prop] === "undefined"){
7034                         continue;
7035                     }
7036                     tween.node[prop] += (target[prop] - origin[prop]) * ratio;
7037                     if (Math.abs(tween.node[prop]) < 10e-8){
7038                         tween.node[prop] = 0;
7039                     }
7040                 }
7041             }
7042         });
7043     }
7044 });
7045 
7046 }(window));
7047