1 /** 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2016 DeNA Co., Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 /// <reference path="base.js"/> 26 /// <reference path="container.js"/> 27 /// <reference path="tick_listener.js"/> 28 /// <reference path="renderer.js"/> 29 /// <reference path="canvas_renderer.js"/> 30 /// <reference path="webgl_renderer.js"/> 31 /// <reference path="user_agent.js"/> 32 /// <reference path="counter.js"/> 33 /// <reference path="mouse_event.js"/> 34 /// <reference path="ticker.js"/> 35 /// <reference path="graphics.js"/> 36 /// <reference path="sound.js"/> 37 /// <reference path="image_factory.js"/> 38 /// <reference path="config.js"/> 39 40 /** 41 * A class that represents the root node of an object tree. 42 * @param {HTMLCanvasElement|string} canvas 43 * @extends {createjs.Container} 44 * @implements {EventListener} 45 * @implements {createjs.TickListener} 46 * @constructor 47 */ 48 createjs.Stage = function(canvas) { 49 createjs.Container.call(this); 50 51 /** 52 * The renderer object that renders CreateJS objects in the output <canvas> 53 * element. 54 * @type {createjs.Renderer} 55 * @private 56 */ 57 this.renderer_ = this.createRenderer_(canvas); 58 59 /** 60 * Mapping from a pointer ID to its data. 61 * @type {Array.<createjs.Stage.Pointer>} 62 * @private 63 */ 64 this.pointers_ = []; 65 66 this.enableDOMEvents(true); 67 }; 68 createjs.inherits('Stage', createjs.Stage, createjs.Container); 69 70 /** 71 * The pointer ID representing there are not any active pointers. 72 * @const {number} 73 */ 74 createjs.Stage.POINTER_NULL = -2; 75 76 /** 77 * The pointer ID representing a mouse event. 78 * @const {number} 79 */ 80 createjs.Stage.POINTER_MOUSE = -1; 81 82 /** 83 * A bit-mask representing pointer events. 84 * @enum {number} 85 */ 86 createjs.Stage.Event = { 87 MOUSE: 0, 88 TOUCH: 1, 89 POINTER: 2 90 }; 91 92 /** 93 * The list of events used by this stage. 94 * @const {Array.<Array.<string>>} 95 * @private 96 */ 97 createjs.Stage.EVENTS_ = [ 98 ['mousedown', 'mousemove', 'mouseup'], 99 ['touchstart', 'touchmove', 'touchend'], 100 ['pointerstart', 'pointermove', 'pointerend'] 101 ]; 102 103 /** 104 * An inner class encapsulating a point where a mouse event fires. 105 * @param {number} id 106 * @constructor 107 */ 108 createjs.Stage.Pointer = function(id) { 109 /** 110 * The pointer ID provided by the host browser. 111 * @const {number} 112 * @private 113 */ 114 this.id = id; 115 116 /** 117 * The page position of this event. 118 * @const {createjs.Point} 119 * @private 120 */ 121 this.page_ = new createjs.Point(-1, -1); 122 123 /** 124 * The source position of this event when it occurs in the hosting stage. 125 * @const {createjs.Point} 126 * @private 127 */ 128 this.point_ = new createjs.Point(0, 0); 129 130 /** 131 * Whether this event is occurs in the hosting stage. 132 * @type {boolean} 133 * @private 134 */ 135 this.inBounds_ = false; 136 137 /** 138 * The display object under the source position. 139 * @type {createjs.DisplayObject} 140 * @private 141 */ 142 this.target_ = null; 143 144 /** 145 * The source position of this event whether it occurs in the hosting stage 146 * or not. This position may be the outside of the stage if a user moves a 147 * mouse from its inside to its outside. 148 * @const {createjs.Point} 149 * @private 150 */ 151 this.raw_ = new createjs.Point(0, 0); 152 }; 153 154 /** 155 * The current X position of the primary pointer. 156 * @type {number} 157 */ 158 createjs.Stage.prototype['mouseX'] = 0; 159 160 /** 161 * The current mouse Y position of the primary pointer. 162 * @type {number} 163 */ 164 createjs.Stage.prototype['mouseY'] = 0; 165 166 /** 167 * Whether the primary pointer is currently within the bounds of the canvas. 168 * @type {boolean} 169 */ 170 createjs.Stage.prototype['mouseInBounds'] = false; 171 172 /** 173 * The draw event dispatched to listeners. 174 * @type {createjs.Event} 175 * @private 176 */ 177 createjs.Stage.prototype.drawEvent_ = null; 178 179 /** 180 * The mouse event dispatched to listeners. 181 * @type {createjs.MouseEvent} 182 * @private 183 */ 184 createjs.Stage.prototype.mouseEvent_ = null; 185 186 /** 187 * The ID of the primary pointer. 188 * @type {number} 189 * @private 190 */ 191 createjs.Stage.prototype.primaryPointer_ = createjs.Stage.POINTER_NULL; 192 193 /** 194 * A bit-mask of DOM events listened by this object. 195 * @type {number} 196 * @private 197 */ 198 createjs.Stage.prototype.domEvents_ = 0; 199 200 /** 201 * Whether to prevent touch events. 202 * @type {boolean} 203 * @private 204 */ 205 createjs.Stage.prototype.preventDefault_ = false; 206 207 /** 208 * Creates a renderer used for rendering objects to the specified canvas. 209 * @param {HTMLElement|string} value 210 * @return {createjs.Renderer} renderer 211 * @private 212 */ 213 createjs.Stage.prototype.createRenderer_ = function(value) { 214 /// <param name="value"/> 215 /// <returns type="createjs.Renderer"/> 216 if (!value) { 217 return null; 218 } 219 if (createjs.isString(value)) { 220 value = document.getElementById(createjs.getString(value)); 221 } 222 var canvas = createjs.castCanvas(value); 223 // Write the version of this library. 224 canvas.setAttribute( 225 'dena-version', 226 createjs.DENA_MAJOR_VERSION + '.' + 227 createjs.DENA_MINOR_VERSION + '.' + 228 createjs.DENA_BUILD_NUMBER + '.' + 229 createjs.DENA_PATCH_LEVEL); 230 if (!createjs.WebGLRenderer.id) { 231 createjs.WebGLRenderer.id = 1; 232 var CONTEXTS = ['webgl', 'experimental-webgl']; 233 for (var i = 0; i < CONTEXTS.length; ++i) { 234 var context = canvas.getContext(CONTEXTS[i]); 235 if (context) { 236 createjs.WebGLRenderer.context = CONTEXTS[i]; 237 return new createjs.WebGLRenderer(canvas, context, null); 238 } 239 } 240 } 241 if (createjs.WebGLRenderer.context) { 242 var context = canvas.getContext(createjs.WebGLRenderer.context); 243 return new createjs.WebGLRenderer(canvas, context, null); 244 } 245 return new createjs.CanvasRenderer(canvas, null); 246 }; 247 248 /** 249 * Sends a draw event to the listeners attached to this stage. This stage does 250 * not dispatch draw events, which often cause rendering glitches. 251 * @param {string} type 252 * @private 253 */ 254 createjs.Stage.prototype.sendDrawEvent_ = function(type) { 255 /// <param type="string" name="type"/> 256 } 257 258 /** 259 * Returns a mouse event to be dispatched to CreateJS objects. (This stage uses 260 * a static createjs.MouseEvent object to avoid creating a mouse event every 261 * time when the stage dispatches it.) 262 * @param {Event} event 263 * @param {number} id 264 * @return {createjs.MouseEvent} 265 * @private 266 */ 267 createjs.Stage.prototype.getMouseEvent_ = function(event, id) { 268 /// <param type="Event" name="event"/> 269 /// <param type="number" name="id"/> 270 /// <returns type="createjs.MouseEvent"/> 271 if (!this.mouseEvent_) { 272 this.mouseEvent_ = new createjs.MouseEvent( 273 '', false, false, 0, 0, event, id, false, 0, 0); 274 } else { 275 this.mouseEvent_.reset(event, id); 276 } 277 return this.mouseEvent_; 278 } 279 280 /** 281 * Applies changes to the createjs.Event object to the specified Event object. 282 * (The createjs.Event object, which is dispatched to CreateJS objects, is a 283 * JavaScript object and its values need to be copied to the Event object to 284 * prevent default actions and to stop propagations. 285 * @param {Event} event 286 * @param {boolean} defaultPrevented 287 * @private 288 */ 289 createjs.Stage.prototype.applyMouseEvent_ = function(event, defaultPrevented) { 290 /// <param type="Event" name="event"/> 291 /// <param type="boolean" name="defaultPrevented"/> 292 var mouse = this.mouseEvent_; 293 if (mouse) { 294 defaultPrevented = defaultPrevented || mouse.defaultPrevented 295 mouse.defaultPrevented = false; 296 if (mouse.propagationStopped) { 297 event.stopPropagation(); 298 } 299 mouse.propagationStopped = false; 300 } 301 if (defaultPrevented) { 302 event.preventDefault(); 303 } 304 } 305 306 /** 307 * Returns the createjs.Renderer object associated with this stage. 308 * @return {createjs.Renderer} 309 * @private 310 */ 311 createjs.Stage.prototype.getRenderer_ = function() { 312 /// <returns type="createjs.Renderer"/> 313 return this.renderer_; 314 }; 315 316 /** 317 * Returns the HTMLCanvasElement object associated with this object. 318 * @return {HTMLCanvasElement} 319 * @private 320 */ 321 createjs.Stage.prototype.getCanvas_ = function() { 322 /// <returns type="HTMLCanvasElement"/> 323 return this.getRenderer_().getCanvas(); 324 }; 325 326 /** 327 * Attaches an HTMLCanvasElement object to this object. This method discards all 328 * resources attached to the currently-attached createjs.Renderer object and 329 * re-create a createjs.Renderer object with the specified <canvas> element. 330 * @param {HTMLCanvasElement} canvas 331 * @private 332 */ 333 createjs.Stage.prototype.setCanvas_ = function(canvas) { 334 /// <param type="HTMLCanvasElement" name="canvas"/> 335 var renderer = this.getRenderer_(); 336 if (renderer) { 337 var current = renderer.getCanvas(); 338 if (current) { 339 this.removeListeners_(current, createjs.Stage.Event.MOUSE); 340 this.removeListeners_(current, createjs.Stage.Event.TOUCH); 341 this.removeListeners_(current, createjs.Stage.Event.POINTER); 342 } 343 renderer.destroy(); 344 this.renderer_ = null; 345 } 346 this.renderer_ = this.createRenderer_(canvas); 347 }; 348 349 /** 350 * Retrieves the pointer object, which stores the last position of the specified 351 * pointer. 352 * @param {number} id 353 * @param {boolean=} opt_remove 354 * @return {createjs.Stage.Pointer} 355 * @private 356 */ 357 createjs.Stage.prototype.getPointer_ = function(id, opt_remove) { 358 /// <param type="number" name="id"/> 359 /// <param type="boolean" optional="true" name="opt_remove"/> 360 /// <returns type="createjs.Stage.Pointer"/> 361 for (var i = 0; i < this.pointers_.length; ++i) { 362 if (this.pointers_[i].id == id) { 363 var pointer = this.pointers_[i]; 364 if (opt_remove && id >= 0) { 365 this.pointers_.splice(i, 1); 366 } 367 return pointer; 368 } 369 } 370 createjs.assert(!opt_remove); 371 var pointer = new createjs.Stage.Pointer(id); 372 this.pointers_.push(pointer); 373 return pointer; 374 }; 375 376 /** 377 * Updates the pointer position. This method normalizes the event position to 378 * fit it in the HTMLCanvasElement object associated with this object. This 379 * method may be called with a MouseEvent object, a Touch object, or a 380 * PointerEvent object. 381 * @param {createjs.Stage.Pointer} pointer 382 * @param {number} x 383 * @param {number} y 384 * @param {createjs.MouseEvent} mouse 385 * @private 386 */ 387 createjs.Stage.prototype.updatePointer_ = function(pointer, x, y, mouse) { 388 /// <param type="createjs.Stage.Pointer" name="pointer"/> 389 /// <param type="number" name="x"/> 390 /// <param type="number" name="y"/> 391 /// <param type="createjs.MouseEvent" name="mouse"/> 392 // Update the position of this pointer only when it has been moved by at least 393 // four pixels to avoid updating it too often. (It is slow to update a pointer 394 // position on mobile devices.) 395 if (createjs.abs(pointer.page_.x - x) <= 4 && 396 createjs.abs(pointer.page_.y - y) <= 4) { 397 return; 398 } 399 pointer.page_.x = x; 400 pointer.page_.y = y; 401 var renderer = this.getRenderer_(); 402 var canvas = renderer.getCanvas(); 403 var width = renderer.getWidth(); 404 var height = renderer.getHeight(); 405 // Emulate the above adjusted offsetX and offsetY values for browsers that 406 // do not provide them, e.g. MouseEvents of Firefox or Touch objects. 407 var bounds = canvas.getBoundingClientRect(); 408 var offsetX = window.pageXOffset - document.body.clientLeft; 409 var offsetY = window.pageYOffset - document.body.clientTop; 410 var styles = window.getComputedStyle(canvas); 411 var left = bounds.left + offsetX + 412 createjs.parseInt(styles.paddingLeft) + 413 createjs.parseInt(styles.borderLeftWidth); 414 var top = bounds.top + offsetY + 415 createjs.parseInt(styles.paddingTop) + 416 createjs.parseInt(styles.borderTopWidth); 417 var right = bounds.right + offsetX - 418 createjs.parseInt(styles.paddingRight) - 419 createjs.parseInt(styles.borderRightWidth); 420 var bottom = bounds.bottom + offsetY - 421 createjs.parseInt(styles.paddingBottom) - 422 createjs.parseInt(styles.borderBottomWidth); 423 x = (x - left) / (right - left) * width; 424 y = (y - top) / (bottom - top) * height; 425 var inBounds = (0 <= x && x < width) && (0 <= y && y < height); 426 if (inBounds) { 427 pointer.point_.x = x; 428 pointer.point_.y = y; 429 } 430 pointer.inBounds_ = inBounds; 431 pointer.raw_.x = x; 432 pointer.raw_.y = y; 433 var primary = pointer.id == this.primaryPointer_; 434 if (primary) { 435 this['mouseX'] = x; 436 this['mouseY'] = y; 437 this['mouseInBounds'] = inBounds; 438 } 439 mouse.setStage(pointer.point_.x, pointer.point_.y); 440 mouse.setPrimary(primary); 441 mouse.setRaw(pointer.raw_.x, pointer.raw_.y); 442 }; 443 444 /** 445 * Called when this stage receives a pointer-down event, i.e. a mousedown event, 446 * a pointerdown event, or a touchdown event. 447 * @param {number} id 448 * @param {number} x 449 * @param {number} y 450 * @param {Event} event 451 * @private 452 */ 453 createjs.Stage.prototype.handlePointerDown_ = function(id, x, y, event) { 454 /// <param type="number" name="id"/> 455 /// <param type="number" name="x"/> 456 /// <param type="number" name="y"/> 457 /// <param type="Event" name="event"/> 458 var mouse = this.getMouseEvent_(event, id); 459 // Dispatch a 'stagemousedown' event to this stage and a 'mousedown' event 460 // to a display object under the given point. 461 var pointer = this.getPointer_(id); 462 this.updatePointer_(pointer, x, y, mouse); 463 mouse.type = 'stagemousedown'; 464 mouse.bubbles = false; 465 if (pointer.inBounds_ && this.hasListener(mouse.type)) { 466 this.dispatchRawEvent(mouse); 467 } 468 var TYPES = 469 createjs.EventDispatcher.CLICK | createjs.EventDispatcher.MOUSE_DOWN; 470 var target = this.hitTestObject(pointer.point_, TYPES, 0); 471 if (target) { 472 mouse.type = 'mousedown'; 473 mouse.bubbles = true; 474 target.dispatchRawEvent(mouse); 475 } 476 if (!mouse.defaultPrevented) { 477 pointer.target_ = target; 478 } 479 }; 480 481 /** 482 * Called when this stage receives a pointer-move event, i.e. a mousemove event, 483 * a pointermove event, or a touchmove event. 484 * @param {number} id 485 * @param {number} x 486 * @param {number} y 487 * @param {Event} event 488 * @private 489 */ 490 createjs.Stage.prototype.handlePointerMove_ = function(id, x, y, event) { 491 /// <param type="number" name="id"/> 492 /// <param type="number" name="x"/> 493 /// <param type="number" name="y"/> 494 /// <param type="Event" name="event"/> 495 var mouse = this.getMouseEvent_(event, id); 496 // Compare the 'inBound' state before updating pointer events with the one 497 // after updating them to send mouse events as listed below. 498 // +--------+-------+-------------------------------------------+ 499 // | before | after | events | 500 // +--------+-------+-------------------------------------------+ 501 // | false | false | none | 502 // | false | true | mouseenter, stagemousemove, and pressmove | 503 // | true | false | mouseleave, stagemousemove, and pressmove | 504 // | true | true | stagemousemove and pressmove | 505 // +--------+-------+-------------------------------------------+ 506 var pointer = this.getPointer_(id); 507 var inBounds = pointer.inBounds_; 508 this.updatePointer_(pointer, x, y, mouse); 509 if (inBounds || pointer.inBounds_) { 510 if (id == createjs.Stage.POINTER_MOUSE && pointer.inBounds_ == !inBounds) { 511 mouse.type = inBounds ? 'mouseleave' : 'mouseenter'; 512 mouse.bubbles = false; 513 if (this.hasListener(mouse.type)) { 514 this.dispatchRawEvent(mouse); 515 } 516 } 517 mouse.type = 'stagemousemove'; 518 mouse.bubbles = false; 519 if (this.hasListener(mouse.type)) { 520 this.dispatchRawEvent(mouse); 521 } 522 if (pointer.target_) { 523 mouse.type = 'pressmove'; 524 mouse.bubbles = true; 525 pointer.target_.dispatchRawEvent(mouse); 526 } 527 } 528 }; 529 530 /** 531 * Called when this stage receives a pointer-up event, i.e. a mouseup event, a 532 * pointerend event, or a touchend event. 533 * @param {number} id 534 * @param {number} x 535 * @param {number} y 536 * @param {Event} event 537 * @private 538 */ 539 createjs.Stage.prototype.handlePointerUp_ = function(id, x, y, event) { 540 /// <param type="number" name="id"/> 541 /// <param type="number" name="x"/> 542 /// <param type="number" name="y"/> 543 /// <param type="Event" name="event"/> 544 var mouse = this.getMouseEvent_(event, id); 545 var pointer = this.getPointer_(id, true); 546 mouse.type = 'stagemouseup'; 547 mouse.bubbles = false; 548 if (this.hasListener(mouse.type)) { 549 this.dispatchRawEvent(mouse); 550 } 551 if (pointer.target_) { 552 // Compare the saved target (i.e. the target for a 'mousedown' event with 553 // the one under the given position. Dispatch a 'click' event if they are 554 // the same object. 555 var TYPES = 556 createjs.EventDispatcher.CLICK | createjs.EventDispatcher.PRESS_UP; 557 var target = this.hitTestObject(pointer.point_, TYPES, 0); 558 if (target == pointer.target_) { 559 mouse.type = 'click'; 560 mouse.bubbles = true; 561 target.dispatchRawEvent(mouse); 562 } 563 mouse.type = 'pressup'; 564 mouse.bubbles = true; 565 pointer.target_.dispatchRawEvent(mouse); 566 mouse.type = 'mouseup'; 567 pointer.target_.dispatchRawEvent(mouse); 568 pointer.target_ = null; 569 } 570 }; 571 572 /** 573 * Called when this stage receives a pointer-move event, i.e. a mouseenter event 574 * or a pointerenter event. 575 * @param {string} type 576 * @param {number} id 577 * @param {number} x 578 * @param {number} y 579 * @param {Event} event 580 * @private 581 */ 582 createjs.Stage.prototype.handlePointerEnter_ = function(type, id, x, y, event) { 583 /// <param type="string" name="type"/> 584 /// <param type="number" name="id"/> 585 /// <param type="number" name="x"/> 586 /// <param type="number" name="y"/> 587 /// <param type="Event" name="event"/> 588 var mouse = this.getMouseEvent_(event, id); 589 var pointer = this.getPointer_(id); 590 this.updatePointer_(pointer, x, y, mouse); 591 if (this.hasListener(type)) { 592 mouse.type = type; 593 mouse.bubbles = true; 594 this.dispatchRawEvent(mouse); 595 } 596 pointer.target_ = null; 597 }; 598 599 /** 600 * Called when this stage receives a pointer-move event, i.e. a mouseleave event 601 * or a pointerleave event. 602 * @param {string} type 603 * @param {number} id 604 * @param {number} x 605 * @param {number} y 606 * @param {Event} event 607 * @private 608 */ 609 createjs.Stage.prototype.handlePointerLeave_ = function(type, id, x, y, event) { 610 /// <param type="string" name="type"/> 611 /// <param type="number" name="id"/> 612 /// <param type="number" name="x"/> 613 /// <param type="number" name="y"/> 614 /// <param type="Event" name="event"/> 615 var mouse = this.getMouseEvent_(event, id); 616 var pointer = this.getPointer_(id); 617 this.updatePointer_(pointer, x, y, mouse); 618 if (this.hasListener(type)) { 619 mouse.type = type; 620 mouse.bubbles = true; 621 this.dispatchRawEvent(mouse); 622 } 623 pointer.target_ = null; 624 }; 625 626 /** 627 * Called when this stage needs to send a mouseover event. 628 * @param {boolean} clear 629 * @param {createjs.Stage} owner 630 * @param {createjs.Stage} eventTarget 631 * @private 632 */ 633 createjs.Stage.prototype.testMouseOver_ = function(clear, owner, eventTarget) { 634 /// <param type="boolean" name="clear"/> 635 /// <param type="createjs.Stage" name="owner"/> 636 /// <param type="createjs.Stage" name="eventTarget"/> 637 createjs.notImplemented(); 638 }; 639 640 /** 641 * Attaches this object to the specified set of events. 642 * @param {EventTarget} canvas 643 * @param {number} id 644 * @private 645 */ 646 createjs.Stage.prototype.addListeners_ = function(canvas, id) { 647 /// <param type="EventTarget" name="canvas"/> 648 /// <param type="number" name="id"/> 649 var mask = 1 << id; 650 if (!(this.domEvents_ & mask)) { 651 this.domEvents_ |= mask; 652 var events = createjs.Stage.EVENTS_[id]; 653 for (var j = 0; j < events.length; ++j) { 654 canvas.addEventListener(events[j], this, false); 655 } 656 } 657 }; 658 659 /** 660 * Removes this object from the specified set of events. 661 * @param {EventTarget} canvas 662 * @param {number} id 663 * @private 664 */ 665 createjs.Stage.prototype.removeListeners_ = function(canvas, id) { 666 /// <param type="EventTarget" name="canvas"/> 667 /// <param type="number" name="id"/> 668 var mask = 1 << id; 669 this.domEvents_ &= ~mask; 670 var events = createjs.Stage.EVENTS_[id]; 671 for (var j = 0; j < events.length; ++j) { 672 canvas.removeEventListener(events[j], this, false); 673 } 674 }; 675 676 /** 677 * Updates this stage. This method is usually used by applications to draw all 678 * display objects without running tweens. 679 * @const 680 */ 681 createjs.Stage.prototype.update = function() { 682 if (this.renderer_ && this.renderer_.getCanvas()) { 683 this.handleTick(createjs.Ticker.getRunTime()); 684 } 685 }; 686 687 /** 688 * Clears the target canvas. 689 * @const 690 */ 691 createjs.Stage.prototype.clear = function() { 692 }; 693 694 /** 695 * Returns a data URL that contains a Base64-encoded image of the contents of 696 * the stage. The returned data URL can be specified as the src value of an 697 * image element. 698 * @param {string} backgroundColor 699 * @param {string} mimeType 700 * @return {string} 701 * @const 702 */ 703 createjs.Stage.prototype.toDataURL = function(backgroundColor, mimeType) { 704 /// <param type="string" name="backgroundColor"/> 705 /// <param type="string" name="mimeType"/> 706 /// <returns type="string"/> 707 createjs.notImplemented(); 708 return ''; 709 }; 710 711 /** 712 * Enables or disables (by passing a frequency of 0) dispatching mouse-over 713 * events. 714 * @param {number=} opt_frequency 715 * @const 716 */ 717 createjs.Stage.prototype.enableMouseOver = function(opt_frequency) { 718 /// <param type="number" optional="true" name="opt_frequency"/> 719 createjs.notImplemented(); 720 }; 721 722 /** 723 * Starts or stops listening mouse events. 724 * @param {boolean} enable 725 * @const 726 */ 727 createjs.Stage.prototype.enableDOMEvents = function(enable) { 728 /// <param type="boolean" name="enable"/> 729 if (!this.renderer_) { 730 return; 731 } 732 var canvas = this.getCanvas_(); 733 if (canvas) { 734 if (!enable) { 735 this.removeListeners_(canvas, createjs.Stage.Event.MOUSE); 736 } else { 737 this.addListeners_(canvas, createjs.Stage.Event.MOUSE); 738 window.addEventListener('hashchange', this, false); 739 window.addEventListener('unload', this, false); 740 } 741 } 742 }; 743 744 /** 745 * Starts or stops listening touch events. 746 * @param {boolean} enable 747 * @param {boolean} preventDefault 748 * @const 749 */ 750 createjs.Stage.prototype.enableTouchEvents = function(enable, preventDefault) { 751 if (!this.renderer_) { 752 return; 753 } 754 var canvas = this.getCanvas_(); 755 if (!enable) { 756 this.removeListeners_(canvas, createjs.Stage.Event.TOUCH); 757 this.removeListeners_(canvas, createjs.Stage.Event.POINTER); 758 } else { 759 this.preventDefault_ = preventDefault; 760 if (createjs.UserAgent.isMSIE()) { 761 this.addListeners_(canvas, createjs.Stage.Event.POINTER); 762 } else { 763 this.addListeners_(canvas, createjs.Stage.Event.TOUCH); 764 } 765 } 766 }; 767 768 /** @override */ 769 createjs.Stage.prototype.isStage = true; 770 771 /** @override */ 772 createjs.Stage.prototype.removeAllChildren = function(opt_destroy) { 773 /// <param type="boolean" optional="true" name="opt_destroy"/> 774 // Remove not only all children of this stage but also the global resources 775 // used by them to avoid resource leaks. 776 createjs.Stage.superClass_.removeAllChildren.call(this); 777 createjs.Graphics.reset(); 778 createjs.Ticker.reset(opt_destroy); 779 if (createjs.DEBUG) { 780 createjs.Counter.reset(); 781 } 782 var images = createjs.ImageFactory.reset(opt_destroy); 783 var renderer = this.getRenderer_(); 784 if (renderer) { 785 if (images) { 786 var destroy = opt_destroy && createjs.UserAgent.isIPhone(); 787 for (var key in images) { 788 var image = images[key]; 789 if (image) { 790 renderer.uncache(image); 791 if (destroy) { 792 // Attach a 1x1 GIF image to this <img> element to discard the 793 // previous one. 794 image.src = 795 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' 796 } 797 } 798 } 799 } 800 renderer.destroy(); 801 } 802 }; 803 804 /** @override */ 805 createjs.Stage.prototype.handleTick = function(time) { 806 /// <param type="number" name="time"/> 807 if (!this.renderer_) { 808 return; 809 } 810 createjs.assert(!!this.getCanvas_()); 811 812 // Renders display objects to an HTMLCanvasElement with the createjs.Renderer 813 // interface. 814 if (createjs.DEBUG) { 815 createjs.Counter.paintedObjects = 0; 816 createjs.Counter.visibleObjects = 0; 817 createjs.Counter.totalObjects = 0; 818 createjs.Counter.updatedTweens = 0; 819 createjs.Counter.runningTweens = 0; 820 } 821 this.sendDrawEvent_('drawstart'); 822 this.updateTweens(time); 823 this.renderer_.begin(); 824 this.layout(this.renderer_, this, this.getDirty(), time, 1); 825 this.renderer_.paint(time); 826 this.sendDrawEvent_('drawend'); 827 }; 828 829 /** @override */ 830 createjs.Stage.prototype.handleEvent = function(event) { 831 /// <param type="Event" name="event"/> 832 var TOUCH_MASK = 1 << createjs.Stage.Event.TOUCH; 833 var POINTER_MASK = 1 << createjs.Stage.Event.POINTER; 834 var MOUSE_MASK = 1 << createjs.Stage.Event.MOUSE; 835 // Process touch events first for phones, which do not have so much processing 836 // power as PCs. 837 var type = event.type; 838 var processed = false; 839 if (this.domEvents_ & TOUCH_MASK) { 840 if (type == 'touchstart') { 841 processed = true; 842 var touches = event.changedTouches; // event.touches; 843 if (this.primaryPointer_ == createjs.Stage.POINTER_NULL) { 844 this.primaryPointer_ = touches[0].identifier || 0; 845 } 846 for (var i = 0; i < touches.length; ++i) { 847 var touch = touches[i]; 848 var id = touch.identifier || i; 849 this.handlePointerDown_(id, touch.pageX, touch.pageY, event); 850 } 851 } else if (type == 'touchmove') { 852 processed = true; 853 var touches = event.changedTouches; // event.touches; 854 for (var i = 0; i < touches.length; ++i) { 855 var touch = touches[i]; 856 var id = touches[i].identifier || i; 857 this.handlePointerMove_(id, touch.pageX, touch.pageY, event); 858 } 859 } else if (type == 'touchend') { 860 processed = true; 861 var touches = event.changedTouches; // event.touches; 862 for (var i = 0; i < touches.length; ++i) { 863 var touch = touches[i]; 864 var id = touch.identifier || i; 865 this.handlePointerUp_(id, touch.pageX, touch.pageY, event); 866 if (this.primaryPointer_ == id) { 867 this.primaryPointer_ = createjs.Stage.POINTER_NULL; 868 } 869 } 870 } 871 } else if (this.domEvents_ & POINTER_MASK) { 872 var pageX = event.pageX; 873 var pageY = event.pageY; 874 if (type == 'pointerdown') { 875 processed = true; 876 if (event.isPrimary) { 877 this.primaryPointer_ = event.pointerId; 878 } 879 this.handlePointerDown_(event.pointerId, pageX, pageY, event); 880 } else if (type == 'pointermove') { 881 processed = true; 882 this.handlePointerMove_(event.pointerId, pageX, pageY, event); 883 } else if (type == 'pointerup') { 884 this.handlePointerUp_(event.pointerId, pageX, pageY, event); 885 processed = true; 886 } 887 } 888 // Prevent a browser from sending mouse events when this object has processed 889 // touch events or pointer events. 890 if (processed) { 891 this.applyMouseEvent_(event, this.preventDefault_); 892 // Kick the global ticker and try updating this stage for Android Chrome, 893 // which dispatches touch events too often and it does not call the 894 // setInterval() callback of the ticker while it sends touch events. 895 createjs.Ticker.kick(); 896 return; 897 } 898 // Remove all children of this stage and the global resources used by the 899 // children when this stage is being unloaded. (A hashchange event is not 900 // actually an unload event and some browsers do not automatically delete 901 // resources used by the current page, i.e. they may leak objects used by this 902 // stage and its children.) 903 if (type == 'hashchange' || type == 'unload') { 904 window.removeEventListener(type, this, false); 905 this.removeAllChildren(true); 906 var unload = type == 'unload' ? 1 : 0; 907 createjs.Sound.reset(unload); 908 return; 909 } 910 // Finally, process mouse events. 911 var pageX = event.pageX; 912 var pageY = event.pageY; 913 if (this.domEvents_ & MOUSE_MASK) { 914 if (type == 'mousedown') { 915 this.primaryPointer_ = createjs.Stage.POINTER_MOUSE; 916 this.handlePointerDown_( 917 createjs.Stage.POINTER_MOUSE, pageX, pageY, event); 918 } else if (type == 'mousemove') { 919 this.handlePointerMove_( 920 createjs.Stage.POINTER_MOUSE, pageX, pageY, event); 921 } else if (type == 'mouseup') { 922 this.handlePointerUp_(createjs.Stage.POINTER_MOUSE, pageX, pageY, event); 923 } 924 this.applyMouseEvent_(event, false); 925 } 926 }; 927 928 // Adds getters and setters for applications to access internal variables. 929 Object.defineProperties(createjs.Stage.prototype, { 930 'canvas': { 931 get: createjs.Stage.prototype.getCanvas_, 932 set: createjs.Stage.prototype.setCanvas_ 933 } 934 }); 935 936 // Export the createjs.Stage object to the global namespace. 937 createjs.exportObject('createjs.Stage', createjs.Stage, { 938 // createjs.Stage methods 939 'update': createjs.Stage.prototype.update, 940 'clear': createjs.notReached, 941 'toDataURL': createjs.notImplemented, 942 'enableMouseOver': createjs.notImplemented, 943 'enableDOMEvents': createjs.Stage.prototype.enableDOMEvents, 944 'handleEvent': createjs.Stage.prototype.handleEvent 945 }); 946