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