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