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