From bb3e231feca3311508d23d64a8c20e1605291ba0 Mon Sep 17 00:00:00 2001
From: Gousalya-hmh <54981888+Gousalya-hmh@users.noreply.github.com>
Date: Fri, 27 May 2022 12:32:18 +0530
Subject: [PATCH 1/2] Altered touch fix
---
lib/easeljs.js | 31767 ++++++++++++++++++++++++-----------------------
1 file changed, 15891 insertions(+), 15876 deletions(-)
diff --git a/lib/easeljs.js b/lib/easeljs.js
index d28e13489..917b11b41 100644
--- a/lib/easeljs.js
+++ b/lib/easeljs.js
@@ -1,466 +1,466 @@
-/*!
-* EaselJS
-* Visit http://createjs.com/ for documentation, updates and examples.
-*
-* Copyright (c) 2010 gskinner.com, inc.
-*
-* Permission is hereby granted, free of charge, to any person
-* obtaining a copy of this software and associated documentation
-* files (the "Software"), to deal in the Software without
-* restriction, including without limitation the rights to use,
-* copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to whom the
-* Software is furnished to do so, subject to the following
-* conditions:
-*
-* The above copyright notice and this permission notice shall be
-* included in all copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-* OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-
-//##############################################################################
-// extend.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Sets up the prototype chain and constructor property for a new class.
- *
- * This should be called right after creating the class constructor.
- *
- * function MySubClass() {}
- * createjs.extend(MySubClass, MySuperClass);
- * MySubClass.prototype.doSomething = function() { }
- *
- * var foo = new MySubClass();
- * console.log(foo instanceof MySuperClass); // true
- * console.log(foo.prototype.constructor === MySubClass); // true
- *
- * @method extend
- * @param {Function} subclass The subclass.
- * @param {Function} superclass The superclass to extend.
- * @return {Function} Returns the subclass's new prototype.
- */
-createjs.extend = function(subclass, superclass) {
- "use strict";
-
- function o() { this.constructor = subclass; }
- o.prototype = superclass.prototype;
- return (subclass.prototype = new o());
-};
-
-//##############################################################################
-// promote.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
- * It is recommended to use the super class's name as the prefix.
- * An alias to the super class's constructor is always added in the format `prefix_constructor`.
- * This allows the subclass to call super class methods without using `function.call`, providing better performance.
- *
- * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
- * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
- * prototype of `MySubClass` as `MySuperClass_draw`.
- *
- * This should be called after the class's prototype is fully defined.
- *
- * function ClassA(name) {
- * this.name = name;
- * }
- * ClassA.prototype.greet = function() {
- * return "Hello "+this.name;
- * }
- *
- * function ClassB(name, punctuation) {
- * this.ClassA_constructor(name);
- * this.punctuation = punctuation;
- * }
- * createjs.extend(ClassB, ClassA);
- * ClassB.prototype.greet = function() {
- * return this.ClassA_greet()+this.punctuation;
- * }
- * createjs.promote(ClassB, "ClassA");
- *
- * var foo = new ClassB("World", "!?!");
- * console.log(foo.greet()); // Hello World!?!
- *
- * @method promote
- * @param {Function} subclass The class to promote super class methods on.
- * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
- * @return {Function} Returns the subclass.
- */
-createjs.promote = function(subclass, prefix) {
- "use strict";
-
- var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
- if (supP) {
- subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
- for (var n in supP) {
- if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
- }
- }
- return subclass;
-};
-
-//##############################################################################
-// indexOf.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
- * that value. Returns -1 if value is not found.
- *
- * var i = createjs.indexOf(myArray, myElementToFind);
- *
- * @method indexOf
- * @param {Array} array Array to search for searchElement
- * @param searchElement Element to find in array.
- * @return {Number} The first index of searchElement in array.
- */
-createjs.indexOf = function (array, searchElement){
- "use strict";
-
- for (var i = 0,l=array.length; i < l; i++) {
- if (searchElement === array[i]) {
- return i;
- }
- }
- return -1;
-};
-
-//##############################################################################
-// UID.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. UID.get()
)
- * and should not be instantiated.
- * @class UID
- * @static
- **/
- function UID() {
- throw "UID cannot be instantiated";
- }
-
-
-// private static properties:
- /**
- * @property _nextID
- * @type Number
- * @protected
- **/
- UID._nextID = 0;
-
-
-// public static methods:
- /**
- * Returns the next unique id.
- * @method get
- * @return {Number} The next unique id
- * @static
- **/
- UID.get = function() {
- return UID._nextID++;
- };
-
-
- createjs.UID = UID;
-}());
-
-//##############################################################################
-// deprecate.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Wraps deprecated methods so they still be used, but throw warnings to developers.
- *
- * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
- *
- * The recommended approach for deprecated properties is:
- *
- * try {
- * Obj ect.defineProperties(object, {
- * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
- * readWriteProp: {
- * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
- * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
- * });
- * } catch (e) {}
- *
- * @method deprecate
- * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
- * @param {String} [name=null] The name of the method or property to display in the console warning.
- * to deprecate properties.
- * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
- * logging the warning in the console.
- */
-createjs.deprecate = function(fallbackMethod, name) {
- "use strict";
- return function() {
- var msg = "Deprecated property or method '"+name+"'. See docs for info.";
- console && (console.warn ? console.warn(msg) : console.log(msg));
- return fallbackMethod && fallbackMethod.apply(this, arguments);
- }
-};
-
-//##############################################################################
-// Event.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-// constructor:
- /**
- * Contains properties and methods shared by all events for use with
- * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
- *
- * Note that Event objects are often reused, so you should never
- * rely on an event object's state outside of the call stack it was received in.
- * @class Event
- * @param {String} type The event type.
- * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
- * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
- * @constructor
- **/
- function Event(type, bubbles, cancelable) {
-
-
- // public properties:
- /**
- * The type of event.
- * @property type
- * @type String
- **/
- this.type = type;
-
- /**
- * The object that generated an event.
- * @property target
- * @type Object
- * @default null
- * @readonly
- */
- this.target = null;
-
- /**
- * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
- * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
- * is generated from childObj, then a listener on parentObj would receive the event with
- * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
- * @property currentTarget
- * @type Object
- * @default null
- * @readonly
- */
- this.currentTarget = null;
-
- /**
- * For bubbling events, this indicates the current event phase:
- * - capture phase: starting from the top parent to the target
- * - at target phase: currently being dispatched from the target
- * - bubbling phase: from the target to the top parent
- *
- * @property eventPhase
- * @type Number
- * @default 0
- * @readonly
- */
- this.eventPhase = 0;
-
- /**
- * Indicates whether the event will bubble through the display list.
- * @property bubbles
- * @type Boolean
- * @default false
- * @readonly
- */
- this.bubbles = !!bubbles;
-
- /**
- * Indicates whether the default behaviour of this event can be cancelled via
- * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
- * @property cancelable
- * @type Boolean
- * @default false
- * @readonly
- */
- this.cancelable = !!cancelable;
-
- /**
- * The epoch time at which this event was created.
- * @property timeStamp
- * @type Number
- * @default 0
- * @readonly
- */
- this.timeStamp = (new Date()).getTime();
-
- /**
- * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
- * on this event.
- * @property defaultPrevented
- * @type Boolean
- * @default false
- * @readonly
- */
- this.defaultPrevented = false;
-
- /**
- * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
- * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
- * @property propagationStopped
- * @type Boolean
- * @default false
- * @readonly
- */
- this.propagationStopped = false;
-
- /**
- * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
- * on this event.
- * @property immediatePropagationStopped
- * @type Boolean
- * @default false
- * @readonly
- */
- this.immediatePropagationStopped = false;
-
- /**
- * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
- * @property removed
- * @type Boolean
- * @default false
- * @readonly
- */
- this.removed = false;
- }
- var p = Event.prototype;
-
-// public methods:
- /**
- * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true if the event is cancelable.
- * Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will
- * cancel the default behaviour associated with the event.
- * @method preventDefault
- **/
- p.preventDefault = function() {
- this.defaultPrevented = this.cancelable&&true;
- };
-
- /**
- * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
- * Mirrors the DOM event standard.
- * @method stopPropagation
- **/
- p.stopPropagation = function() {
- this.propagationStopped = true;
- };
-
- /**
- * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
- * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
- * Mirrors the DOM event standard.
- * @method stopImmediatePropagation
- **/
- p.stopImmediatePropagation = function() {
- this.immediatePropagationStopped = this.propagationStopped = true;
- };
-
- /**
- * Causes the active listener to be removed via removeEventListener();
- *
- * myBtn.addEventListener("click", function(evt) {
- * // do stuff...
- * evt.remove(); // removes this listener.
- * });
- *
- * @method remove
- **/
- p.remove = function() {
- this.removed = true;
- };
-
- /**
- * Returns a clone of the Event instance.
- * @method clone
- * @return {Event} a clone of the Event instance.
- **/
- p.clone = function() {
- return new Event(this.type, this.bubbles, this.cancelable);
- };
-
- /**
- * Provides a chainable shortcut method for setting a number of properties on the instance.
- *
- * @method set
- * @param {Object} props A generic object containing properties to copy to the instance.
- * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
- * @chainable
- */
- p.set = function(props) {
- for (var n in props) { this[n] = props[n]; }
- return this;
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Event (type="+this.type+")]";
- };
-
- createjs.Event = Event;
-}());
-
-//##############################################################################
-// EventDispatcher.js
-//##############################################################################
-
+/*!
+* EaselJS
+* Visit http://createjs.com/ for documentation, updates and examples.
+*
+* Copyright (c) 2010 gskinner.com, inc.
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use,
+* copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following
+* conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+
+//##############################################################################
+// extend.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Sets up the prototype chain and constructor property for a new class.
+ *
+ * This should be called right after creating the class constructor.
+ *
+ * function MySubClass() {}
+ * createjs.extend(MySubClass, MySuperClass);
+ * MySubClass.prototype.doSomething = function() { }
+ *
+ * var foo = new MySubClass();
+ * console.log(foo instanceof MySuperClass); // true
+ * console.log(foo.prototype.constructor === MySubClass); // true
+ *
+ * @method extend
+ * @param {Function} subclass The subclass.
+ * @param {Function} superclass The superclass to extend.
+ * @return {Function} Returns the subclass's new prototype.
+ */
+createjs.extend = function(subclass, superclass) {
+ "use strict";
+
+ function o() { this.constructor = subclass; }
+ o.prototype = superclass.prototype;
+ return (subclass.prototype = new o());
+};
+
+//##############################################################################
+// promote.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
+ * It is recommended to use the super class's name as the prefix.
+ * An alias to the super class's constructor is always added in the format `prefix_constructor`.
+ * This allows the subclass to call super class methods without using `function.call`, providing better performance.
+ *
+ * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
+ * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
+ * prototype of `MySubClass` as `MySuperClass_draw`.
+ *
+ * This should be called after the class's prototype is fully defined.
+ *
+ * function ClassA(name) {
+ * this.name = name;
+ * }
+ * ClassA.prototype.greet = function() {
+ * return "Hello "+this.name;
+ * }
+ *
+ * function ClassB(name, punctuation) {
+ * this.ClassA_constructor(name);
+ * this.punctuation = punctuation;
+ * }
+ * createjs.extend(ClassB, ClassA);
+ * ClassB.prototype.greet = function() {
+ * return this.ClassA_greet()+this.punctuation;
+ * }
+ * createjs.promote(ClassB, "ClassA");
+ *
+ * var foo = new ClassB("World", "!?!");
+ * console.log(foo.greet()); // Hello World!?!
+ *
+ * @method promote
+ * @param {Function} subclass The class to promote super class methods on.
+ * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
+ * @return {Function} Returns the subclass.
+ */
+createjs.promote = function(subclass, prefix) {
+ "use strict";
+
+ var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
+ if (supP) {
+ subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
+ for (var n in supP) {
+ if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
+ }
+ }
+ return subclass;
+};
+
+//##############################################################################
+// indexOf.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
+ * that value. Returns -1 if value is not found.
+ *
+ * var i = createjs.indexOf(myArray, myElementToFind);
+ *
+ * @method indexOf
+ * @param {Array} array Array to search for searchElement
+ * @param searchElement Element to find in array.
+ * @return {Number} The first index of searchElement in array.
+ */
+createjs.indexOf = function (array, searchElement){
+ "use strict";
+
+ for (var i = 0,l=array.length; i < l; i++) {
+ if (searchElement === array[i]) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+//##############################################################################
+// UID.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. UID.get()
)
+ * and should not be instantiated.
+ * @class UID
+ * @static
+ **/
+ function UID() {
+ throw "UID cannot be instantiated";
+ }
+
+
+// private static properties:
+ /**
+ * @property _nextID
+ * @type Number
+ * @protected
+ **/
+ UID._nextID = 0;
+
+
+// public static methods:
+ /**
+ * Returns the next unique id.
+ * @method get
+ * @return {Number} The next unique id
+ * @static
+ **/
+ UID.get = function() {
+ return UID._nextID++;
+ };
+
+
+ createjs.UID = UID;
+}());
+
+//##############################################################################
+// deprecate.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Wraps deprecated methods so they still be used, but throw warnings to developers.
+ *
+ * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
+ *
+ * The recommended approach for deprecated properties is:
+ *
+ * try {
+ * Obj ect.defineProperties(object, {
+ * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
+ * readWriteProp: {
+ * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
+ * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
+ * });
+ * } catch (e) {}
+ *
+ * @method deprecate
+ * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
+ * @param {String} [name=null] The name of the method or property to display in the console warning.
+ * to deprecate properties.
+ * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
+ * logging the warning in the console.
+ */
+createjs.deprecate = function(fallbackMethod, name) {
+ "use strict";
+ return function() {
+ var msg = "Deprecated property or method '"+name+"'. See docs for info.";
+ console && (console.warn ? console.warn(msg) : console.log(msg));
+ return fallbackMethod && fallbackMethod.apply(this, arguments);
+ }
+};
+
+//##############################################################################
+// Event.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+// constructor:
+ /**
+ * Contains properties and methods shared by all events for use with
+ * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
+ *
+ * Note that Event objects are often reused, so you should never
+ * rely on an event object's state outside of the call stack it was received in.
+ * @class Event
+ * @param {String} type The event type.
+ * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
+ * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
+ * @constructor
+ **/
+ function Event(type, bubbles, cancelable) {
+
+
+ // public properties:
+ /**
+ * The type of event.
+ * @property type
+ * @type String
+ **/
+ this.type = type;
+
+ /**
+ * The object that generated an event.
+ * @property target
+ * @type Object
+ * @default null
+ * @readonly
+ */
+ this.target = null;
+
+ /**
+ * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
+ * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
+ * is generated from childObj, then a listener on parentObj would receive the event with
+ * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
+ * @property currentTarget
+ * @type Object
+ * @default null
+ * @readonly
+ */
+ this.currentTarget = null;
+
+ /**
+ * For bubbling events, this indicates the current event phase:
+ * - capture phase: starting from the top parent to the target
+ * - at target phase: currently being dispatched from the target
+ * - bubbling phase: from the target to the top parent
+ *
+ * @property eventPhase
+ * @type Number
+ * @default 0
+ * @readonly
+ */
+ this.eventPhase = 0;
+
+ /**
+ * Indicates whether the event will bubble through the display list.
+ * @property bubbles
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.bubbles = !!bubbles;
+
+ /**
+ * Indicates whether the default behaviour of this event can be cancelled via
+ * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
+ * @property cancelable
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.cancelable = !!cancelable;
+
+ /**
+ * The epoch time at which this event was created.
+ * @property timeStamp
+ * @type Number
+ * @default 0
+ * @readonly
+ */
+ this.timeStamp = (new Date()).getTime();
+
+ /**
+ * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
+ * on this event.
+ * @property defaultPrevented
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.defaultPrevented = false;
+
+ /**
+ * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
+ * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
+ * @property propagationStopped
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.propagationStopped = false;
+
+ /**
+ * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
+ * on this event.
+ * @property immediatePropagationStopped
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.immediatePropagationStopped = false;
+
+ /**
+ * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
+ * @property removed
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.removed = false;
+ }
+ var p = Event.prototype;
+
+// public methods:
+ /**
+ * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true if the event is cancelable.
+ * Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will
+ * cancel the default behaviour associated with the event.
+ * @method preventDefault
+ **/
+ p.preventDefault = function() {
+ this.defaultPrevented = this.cancelable&&true;
+ };
+
+ /**
+ * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
+ * Mirrors the DOM event standard.
+ * @method stopPropagation
+ **/
+ p.stopPropagation = function() {
+ this.propagationStopped = true;
+ };
+
+ /**
+ * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
+ * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
+ * Mirrors the DOM event standard.
+ * @method stopImmediatePropagation
+ **/
+ p.stopImmediatePropagation = function() {
+ this.immediatePropagationStopped = this.propagationStopped = true;
+ };
+
+ /**
+ * Causes the active listener to be removed via removeEventListener();
+ *
+ * myBtn.addEventListener("click", function(evt) {
+ * // do stuff...
+ * evt.remove(); // removes this listener.
+ * });
+ *
+ * @method remove
+ **/
+ p.remove = function() {
+ this.removed = true;
+ };
+
+ /**
+ * Returns a clone of the Event instance.
+ * @method clone
+ * @return {Event} a clone of the Event instance.
+ **/
+ p.clone = function() {
+ return new Event(this.type, this.bubbles, this.cancelable);
+ };
+
+ /**
+ * Provides a chainable shortcut method for setting a number of properties on the instance.
+ *
+ * @method set
+ * @param {Object} props A generic object containing properties to copy to the instance.
+ * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
+ * @chainable
+ */
+ p.set = function(props) {
+ for (var n in props) { this[n] = props[n]; }
+ return this;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Event (type="+this.type+")]";
+ };
+
+ createjs.Event = Event;
+}());
+
+//##############################################################################
+// EventDispatcher.js
+//##############################################################################
+
this.createjs = this.createjs||{};
(function() {
@@ -836,5106 +836,12 @@ this.createjs = this.createjs||{};
createjs.EventDispatcher = EventDispatcher;
-}());
-
-//##############################################################################
-// Ticker.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * The Ticker provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe to the tick
- * event to be notified when a set time interval has elapsed.
- *
- * Note that the interval that the tick event is called is a target interval, and may be broadcast at a slower interval
- * when under high CPU load. The Ticker class uses a static interface (ex. `Ticker.framerate = 30;`) and
- * can not be instantiated.
- *
- * Example
- *
- * createjs.Ticker.addEventListener("tick", handleTick);
- * function handleTick(event) {
- * // Actions carried out each tick (aka frame)
- * if (!event.paused) {
- * // Actions carried out when the Ticker is not paused.
- * }
- * }
- *
- * @class Ticker
- * @uses EventDispatcher
- * @static
- **/
- function Ticker() {
- throw "Ticker cannot be instantiated.";
- }
-
-
-// constants:
- /**
- * In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It
- * uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and
- * dispatches the tick when the time is within a certain threshold.
- *
- * This mode has a higher variance for time between frames than {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}},
- * but does not require that content be time based as with {{#crossLink "Ticker/RAF:property"}}{{/crossLink}} while
- * gaining the benefits of that API (screen synch, background throttling).
- *
- * Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so
- * framerates of 10, 12, 15, 20, and 30 work well.
- *
- * Falls back to {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
- * supported.
- * @property RAF_SYNCHED
- * @static
- * @type {String}
- * @default "synched"
- * @readonly
- **/
- Ticker.RAF_SYNCHED = "synched";
-
- /**
- * In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely.
- * Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based.
- * You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
- * event object's "delta" properties to make this easier.
- *
- * Falls back on {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
- * supported.
- * @property RAF
- * @static
- * @type {String}
- * @default "raf"
- * @readonly
- **/
- Ticker.RAF = "raf";
-
- /**
- * In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not
- * provide the benefits of requestAnimationFrame (screen synch, background throttling).
- * @property TIMEOUT
- * @static
- * @type {String}
- * @default "timeout"
- * @readonly
- **/
- Ticker.TIMEOUT = "timeout";
-
-
-// static events:
- /**
- * Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using
- * {{#crossLink "Ticker/paused:property"}}{{/crossLink}}.
- *
- * Example
- *
- * createjs.Ticker.addEventListener("tick", handleTick);
- * function handleTick(event) {
- * console.log("Paused:", event.paused, event.delta);
- * }
- *
- * @event tick
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {Boolean} paused Indicates whether the ticker is currently paused.
- * @param {Number} delta The time elapsed in ms since the last tick.
- * @param {Number} time The total time in ms since Ticker was initialized.
- * @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example,
- * you could determine the amount of time that the Ticker has been paused since initialization with `time-runTime`.
- * @since 0.6.0
- */
-
-
-// public static properties:
- /**
- * Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See
- * {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}}, {{#crossLink "Ticker/RAF:property"}}{{/crossLink}}, and
- * {{#crossLink "Ticker/RAF_SYNCHED:property"}}{{/crossLink}} for mode details.
- * @property timingMode
- * @static
- * @type {String}
- * @default Ticker.TIMEOUT
- **/
- Ticker.timingMode = null;
-
- /**
- * Specifies a maximum value for the delta property in the tick event object. This is useful when building time
- * based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep,
- * alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value
- * (ex. maxDelta=50 when running at 40fps).
- *
- * This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta
- * when using both delta and other values.
- *
- * If 0, there is no maximum.
- * @property maxDelta
- * @static
- * @type {number}
- * @default 0
- */
- Ticker.maxDelta = 0;
-
- /**
- * When the ticker is paused, all listeners will still receive a tick event, but the paused
property
- * of the event will be `true`. Also, while paused the `runTime` will not increase. See {{#crossLink "Ticker/tick:event"}}{{/crossLink}},
- * {{#crossLink "Ticker/getTime"}}{{/crossLink}}, and {{#crossLink "Ticker/getEventTime"}}{{/crossLink}} for more
- * info.
- *
- * Example
- *
- * createjs.Ticker.addEventListener("tick", handleTick);
- * createjs.Ticker.paused = true;
- * function handleTick(event) {
- * console.log(event.paused,
- * createjs.Ticker.getTime(false),
- * createjs.Ticker.getTime(true));
- * }
- *
- * @property paused
- * @static
- * @type {Boolean}
- * @default false
- **/
- Ticker.paused = false;
-
-
-// mix-ins:
- // EventDispatcher methods:
- Ticker.removeEventListener = null;
- Ticker.removeAllEventListeners = null;
- Ticker.dispatchEvent = null;
- Ticker.hasEventListener = null;
- Ticker._listeners = null;
- createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods.
- Ticker._addEventListener = Ticker.addEventListener;
- Ticker.addEventListener = function() {
- !Ticker._inited&&Ticker.init();
- return Ticker._addEventListener.apply(Ticker, arguments);
- };
-
-
-// private static properties:
- /**
- * @property _inited
- * @static
- * @type {Boolean}
- * @private
- **/
- Ticker._inited = false;
-
- /**
- * @property _startTime
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._startTime = 0;
-
- /**
- * @property _pausedTime
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._pausedTime=0;
-
- /**
- * The number of ticks that have passed
- * @property _ticks
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._ticks = 0;
-
- /**
- * The number of ticks that have passed while Ticker has been paused
- * @property _pausedTicks
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._pausedTicks = 0;
-
- /**
- * @property _interval
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._interval = 50;
-
- /**
- * @property _lastTime
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._lastTime = 0;
-
- /**
- * @property _times
- * @static
- * @type {Array}
- * @private
- **/
- Ticker._times = null;
-
- /**
- * @property _tickTimes
- * @static
- * @type {Array}
- * @private
- **/
- Ticker._tickTimes = null;
-
- /**
- * Stores the timeout or requestAnimationFrame id.
- * @property _timerId
- * @static
- * @type {Number}
- * @private
- **/
- Ticker._timerId = null;
-
- /**
- * True if currently using requestAnimationFrame, false if using setTimeout. This may be different than timingMode
- * if that property changed and a tick hasn't fired.
- * @property _raf
- * @static
- * @type {Boolean}
- * @private
- **/
- Ticker._raf = true;
-
-
-// static getter / setters:
- /**
- * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
- * @method _setInterval
- * @private
- * @static
- * @param {Number} interval
- **/
- Ticker._setInterval = function(interval) {
- Ticker._interval = interval;
- if (!Ticker._inited) { return; }
- Ticker._setupTick();
- };
- // Ticker.setInterval is @deprecated. Remove for 1.1+
- Ticker.setInterval = createjs.deprecate(Ticker._setInterval, "Ticker.setInterval");
-
- /**
- * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
- * @method _getInterval
- * @private
- * @static
- * @return {Number}
- **/
- Ticker._getInterval = function() {
- return Ticker._interval;
- };
- // Ticker.getInterval is @deprecated. Remove for 1.1+
- Ticker.getInterval = createjs.deprecate(Ticker._getInterval, "Ticker.getInterval");
-
- /**
- * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
- * @method _setFPS
- * @private
- * @static
- * @param {Number} value
- **/
- Ticker._setFPS = function(value) {
- Ticker._setInterval(1000/value);
- };
- // Ticker.setFPS is @deprecated. Remove for 1.1+
- Ticker.setFPS = createjs.deprecate(Ticker._setFPS, "Ticker.setFPS");
-
- /**
- * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
- * @method _getFPS
- * @static
- * @private
- * @return {Number}
- **/
- Ticker._getFPS = function() {
- return 1000/Ticker._interval;
- };
- // Ticker.getFPS is @deprecated. Remove for 1.1+
- Ticker.getFPS = createjs.deprecate(Ticker._getFPS, "Ticker.getFPS");
-
- /**
- * Indicates the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
- * Note that actual time between ticks may be more than specified depending on CPU load.
- * This property is ignored if the ticker is using the `RAF` timing mode.
- * @property interval
- * @static
- * @type {Number}
- **/
-
- /**
- * Indicates the target frame rate in frames per second (FPS). Effectively just a shortcut to `interval`, where
- * `framerate == 1000/interval`.
- * @property framerate
- * @static
- * @type {Number}
- **/
- try {
- Object.defineProperties(Ticker, {
- interval: { get: Ticker._getInterval, set: Ticker._setInterval },
- framerate: { get: Ticker._getFPS, set: Ticker._setFPS }
- });
- } catch (e) { console.log(e); }
-
-
-// public static methods:
- /**
- * Starts the tick. This is called automatically when the first listener is added.
- * @method init
- * @static
- **/
- Ticker.init = function() {
- if (Ticker._inited) { return; }
- Ticker._inited = true;
- Ticker._times = [];
- Ticker._tickTimes = [];
- Ticker._startTime = Ticker._getTime();
- Ticker._times.push(Ticker._lastTime = 0);
- Ticker.interval = Ticker._interval;
- };
-
- /**
- * Stops the Ticker and removes all listeners. Use init() to restart the Ticker.
- * @method reset
- * @static
- **/
- Ticker.reset = function() {
- if (Ticker._raf) {
- var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame;
- f&&f(Ticker._timerId);
- } else {
- clearTimeout(Ticker._timerId);
- }
- Ticker.removeAllEventListeners("tick");
- Ticker._timerId = Ticker._times = Ticker._tickTimes = null;
- Ticker._startTime = Ticker._lastTime = Ticker._ticks = Ticker._pausedTime = 0;
- Ticker._inited = false;
- };
-
- /**
- * Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS
- * because it only measures the time spent within the tick execution stack.
- *
- * Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between
- * the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that
- * there may be up to 35ms of "idle" time between the end of one tick and the start of the next.
- *
- * Example 2: With a target FPS of 30, {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} returns 10fps, which
- * indicates an average of 100ms between the end of one tick and the end of the next. However, {{#crossLink "Ticker/getMeasuredTickTime"}}{{/crossLink}}
- * returns 20ms. This would indicate that something other than the tick is using ~80ms (another script, DOM
- * rendering, etc).
- * @method getMeasuredTickTime
- * @static
- * @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick.
- * Defaults to the number of ticks per second. To get only the last tick's time, pass in 1.
- * @return {Number} The average time spent in a tick in milliseconds.
- **/
- Ticker.getMeasuredTickTime = function(ticks) {
- var ttl=0, times=Ticker._tickTimes;
- if (!times || times.length < 1) { return -1; }
-
- // by default, calculate average for the past ~1 second:
- ticks = Math.min(times.length, ticks||(Ticker._getFPS()|0));
- for (var i=0; i= (Ticker._interval-1)*0.97) {
- Ticker._tick();
- }
- };
-
- /**
- * @method _handleRAF
- * @static
- * @private
- **/
- Ticker._handleRAF = function() {
- Ticker._timerId = null;
- Ticker._setupTick();
- Ticker._tick();
- };
-
- /**
- * @method _handleTimeout
- * @static
- * @private
- **/
- Ticker._handleTimeout = function() {
- Ticker._timerId = null;
- Ticker._setupTick();
- Ticker._tick();
- };
-
- /**
- * @method _setupTick
- * @static
- * @private
- **/
- Ticker._setupTick = function() {
- if (Ticker._timerId != null) { return; } // avoid duplicates
-
- var mode = Ticker.timingMode;
- if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) {
- var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
- if (f) {
- Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch);
- Ticker._raf = true;
- return;
- }
- }
- Ticker._raf = false;
- Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval);
- };
-
- /**
- * @method _tick
- * @static
- * @private
- **/
- Ticker._tick = function() {
- var paused = Ticker.paused;
- var time = Ticker._getTime();
- var elapsedTime = time-Ticker._lastTime;
- Ticker._lastTime = time;
- Ticker._ticks++;
-
- if (paused) {
- Ticker._pausedTicks++;
- Ticker._pausedTime += elapsedTime;
- }
-
- if (Ticker.hasEventListener("tick")) {
- var event = new createjs.Event("tick");
- var maxDelta = Ticker.maxDelta;
- event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime;
- event.paused = paused;
- event.time = time;
- event.runTime = time-Ticker._pausedTime;
- Ticker.dispatchEvent(event);
- }
-
- Ticker._tickTimes.unshift(Ticker._getTime()-time);
- while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); }
-
- Ticker._times.unshift(time);
- while (Ticker._times.length > 100) { Ticker._times.pop(); }
- };
-
- /**
- * @method _getTime
- * @static
- * @private
- **/
- var w=window, now=w.performance.now || w.performance.mozNow || w.performance.msNow || w.performance.oNow || w.performance.webkitNow;
- Ticker._getTime = function() {
- return ((now&&now.call(w.performance))||(new Date().getTime())) - Ticker._startTime;
- };
-
-
- createjs.Ticker = Ticker;
-}());
-
-//##############################################################################
-// VideoBuffer.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * When an HTML video seeks, including when looping, there is an indeterminate period before a new frame is available.
- * This can result in the video blinking or flashing when it is drawn to a canvas. The VideoBuffer class resolves
- * this issue by drawing each frame to an off-screen canvas and preserving the prior frame during a seek.
- *
- * var myBuffer = new createjs.VideoBuffer(myVideo);
- * var myBitmap = new Bitmap(myBuffer);
- *
- * @class VideoBuffer
- * @param {HTMLVideoElement} video The HTML video element to buffer.
- * @constructor
- **/
- function VideoBuffer(video) {
-
- // private properties:
- /**
- * Used by Bitmap to determine when the video buffer is ready to be drawn. Not intended for general use.
- * @property readyState
- * @protected
- * @type {Number}
- * @default 0
- **/
- this.readyState = video.readyState;
-
- /**
- * @property _video
- * @protected
- * @type {HTMLVideoElement}
- * @default 0
- **/
- this._video = video;
-
- /**
- * @property _canvas
- * @protected
- * @type {HTMLCanvasElement}
- * @default 0
- **/
- this._canvas = null;
-
- /**
- * @property _lastTime
- * @protected
- * @type {Number}
- * @default -1
- **/
- this._lastTime = -1;
-
- if (this.readyState < 2) { video.addEventListener("canplaythrough", this._videoReady.bind(this)); } //once:true isn't supported everywhere, but its a non-critical optimization here.
- }
- var p = VideoBuffer.prototype;
-
-
-// public methods:
- /**
- * Gets an HTML canvas element showing the current video frame, or the previous frame if in a seek / loop.
- * Primarily for use by {{#crossLink "Bitmap"}}{{/crossLink}}.
- * @method getImage
- **/
- p.getImage = function() {
- if (this.readyState < 2) { return; }
- var canvas=this._canvas, video = this._video;
- if (!canvas) {
- canvas = this._canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
- canvas.width = video.videoWidth;
- canvas.height = video.videoHeight;
- }
- if (video.readyState >= 2 && video.currentTime !== this._lastTime) {
- var ctx = canvas.getContext("2d");
- ctx.clearRect(0,0,canvas.width,canvas.height);
- ctx.drawImage(video,0,0,canvas.width,canvas.height);
- this._lastTime = video.currentTime;
- }
- return canvas;
- };
-
-// private methods:
- /**
- * @method _videoReady
- * @protected
- **/
- p._videoReady = function() {
- this.readyState = 2;
- };
-
-
- createjs.VideoBuffer = VideoBuffer;
-}());
-
-//##############################################################################
-// MouseEvent.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties,
- * see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings.
- * @class MouseEvent
- * @param {String} type The event type.
- * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
- * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
- * @param {Number} stageX The normalized x position relative to the stage.
- * @param {Number} stageY The normalized y position relative to the stage.
- * @param {MouseEvent} nativeEvent The native DOM event related to this mouse event.
- * @param {Number} pointerID The unique id for the pointer.
- * @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment.
- * @param {Number} rawX The raw x position relative to the stage.
- * @param {Number} rawY The raw y position relative to the stage.
- * @param {DisplayObject} relatedTarget The secondary target for the event.
- * @extends Event
- * @constructor
- **/
- function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY, relatedTarget) {
- this.Event_constructor(type, bubbles, cancelable);
-
-
- // public properties:
- /**
- * The normalized x position on the stage. This will always be within the range 0 to stage width.
- * @property stageX
- * @type Number
- */
- this.stageX = stageX;
-
- /**
- * The normalized y position on the stage. This will always be within the range 0 to stage height.
- * @property stageY
- * @type Number
- **/
- this.stageY = stageY;
-
- /**
- * The raw x position relative to the stage. Normally this will be the same as the stageX value, unless
- * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
- * @property rawX
- * @type Number
- */
- this.rawX = (rawX==null)?stageX:rawX;
-
- /**
- * The raw y position relative to the stage. Normally this will be the same as the stageY value, unless
- * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
- * @property rawY
- * @type Number
- */
- this.rawY = (rawY==null)?stageY:rawY;
-
- /**
- * The native MouseEvent generated by the browser. The properties and API for this
- * event may differ between browsers. This property will be null if the
- * EaselJS property was not directly generated from a native MouseEvent.
- * @property nativeEvent
- * @type HtmlMouseEvent
- * @default null
- **/
- this.nativeEvent = nativeEvent;
-
- /**
- * The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system
- * supplied id value.
- * @property pointerID
- * @type {Number}
- */
- this.pointerID = pointerID;
-
- /**
- * Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse.
- * For touch pointers, the first pointer in the current stack will be considered the primary pointer.
- * @property primary
- * @type {Boolean}
- */
- this.primary = !!primary;
-
- /**
- * The secondary target for the event, if applicable. This is used for mouseout/rollout
- * events to indicate the object that the mouse entered from, mouseover/rollover for the object the mouse exited,
- * and stagemousedown/stagemouseup events for the object that was the under the cursor, if any.
- *
- * Only valid interaction targets will be returned (ie. objects with mouse listeners or a cursor set).
- * @property relatedTarget
- * @type {DisplayObject}
- */
- this.relatedTarget = relatedTarget;
- }
- var p = createjs.extend(MouseEvent, createjs.Event);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// getter / setters:
- /**
- * Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
- * @property localX
- * @type {Number}
- * @readonly
- */
- p._get_localX = function() {
- return this.currentTarget.globalToLocal(this.rawX, this.rawY).x;
- };
-
- /**
- * Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
- * @property localY
- * @type {Number}
- * @readonly
- */
- p._get_localY = function() {
- return this.currentTarget.globalToLocal(this.rawX, this.rawY).y;
- };
-
- /**
- * Indicates whether the event was generated by a touch input (versus a mouse input).
- * @property isTouch
- * @type {Boolean}
- * @readonly
- */
- p._get_isTouch = function() {
- return this.pointerID !== -1;
- };
-
-
- try {
- Object.defineProperties(p, {
- localX: { get: p._get_localX },
- localY: { get: p._get_localY },
- isTouch: { get: p._get_isTouch }
- });
- } catch (e) {} // TODO: use Log
-
-
-// public methods:
- /**
- * Returns a clone of the MouseEvent instance.
- * @method clone
- * @return {MouseEvent} a clone of the MouseEvent instance.
- **/
- p.clone = function() {
- return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY);
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]";
- };
-
-
- createjs.MouseEvent = createjs.promote(MouseEvent, "Event");
-}());
-
-//##############################################################################
-// Matrix2D.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Represents an affine transformation matrix, and provides tools for constructing and concatenating matrices.
- *
- * This matrix can be visualized as:
- *
- * [ a c tx
- * b d ty
- * 0 0 1 ]
- *
- * Note the locations of b and c.
- *
- * @class Matrix2D
- * @param {Number} [a=1] Specifies the a property for the new matrix.
- * @param {Number} [b=0] Specifies the b property for the new matrix.
- * @param {Number} [c=0] Specifies the c property for the new matrix.
- * @param {Number} [d=1] Specifies the d property for the new matrix.
- * @param {Number} [tx=0] Specifies the tx property for the new matrix.
- * @param {Number} [ty=0] Specifies the ty property for the new matrix.
- * @constructor
- **/
- function Matrix2D(a, b, c, d, tx, ty) {
- this.setValues(a,b,c,d,tx,ty);
-
- // public properties:
- // assigned in the setValues method.
- /**
- * Position (0, 0) in a 3x3 affine transformation matrix.
- * @property a
- * @type Number
- **/
-
- /**
- * Position (0, 1) in a 3x3 affine transformation matrix.
- * @property b
- * @type Number
- **/
-
- /**
- * Position (1, 0) in a 3x3 affine transformation matrix.
- * @property c
- * @type Number
- **/
-
- /**
- * Position (1, 1) in a 3x3 affine transformation matrix.
- * @property d
- * @type Number
- **/
-
- /**
- * Position (2, 0) in a 3x3 affine transformation matrix.
- * @property tx
- * @type Number
- **/
-
- /**
- * Position (2, 1) in a 3x3 affine transformation matrix.
- * @property ty
- * @type Number
- **/
- }
- var p = Matrix2D.prototype;
-
-// constants:
- /**
- * Multiplier for converting degrees to radians. Used internally by Matrix2D.
- * @property DEG_TO_RAD
- * @static
- * @final
- * @type Number
- * @readonly
- **/
- Matrix2D.DEG_TO_RAD = Math.PI/180;
-
-
-// static public properties:
- /**
- * An identity matrix, representing a null transformation.
- * @property identity
- * @static
- * @type Matrix2D
- * @readonly
- **/
- Matrix2D.identity = null; // set at bottom of class definition.
-
-
-// public methods:
- /**
- * Sets the specified values on this instance.
- * @method setValues
- * @param {Number} [a=1] Specifies the a property for the new matrix.
- * @param {Number} [b=0] Specifies the b property for the new matrix.
- * @param {Number} [c=0] Specifies the c property for the new matrix.
- * @param {Number} [d=1] Specifies the d property for the new matrix.
- * @param {Number} [tx=0] Specifies the tx property for the new matrix.
- * @param {Number} [ty=0] Specifies the ty property for the new matrix.
- * @return {Matrix2D} This instance. Useful for chaining method calls.
- */
- p.setValues = function(a, b, c, d, tx, ty) {
- // don't forget to update docs in the constructor if these change:
- this.a = (a == null) ? 1 : a;
- this.b = b || 0;
- this.c = c || 0;
- this.d = (d == null) ? 1 : d;
- this.tx = tx || 0;
- this.ty = ty || 0;
- return this;
- };
-
- /**
- * Appends the specified matrix properties to this matrix. All parameters are required.
- * This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
- * @method append
- * @param {Number} a
- * @param {Number} b
- * @param {Number} c
- * @param {Number} d
- * @param {Number} tx
- * @param {Number} ty
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.append = function(a, b, c, d, tx, ty) {
- var a1 = this.a;
- var b1 = this.b;
- var c1 = this.c;
- var d1 = this.d;
- if (a != 1 || b != 0 || c != 0 || d != 1) {
- this.a = a1*a+c1*b;
- this.b = b1*a+d1*b;
- this.c = a1*c+c1*d;
- this.d = b1*c+d1*d;
- }
- this.tx = a1*tx+c1*ty+this.tx;
- this.ty = b1*tx+d1*ty+this.ty;
- return this;
- };
-
- /**
- * Prepends the specified matrix properties to this matrix.
- * This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
- * All parameters are required.
- * @method prepend
- * @param {Number} a
- * @param {Number} b
- * @param {Number} c
- * @param {Number} d
- * @param {Number} tx
- * @param {Number} ty
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.prepend = function(a, b, c, d, tx, ty) {
- var a1 = this.a;
- var c1 = this.c;
- var tx1 = this.tx;
-
- this.a = a*a1+c*this.b;
- this.b = b*a1+d*this.b;
- this.c = a*c1+c*this.d;
- this.d = b*c1+d*this.d;
- this.tx = a*tx1+c*this.ty+tx;
- this.ty = b*tx1+d*this.ty+ty;
- return this;
- };
-
- /**
- * Appends the specified matrix to this matrix.
- * This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
- * @method appendMatrix
- * @param {Matrix2D} matrix
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.appendMatrix = function(matrix) {
- return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
- };
-
- /**
- * Prepends the specified matrix to this matrix.
- * This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
- * For example, you could calculate the combined transformation for a child object using:
- *
- * var o = myDisplayObject;
- * var mtx = o.getMatrix();
- * while (o = o.parent) {
- * // prepend each parent's transformation in turn:
- * o.prependMatrix(o.getMatrix());
- * }
- * @method prependMatrix
- * @param {Matrix2D} matrix
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.prependMatrix = function(matrix) {
- return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
- };
-
- /**
- * Generates matrix properties from the specified display object transform properties, and appends them to this matrix.
- * For example, you can use this to generate a matrix representing the transformations of a display object:
- *
- * var mtx = new createjs.Matrix2D();
- * mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation);
- * @method appendTransform
- * @param {Number} x
- * @param {Number} y
- * @param {Number} scaleX
- * @param {Number} scaleY
- * @param {Number} rotation
- * @param {Number} skewX
- * @param {Number} skewY
- * @param {Number} regX Optional.
- * @param {Number} regY Optional.
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
- if (rotation%360) {
- var r = rotation*Matrix2D.DEG_TO_RAD;
- var cos = Math.cos(r);
- var sin = Math.sin(r);
- } else {
- cos = 1;
- sin = 0;
- }
-
- if (skewX || skewY) {
- // TODO: can this be combined into a single append operation?
- skewX *= Matrix2D.DEG_TO_RAD;
- skewY *= Matrix2D.DEG_TO_RAD;
- this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
- this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
- } else {
- this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
- }
-
- if (regX || regY) {
- // append the registration offset:
- this.tx -= regX*this.a+regY*this.c;
- this.ty -= regX*this.b+regY*this.d;
- }
- return this;
- };
-
- /**
- * Generates matrix properties from the specified display object transform properties, and prepends them to this matrix.
- * For example, you could calculate the combined transformation for a child object using:
- *
- * var o = myDisplayObject;
- * var mtx = new createjs.Matrix2D();
- * do {
- * // prepend each parent's transformation in turn:
- * mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
- * } while (o = o.parent);
- *
- * Note that the above example would not account for {{#crossLink "DisplayObject/transformMatrix:property"}}{{/crossLink}}
- * values. See {{#crossLink "Matrix2D/prependMatrix"}}{{/crossLink}} for an example that does.
- * @method prependTransform
- * @param {Number} x
- * @param {Number} y
- * @param {Number} scaleX
- * @param {Number} scaleY
- * @param {Number} rotation
- * @param {Number} skewX
- * @param {Number} skewY
- * @param {Number} regX Optional.
- * @param {Number} regY Optional.
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
- if (rotation%360) {
- var r = rotation*Matrix2D.DEG_TO_RAD;
- var cos = Math.cos(r);
- var sin = Math.sin(r);
- } else {
- cos = 1;
- sin = 0;
- }
-
- if (regX || regY) {
- // prepend the registration offset:
- this.tx -= regX; this.ty -= regY;
- }
- if (skewX || skewY) {
- // TODO: can this be combined into a single prepend operation?
- skewX *= Matrix2D.DEG_TO_RAD;
- skewY *= Matrix2D.DEG_TO_RAD;
- this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
- this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
- } else {
- this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
- }
- return this;
- };
-
- /**
- * Applies a clockwise rotation transformation to the matrix.
- * @method rotate
- * @param {Number} angle The angle to rotate by, in degrees. To use a value in radians, multiply it by `180/Math.PI`.
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.rotate = function(angle) {
- angle = angle*Matrix2D.DEG_TO_RAD;
- var cos = Math.cos(angle);
- var sin = Math.sin(angle);
-
- var a1 = this.a;
- var b1 = this.b;
-
- this.a = a1*cos+this.c*sin;
- this.b = b1*cos+this.d*sin;
- this.c = -a1*sin+this.c*cos;
- this.d = -b1*sin+this.d*cos;
- return this;
- };
-
- /**
- * Applies a skew transformation to the matrix.
- * @method skew
- * @param {Number} skewX The amount to skew horizontally in degrees. To use a value in radians, multiply it by `180/Math.PI`.
- * @param {Number} skewY The amount to skew vertically in degrees.
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- */
- p.skew = function(skewX, skewY) {
- skewX = skewX*Matrix2D.DEG_TO_RAD;
- skewY = skewY*Matrix2D.DEG_TO_RAD;
- this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0);
- return this;
- };
-
- /**
- * Applies a scale transformation to the matrix.
- * @method scale
- * @param {Number} x The amount to scale horizontally. E.G. a value of 2 will double the size in the X direction, and 0.5 will halve it.
- * @param {Number} y The amount to scale vertically.
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.scale = function(x, y) {
- this.a *= x;
- this.b *= x;
- this.c *= y;
- this.d *= y;
- //this.tx *= x;
- //this.ty *= y;
- return this;
- };
-
- /**
- * Translates the matrix on the x and y axes.
- * @method translate
- * @param {Number} x
- * @param {Number} y
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.translate = function(x, y) {
- this.tx += this.a*x + this.c*y;
- this.ty += this.b*x + this.d*y;
- return this;
- };
-
- /**
- * Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation).
- * @method identity
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.identity = function() {
- this.a = this.d = 1;
- this.b = this.c = this.tx = this.ty = 0;
- return this;
- };
-
- /**
- * Inverts the matrix, causing it to perform the opposite transformation.
- * @method invert
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- **/
- p.invert = function() {
- var a1 = this.a;
- var b1 = this.b;
- var c1 = this.c;
- var d1 = this.d;
- var tx1 = this.tx;
- var n = a1*d1-b1*c1;
-
- this.a = d1/n;
- this.b = -b1/n;
- this.c = -c1/n;
- this.d = a1/n;
- this.tx = (c1*this.ty-d1*tx1)/n;
- this.ty = -(a1*this.ty-b1*tx1)/n;
- return this;
- };
-
- /**
- * Returns true if the matrix is an identity matrix.
- * @method isIdentity
- * @return {Boolean}
- **/
- p.isIdentity = function() {
- return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
- };
-
- /**
- * Returns true if this matrix is equal to the specified matrix (all property values are equal).
- * @method equals
- * @param {Matrix2D} matrix The matrix to compare.
- * @return {Boolean}
- **/
- p.equals = function(matrix) {
- return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d;
- };
-
- /**
- * Transforms a point according to this matrix.
- * @method transformPoint
- * @param {Number} x The x component of the point to transform.
- * @param {Number} y The y component of the point to transform.
- * @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned.
- * @return {Point} This matrix. Useful for chaining method calls.
- **/
- p.transformPoint = function(x, y, pt) {
- pt = pt||{};
- pt.x = x*this.a+y*this.c+this.tx;
- pt.y = x*this.b+y*this.d+this.ty;
- return pt;
- };
-
- /**
- * Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values
- * may not match the transform properties you used to generate the matrix, though they will produce the same visual
- * results.
- * @method decompose
- * @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned.
- * @return {Object} The target, or a new generic object with the transform properties applied.
- */
- p.decompose = function(target) {
- // TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative
- if (target == null) { target = {}; }
- target.x = this.tx;
- target.y = this.ty;
- target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
- target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);
-
- var skewX = Math.atan2(-this.c, this.d);
- var skewY = Math.atan2(this.b, this.a);
-
- var delta = Math.abs(1-skewX/skewY);
- if (delta < 0.00001) { // effectively identical, can use rotation:
- target.rotation = skewY/Matrix2D.DEG_TO_RAD;
- if (this.a < 0 && this.d >= 0) {
- target.rotation += (target.rotation <= 0) ? 180 : -180;
- }
- target.skewX = target.skewY = 0;
- } else {
- target.skewX = skewX/Matrix2D.DEG_TO_RAD;
- target.skewY = skewY/Matrix2D.DEG_TO_RAD;
- }
- return target;
- };
-
- /**
- * Copies all properties from the specified matrix to this matrix.
- * @method copy
- * @param {Matrix2D} matrix The matrix to copy properties from.
- * @return {Matrix2D} This matrix. Useful for chaining method calls.
- */
- p.copy = function(matrix) {
- return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
- };
-
- /**
- * Returns a clone of the Matrix2D instance.
- * @method clone
- * @return {Matrix2D} a clone of the Matrix2D instance.
- **/
- p.clone = function() {
- return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty);
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]";
- };
-
- // this has to be populated after the class is defined:
- Matrix2D.identity = new Matrix2D();
-
-
- createjs.Matrix2D = Matrix2D;
-}());
-
-//##############################################################################
-// DisplayProps.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
- /**
- * Used for calculating and encapsulating display related properties.
- * @class DisplayProps
- * @param {Number} [visible=true] Visible value.
- * @param {Number} [alpha=1] Alpha value.
- * @param {Number} [shadow=null] A Shadow instance or null.
- * @param {Number} [compositeOperation=null] A compositeOperation value or null.
- * @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix.
- * @constructor
- **/
- function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) {
- this.setValues(visible, alpha, shadow, compositeOperation, matrix);
-
- // public properties:
- // assigned in the setValues method.
- /**
- * Property representing the alpha that will be applied to a display object.
- * @property alpha
- * @type Number
- **/
-
- /**
- * Property representing the shadow that will be applied to a display object.
- * @property shadow
- * @type Shadow
- **/
-
- /**
- * Property representing the compositeOperation that will be applied to a display object.
- * You can find a list of valid composite operations at:
- * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing
- * @property compositeOperation
- * @type String
- **/
-
- /**
- * Property representing the value for visible that will be applied to a display object.
- * @property visible
- * @type Boolean
- **/
-
- /**
- * The transformation matrix that will be applied to a display object.
- * @property matrix
- * @type Matrix2D
- **/
- }
- var p = DisplayProps.prototype;
-
-// initialization:
- /**
- * Reinitializes the instance with the specified values.
- * @method setValues
- * @param {Number} [visible=true] Visible value.
- * @param {Number} [alpha=1] Alpha value.
- * @param {Number} [shadow=null] A Shadow instance or null.
- * @param {Number} [compositeOperation=null] A compositeOperation value or null.
- * @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix.
- * @return {DisplayProps} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) {
- this.visible = visible == null ? true : !!visible;
- this.alpha = alpha == null ? 1 : alpha;
- this.shadow = shadow;
- this.compositeOperation = compositeOperation;
- this.matrix = matrix || (this.matrix&&this.matrix.identity()) || new createjs.Matrix2D();
- return this;
- };
-
-// public methods:
- /**
- * Appends the specified display properties. This is generally used to apply a child's properties its parent's.
- * @method append
- * @param {Boolean} visible desired visible value
- * @param {Number} alpha desired alpha value
- * @param {Shadow} shadow desired shadow value
- * @param {String} compositeOperation desired composite operation value
- * @param {Matrix2D} [matrix] a Matrix2D instance
- * @return {DisplayProps} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.append = function(visible, alpha, shadow, compositeOperation, matrix) {
- this.alpha *= alpha;
- this.shadow = shadow || this.shadow;
- this.compositeOperation = compositeOperation || this.compositeOperation;
- this.visible = this.visible && visible;
- matrix&&this.matrix.appendMatrix(matrix);
- return this;
- };
-
- /**
- * Prepends the specified display properties. This is generally used to apply a parent's properties to a child's.
- * For example, to get the combined display properties that would be applied to a child, you could use:
- *
- * var o = myDisplayObject;
- * var props = new createjs.DisplayProps();
- * do {
- * // prepend each parent's props in turn:
- * props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix());
- * } while (o = o.parent);
- *
- * @method prepend
- * @param {Boolean} visible desired visible value
- * @param {Number} alpha desired alpha value
- * @param {Shadow} shadow desired shadow value
- * @param {String} compositeOperation desired composite operation value
- * @param {Matrix2D} [matrix] a Matrix2D instance
- * @return {DisplayProps} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.prepend = function(visible, alpha, shadow, compositeOperation, matrix) {
- this.alpha *= alpha;
- this.shadow = this.shadow || shadow;
- this.compositeOperation = this.compositeOperation || compositeOperation;
- this.visible = this.visible && visible;
- matrix&&this.matrix.prependMatrix(matrix);
- return this;
- };
-
- /**
- * Resets this instance and its matrix to default values.
- * @method identity
- * @return {DisplayProps} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.identity = function() {
- this.visible = true;
- this.alpha = 1;
- this.shadow = this.compositeOperation = null;
- this.matrix.identity();
- return this;
- };
-
- /**
- * Returns a clone of the DisplayProps instance. Clones the associated matrix.
- * @method clone
- * @return {DisplayProps} a clone of the DisplayProps instance.
- **/
- p.clone = function() {
- return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone());
- };
-
-// private methods:
-
- createjs.DisplayProps = DisplayProps;
-})();
-
-//##############################################################################
-// Point.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Represents a point on a 2 dimensional x / y coordinate system.
- *
- * Example
- *
- * var point = new createjs.Point(0, 100);
- *
- * @class Point
- * @param {Number} [x=0] X position.
- * @param {Number} [y=0] Y position.
- * @constructor
- **/
- function Point(x, y) {
- this.setValues(x, y);
-
-
- // public properties:
- // assigned in the setValues method.
- /**
- * X position.
- * @property x
- * @type Number
- **/
-
- /**
- * Y position.
- * @property y
- * @type Number
- **/
- }
- var p = Point.prototype;
-
-// public methods:
- /**
- * Sets the specified values on this instance.
- * @method setValues
- * @param {Number} [x=0] X position.
- * @param {Number} [y=0] Y position.
- * @return {Point} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.setValues = function(x, y) {
- this.x = x||0;
- this.y = y||0;
- return this;
- };
-
- /**
- * Copies all properties from the specified point to this point.
- * @method copy
- * @param {Point} point The point to copy properties from.
- * @return {Point} This point. Useful for chaining method calls.
- * @chainable
- */
- p.copy = function(point) {
- this.x = point.x;
- this.y = point.y;
- return this;
- };
-
- /**
- * Returns a clone of the Point instance.
- * @method clone
- * @return {Point} a clone of the Point instance.
- **/
- p.clone = function() {
- return new Point(this.x, this.y);
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Point (x="+this.x+" y="+this.y+")]";
- };
-
-
- createjs.Point = Point;
-}());
-
-//##############################################################################
-// Rectangle.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Represents a rectangle as defined by the points (x, y) and (x+width, y+height).
- *
- * Example
- *
- * var rect = new createjs.Rectangle(0, 0, 100, 100);
- *
- * @class Rectangle
- * @param {Number} [x=0] X position.
- * @param {Number} [y=0] Y position.
- * @param {Number} [width=0] The width of the Rectangle.
- * @param {Number} [height=0] The height of the Rectangle.
- * @constructor
- **/
- function Rectangle(x, y, width, height) {
- this.setValues(x, y, width, height);
-
-
- // public properties:
- // assigned in the setValues method.
- /**
- * X position.
- * @property x
- * @type Number
- **/
-
- /**
- * Y position.
- * @property y
- * @type Number
- **/
-
- /**
- * Width.
- * @property width
- * @type Number
- **/
-
- /**
- * Height.
- * @property height
- * @type Number
- **/
- }
- var p = Rectangle.prototype;
-
-// public methods:
- /**
- * Sets the specified values on this instance.
- * @method setValues
- * @param {Number} [x=0] X position.
- * @param {Number} [y=0] Y position.
- * @param {Number} [width=0] The width of the Rectangle.
- * @param {Number} [height=0] The height of the Rectangle.
- * @return {Rectangle} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.setValues = function(x, y, width, height) {
- // don't forget to update docs in the constructor if these change:
- this.x = x||0;
- this.y = y||0;
- this.width = width||0;
- this.height = height||0;
- return this;
- };
-
- /**
- * Extends the rectangle's bounds to include the described point or rectangle.
- * @method extend
- * @param {Number} x X position of the point or rectangle.
- * @param {Number} y Y position of the point or rectangle.
- * @param {Number} [width=0] The width of the rectangle.
- * @param {Number} [height=0] The height of the rectangle.
- * @return {Rectangle} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.extend = function(x, y, width, height) {
- width = width||0;
- height = height||0;
- if (x+width > this.x+this.width) { this.width = x+width-this.x; }
- if (y+height > this.y+this.height) { this.height = y+height-this.y; }
- if (x < this.x) { this.width += this.x-x; this.x = x; }
- if (y < this.y) { this.height += this.y-y; this.y = y; }
- return this;
- };
-
- /**
- * Adds the specified padding to the rectangle's bounds.
- * @method pad
- * @param {Number} top
- * @param {Number} left
- * @param {Number} bottom
- * @param {Number} right
- * @return {Rectangle} This instance. Useful for chaining method calls.
- * @chainable
- */
- p.pad = function(top, left, bottom, right) {
- this.x -= left;
- this.y -= top;
- this.width += left+right;
- this.height += top+bottom;
- return this;
- };
-
- /**
- * Copies all properties from the specified rectangle to this rectangle.
- * @method copy
- * @param {Rectangle} rectangle The rectangle to copy properties from.
- * @return {Rectangle} This rectangle. Useful for chaining method calls.
- * @chainable
- */
- p.copy = function(rectangle) {
- return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
- };
-
- /**
- * Returns true if this rectangle fully encloses the described point or rectangle.
- * @method contains
- * @param {Number} x X position of the point or rectangle.
- * @param {Number} y Y position of the point or rectangle.
- * @param {Number} [width=0] The width of the rectangle.
- * @param {Number} [height=0] The height of the rectangle.
- * @return {Boolean} True if the described point or rectangle is contained within this rectangle.
- */
- p.contains = function(x, y, width, height) {
- width = width||0;
- height = height||0;
- return (x >= this.x && x+width <= this.x+this.width && y >= this.y && y+height <= this.y+this.height);
- };
-
- /**
- * Returns a new rectangle which contains this rectangle and the specified rectangle.
- * @method union
- * @param {Rectangle} rect The rectangle to calculate a union with.
- * @return {Rectangle} A new rectangle describing the union.
- */
- p.union = function(rect) {
- return this.clone().extend(rect.x, rect.y, rect.width, rect.height);
- };
-
- /**
- * Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle,
- * or null if they do not intersect.
- * @method intersection
- * @param {Rectangle} rect The rectangle to calculate an intersection with.
- * @return {Rectangle} A new rectangle describing the intersection or null.
- */
- p.intersection = function(rect) {
- var x1 = rect.x, y1 = rect.y, x2 = x1+rect.width, y2 = y1+rect.height;
- if (this.x > x1) { x1 = this.x; }
- if (this.y > y1) { y1 = this.y; }
- if (this.x + this.width < x2) { x2 = this.x + this.width; }
- if (this.y + this.height < y2) { y2 = this.y + this.height; }
- return (x2 <= x1 || y2 <= y1) ? null : new Rectangle(x1, y1, x2-x1, y2-y1);
- };
-
- /**
- * Returns true if the specified rectangle intersects (has any overlap) with this rectangle.
- * @method intersects
- * @param {Rectangle} rect The rectangle to compare.
- * @return {Boolean} True if the rectangles intersect.
- */
- p.intersects = function(rect) {
- return (rect.x <= this.x+this.width && this.x <= rect.x+rect.width && rect.y <= this.y+this.height && this.y <= rect.y + rect.height);
- };
-
- /**
- * Returns true if the width or height are equal or less than 0.
- * @method isEmpty
- * @return {Boolean} True if the rectangle is empty.
- */
- p.isEmpty = function() {
- return this.width <= 0 || this.height <= 0;
- };
-
- /**
- * Returns a clone of the Rectangle instance.
- * @method clone
- * @return {Rectangle} a clone of the Rectangle instance.
- **/
- p.clone = function() {
- return new Rectangle(this.x, this.y, this.width, this.height);
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]";
- };
-
-
- createjs.Rectangle = Rectangle;
-}());
-
-//##############################################################################
-// ButtonHelper.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or
- * {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and
- * automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}},
- * to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame.
- *
- * The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent
- * garbage collection.
- *
- * Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
- *
- * Example
- *
- * var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit");
- * myInstance.addEventListener("click", handleClick);
- * function handleClick(event) {
- * // Click Happened.
- * }
- *
- * @class ButtonHelper
- * @param {Sprite|MovieClip} target The instance to manage.
- * @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button.
- * @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button.
- * @param {String} [downLabel="down"] The label or animation to go to when the user presses the button.
- * @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing
- * states.
- * @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined,
- * then the button's visible states will be used instead. Note that the same instance as the "target" argument can be
- * used for the hitState.
- * @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is
- * null, then the default state of the hitArea will be used. *
- * @constructor
- */
- function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) {
- if (!target.addEventListener) { return; }
-
-
- // public properties:
- /**
- * The target for this button helper.
- * @property target
- * @type MovieClip | Sprite
- * @readonly
- **/
- this.target = target;
-
- /**
- * The label name or frame number to display when the user mouses out of the target. Defaults to "over".
- * @property overLabel
- * @type String | Number
- **/
- this.overLabel = overLabel == null ? "over" : overLabel;
-
- /**
- * The label name or frame number to display when the user mouses over the target. Defaults to "out".
- * @property outLabel
- * @type String | Number
- **/
- this.outLabel = outLabel == null ? "out" : outLabel;
-
- /**
- * The label name or frame number to display when the user presses on the target. Defaults to "down".
- * @property downLabel
- * @type String | Number
- **/
- this.downLabel = downLabel == null ? "down" : downLabel;
-
- /**
- * If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false.
- * @property play
- * @default false
- * @type Boolean
- **/
- this.play = play;
-
-
- // private properties
- /**
- * @property _isPressed
- * @type Boolean
- * @protected
- **/
- this._isPressed = false;
-
- /**
- * @property _isOver
- * @type Boolean
- * @protected
- **/
- this._isOver = false;
-
- /**
- * @property _enabled
- * @type Boolean
- * @protected
- **/
- this._enabled = false;
-
- // setup:
- target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes.
- this.enabled = true;
- this.handleEvent({});
- if (hitArea) {
- if (hitLabel) {
- hitArea.actionsEnabled = false;
- hitArea.gotoAndStop&&hitArea.gotoAndStop(hitLabel);
- }
- target.hitArea = hitArea;
- }
- }
- var p = ButtonHelper.prototype;
-
-// getter / setters:
- /**
- * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
- * @method setEnabled
- * @param {Boolean} value The enabled property to set the instance to.
- * @[rptected
- * @protected
- **/
- p._setEnabled = function(value) {
- if (value == this._enabled) { return; }
- var o = this.target;
- this._enabled = value;
- if (value) {
- o.cursor = "pointer";
- o.addEventListener("rollover", this);
- o.addEventListener("rollout", this);
- o.addEventListener("mousedown", this);
- o.addEventListener("pressup", this);
- if (o._reset) { o.__reset = o._reset; o._reset = this._reset;}
- } else {
- o.cursor = null;
- o.removeEventListener("rollover", this);
- o.removeEventListener("rollout", this);
- o.removeEventListener("mousedown", this);
- o.removeEventListener("pressup", this);
- if (o.__reset) { o._reset = o.__reset; delete(o.__reset); }
- }
- };
- // ButtonHelper.setEnabled is @deprecated. Remove for 1.1+
- p.setEnabled = createjs.deprecate(p._setEnabled, "ButtonHelper.setEnabled");
-
- /**
- * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
- * @method getEnabled
- * @protected
- * @return {Boolean}
- **/
- p._getEnabled = function() {
- return this._enabled;
- };
- // ButtonHelper.getEnabled is @deprecated. Remove for 1.1+
- p.getEnabled = createjs.deprecate(p._getEnabled, "ButtonHelper.getEnabled");
-
- /**
- * Enables or disables the button functionality on the target.
- * @property enabled
- * @type {Boolean}
- **/
- try {
- Object.defineProperties(p, {
- enabled: { get: p._getEnabled, set: p._setEnabled }
- });
- } catch (e) {} // TODO: use Log
-
-
-// public methods:
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[ButtonHelper]";
- };
-
-
-// private methods:
- /**
- * @method handleEvent
- * @param {Object} evt The mouse event to handle.
- * @protected
- **/
- p.handleEvent = function(evt) {
- var label, t = this.target, type = evt.type;
- if (type == "mousedown") {
- this._isPressed = true;
- label = this.downLabel;
- } else if (type == "pressup") {
- this._isPressed = false;
- label = this._isOver ? this.overLabel : this.outLabel;
- } else if (type == "rollover") {
- this._isOver = true;
- label = this._isPressed ? this.downLabel : this.overLabel;
- } else { // rollout and default
- this._isOver = false;
- label = this._isPressed ? this.overLabel : this.outLabel;
- }
- if (this.play) {
- t.gotoAndPlay&&t.gotoAndPlay(label);
- } else {
- t.gotoAndStop&&t.gotoAndStop(label);
- }
- };
-
- /**
- * Injected into target. Preserves the paused state through a reset.
- * @method _reset
- * @protected
- **/
- p._reset = function() {
- // TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures.
- var p = this.paused;
- this.__reset();
- this.paused = p;
- };
-
-
- createjs.ButtonHelper = ButtonHelper;
-}());
-
-//##############################################################################
-// Shadow.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}}
- * via its shadow
property.
- *
- * Example
- *
- * myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10);
- *
- * @class Shadow
- * @constructor
- * @param {String} color The color of the shadow. This can be any valid CSS color value.
- * @param {Number} offsetX The x offset of the shadow in pixels.
- * @param {Number} offsetY The y offset of the shadow in pixels.
- * @param {Number} blur The size of the blurring effect.
- **/
- function Shadow(color, offsetX, offsetY, blur) {
-
-
- // public properties:
- /**
- * The color of the shadow. This can be any valid CSS color value.
- * @property color
- * @type String
- * @default null
- */
- this.color = color||"black";
-
- /** The x offset of the shadow.
- * @property offsetX
- * @type Number
- * @default 0
- */
- this.offsetX = offsetX||0;
-
- /** The y offset of the shadow.
- * @property offsetY
- * @type Number
- * @default 0
- */
- this.offsetY = offsetY||0;
-
- /** The blur of the shadow.
- * @property blur
- * @type Number
- * @default 0
- */
- this.blur = blur||0;
- }
- var p = Shadow.prototype;
-
-// static public properties:
- /**
- * An identity shadow object (all properties are set to 0).
- * @property identity
- * @type Shadow
- * @static
- * @final
- * @readonly
- **/
- Shadow.identity = new Shadow("transparent", 0, 0, 0);
-
-
-// public methods:
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Shadow]";
- };
-
- /**
- * Returns a clone of this Shadow instance.
- * @method clone
- * @return {Shadow} A clone of the current Shadow instance.
- **/
- p.clone = function() {
- return new Shadow(this.color, this.offsetX, this.offsetY, this.blur);
- };
-
-
- createjs.Shadow = Shadow;
-}());
-
-//##############################################################################
-// SpriteSheet.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually
- * animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100
- * images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high).
- *
- * The data passed to the SpriteSheet constructor defines:
- *
- * - The source image or images to use.
- * - The positions of individual image frames.
- * - Sequences of frames that form named animations. Optional.
- * - The target playback framerate. Optional.
- *
- * SpriteSheet Format
- * SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties
- * (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON.
- *
- * images
- * An array of source images. Images can be either an HTMlimage
- * instance, or a uri to an image. The former is recommended to control preloading.
- *
- * images: [image1, "path/to/image2.png"],
- *
- * frames
- * Defines the individual frames. There are two supported formats for frame data:
- * When all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`,
- * and `count` properties.
- *
- *
- * - `width` & `height` are required and specify the dimensions of the frames
- * - `regX` & `regY` indicate the registration point or "origin" of the frames
- * - `spacing` indicate the spacing between frames
- * - `margin` specify the margin around the image(s)
- * - `count` allows you to specify the total number of frames in the spritesheet; if omitted, this will
- * be calculated based on the dimensions of the source images and the frames. Frames will be assigned
- * indexes based on their position in the source images (left to right, top to bottom).
- *
- *
- * frames: {width:64, height:64, count:20, regX: 32, regY:64, spacing:0, margin:0}
- *
- * If the frames are of different sizes, use an array of frame definitions. Each definition is itself an array
- * with 4 required and 3 optional entries, in the order:
- *
- *
- * - The first four, `x`, `y`, `width`, and `height` are required and define the frame rectangle.
- * - The fifth, `imageIndex`, specifies the index of the source image (defaults to 0)
- * - The last two, `regX` and `regY` specify the registration point of the frame
- *
- *
- * frames: [
- * // x, y, width, height, imageIndex*, regX*, regY*
- * [64, 0, 96, 64],
- * [0, 0, 64, 64, 1, 32, 32]
- * // etc.
- * ]
- *
- * animations
- * Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an
- * animation of the same name. Each animation must specify the frames to play, and may
- * also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and
- * the name of the `next` animation to sequence to after it completes.
- *
- * There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate:
- *
- * - for a single frame animation, you can simply specify the frame index
- *
- * animations: {
- * sit: 7
- * }
- *
- *
- * -
- * for an animation of consecutive frames, you can use an array with two required, and two optional entries
- * in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive.
- *
- * animations: {
- * // start, end, next*, speed*
- * run: [0, 8],
- * jump: [9, 12, "run", 2]
- * }
- *
- *
- * -
- * for non-consecutive frames, you can use an object with a `frames` property defining an array of frame
- * indexes to play in order. The object can also specify `next` and `speed` properties.
- *
- * animations: {
- * walk: {
- * frames: [1,2,3,3,2,1]
- * },
- * shoot: {
- * frames: [1,4,5,6],
- * next: "walk",
- * speed: 0.5
- * }
- * }
- *
- *
- *
- * Note: the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency`
- * property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in
- * earlier versions, but is 4x normal speed in EaselJS 0.7.0+.
- *
- * framerate
- * Optional. Indicates the default framerate to play this spritesheet at in frames per second. See
- * {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information.
- *
- * framerate: 20
- *
- * Note that the Sprite framerate will only work if the stage update method is provided with the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
- * event generated by the {{#crossLink "Ticker"}}{{/crossLink}}.
- *
- * createjs.Ticker.on("tick", handleTick);
- * function handleTick(event) {
- * stage.update(event);
- * }
- *
- * Example
- * To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three
- * animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and
- * sequencing back to run.
- *
- * var data = {
- * images: ["sprites.jpg"],
- * frames: {width:50, height:50},
- * animations: {
- * stand:0,
- * run:[1,5],
- * jump:[6,8,"run"]
- * }
- * };
- * var spriteSheet = new createjs.SpriteSheet(data);
- * var animation = new createjs.Sprite(spriteSheet, "run");
- *
- * Generating SpriteSheet Images
- * Spritesheets can be created manually by combining images in PhotoShop, and specifying the frame size or
- * coordinates manually, however there are a number of tools that facilitate this.
- *
- * - Exporting SpriteSheets or HTML5 content from Adobe Flash/Animate supports the EaselJS SpriteSheet format.
- * - The popular Texture Packer has
- * EaselJS support.
- *
- SWF animations in Adobe Flash/Animate can be exported to SpriteSheets using Zoë
- *
- *
- * Cross Origin Issues
- * Warning: Images loaded cross-origin will throw cross-origin security errors when interacted with
- * using:
- *
- * - a mouse
- * - methods such as {{#crossLink "Container/getObjectUnderPoint"}}{{/crossLink}}
- * - Filters (see {{#crossLink "Filter"}}{{/crossLink}})
- * - caching (see {{#crossLink "DisplayObject/cache"}}{{/crossLink}})
- *
- * You can get around this by setting `crossOrigin` property on your images before passing them to EaselJS, or
- * setting the `crossOrigin` property on PreloadJS' LoadQueue or LoadItems.
- *
- * var image = new Image();
- * img.crossOrigin="Anonymous";
- * img.src = "http://server-with-CORS-support.com/path/to/image.jpg";
- *
- * If you pass string paths to SpriteSheets, they will not work cross-origin. The server that stores the image must
- * support cross-origin requests, or this will not work. For more information, check out
- * CORS overview on MDN.
- *
- * @class SpriteSheet
- * @constructor
- * @param {Object} data An object describing the SpriteSheet data.
- * @extends EventDispatcher
- **/
- function SpriteSheet(data) {
- this.EventDispatcher_constructor();
-
-
- // public properties:
- /**
- * Indicates whether all images are finished loading.
- * @property complete
- * @type Boolean
- * @readonly
- **/
- this.complete = true;
-
- /**
- * Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See the Sprite class
- * {{#crossLink "Sprite/framerate:property"}}{{/crossLink}} for more information.
- * @property framerate
- * @type Number
- **/
- this.framerate = 0;
-
-
- // private properties:
- /**
- * @property _animations
- * @protected
- * @type Array
- **/
- this._animations = null;
-
- /**
- * @property _frames
- * @protected
- * @type Array
- **/
- this._frames = null;
-
- /**
- * @property _images
- * @protected
- * @type Array
- **/
- this._images = null;
-
- /**
- * @property _data
- * @protected
- * @type Object
- **/
- this._data = null;
-
- /**
- * @property _loadCount
- * @protected
- * @type Number
- **/
- this._loadCount = 0;
-
- // only used for simple frame defs:
- /**
- * @property _frameHeight
- * @protected
- * @type Number
- **/
- this._frameHeight = 0;
-
- /**
- * @property _frameWidth
- * @protected
- * @type Number
- **/
- this._frameWidth = 0;
-
- /**
- * @property _numFrames
- * @protected
- * @type Number
- **/
- this._numFrames = 0;
-
- /**
- * @property _regX
- * @protected
- * @type Number
- **/
- this._regX = 0;
-
- /**
- * @property _regY
- * @protected
- * @type Number
- **/
- this._regY = 0;
-
- /**
- * @property _spacing
- * @protected
- * @type Number
- **/
- this._spacing = 0;
-
- /**
- * @property _margin
- * @protected
- * @type Number
- **/
- this._margin = 0;
-
- // setup:
- this._parseData(data);
- }
- var p = createjs.extend(SpriteSheet, createjs.EventDispatcher);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// events:
- /**
- * Dispatched when all images are loaded. Note that this only fires if the images
- * were not fully loaded when the sprite sheet was initialized. You should check the complete property
- * to prior to adding a listener. Ex.
- *
- * var sheet = new createjs.SpriteSheet(data);
- * if (!sheet.complete) {
- * // not preloaded, listen for the complete event:
- * sheet.addEventListener("complete", handler);
- * }
- *
- * @event complete
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.6.0
- */
-
- /**
- * Dispatched when getFrame is called with a valid frame index. This is primarily intended for use by {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}}
- * when doing on-demand rendering.
- * @event getframe
- * @param {Number} index The frame index.
- * @param {Object} frame The frame object that getFrame will return.
- */
-
- /**
- * Dispatched when an image encounters an error. A SpriteSheet will dispatch an error event for each image that
- * encounters an error, and will still dispatch a {{#crossLink "SpriteSheet/complete:event"}}{{/crossLink}}
- * event once all images are finished processing, even if an error is encountered.
- * @event error
- * @param {String} src The source of the image that failed to load.
- * @since 0.8.2
- */
-
-
-// getter / setters:
- /**
- * Use the {{#crossLink "SpriteSheet/animations:property"}}{{/crossLink}} property instead.
- * @method _getAnimations
- * @protected
- * @return {Array}
- **/
- p._getAnimations = function() {
- return this._animations.slice();
- };
- // SpriteSheet.getAnimations is @deprecated. Remove for 1.1+
- p.getAnimations = createjs.deprecate(p._getAnimations, "SpriteSheet.getAnimations");
-
- /**
- * Returns an array of all available animation names available on this sprite sheet as strings.
- * @property animations
- * @type {Array}
- * @readonly
- **/
- try {
- Object.defineProperties(p, {
- animations: { get: p._getAnimations }
- });
- } catch (e) {}
-
-
-// public methods:
- /**
- * Returns the total number of frames in the specified animation, or in the whole sprite
- * sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and
- * the images have not been fully loaded.
- * @method getNumFrames
- * @param {String} animation The name of the animation to get a frame count for.
- * @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted.
- */
- p.getNumFrames = function(animation) {
- if (animation == null) {
- return this._frames ? this._frames.length : this._numFrames || 0;
- } else {
- var data = this._data[animation];
- if (data == null) { return 0; }
- else { return data.frames.length; }
- }
- };
-
- /**
- * Returns an object defining the specified animation. The returned object contains:
- * - frames: an array of the frame ids in the animation
- * - speed: the playback speed for this animation
- * - name: the name of the animation
- * - next: the default animation to play next. If the animation loops, the name and next property will be the
- * same.
- *
- * @method getAnimation
- * @param {String} name The name of the animation to get.
- * @return {Object} a generic object with frames, speed, name, and next properties.
- **/
- p.getAnimation = function(name) {
- return this._data[name];
- };
-
- /**
- * Returns an object specifying the image and source rect of the specified frame. The returned object has:
- * - an image property holding a reference to the image object in which the frame is found
- * - a rect property containing a Rectangle instance which defines the boundaries for the frame within that
- * image.
- * - A regX and regY property corresponding to the regX/Y values for the frame.
- *
- * @method getFrame
- * @param {Number} frameIndex The index of the frame.
- * @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist.
- **/
- p.getFrame = function(frameIndex) {
- var frame;
- if (this._frames && (frame=this._frames[frameIndex])) { return frame; }
- return null;
- };
-
- /**
- * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative
- * to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return:
- *
- * [x=-50, y=-40, width=90, height=70]
- *
- * @method getFrameBounds
- * @param {Number} frameIndex The index of the frame.
- * @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created.
- * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded.
- **/
- p.getFrameBounds = function(frameIndex, rectangle) {
- var frame = this.getFrame(frameIndex);
- return frame ? (rectangle||new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null;
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[SpriteSheet]";
- };
-
- /**
- * SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it.
- * @method clone
- **/
- p.clone = function() {
- throw("SpriteSheet cannot be cloned.")
- };
-
-// private methods:
- /**
- * @method _parseData
- * @param {Object} data An object describing the SpriteSheet data.
- * @protected
- **/
- p._parseData = function(data) {
- var i,l,o,a;
- if (data == null) { return; }
-
- this.framerate = data.framerate||0;
-
- // parse images:
- if (data.images && (l=data.images.length) > 0) {
- a = this._images = [];
- for (i=0; i= maxFrames) { break imgLoop; }
- frameCount++;
- this._frames.push({
- image: img,
- rect: new createjs.Rectangle(x, y, frameWidth, frameHeight),
- regX: this._regX,
- regY: this._regY
- });
- x += frameWidth+spacing;
- }
- y += frameHeight+spacing;
- }
- }
- this._numFrames = frameCount;
- };
-
-
- createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher");
-}());
-
-//##############################################################################
-// Graphics.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a
- * specified context. Note that you can use Graphics without any dependency on the EaselJS framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}}
- * directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the
- * context of an EaselJS display list.
- *
- * There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or
- * instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}.
- * The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes.
- *
- * var g = new createjs.Graphics();
- * g.setStrokeStyle(1);
- * g.beginStroke("#000000");
- * g.beginFill("red");
- * g.drawCircle(0,0,30);
- *
- * All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example,
- * the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill:
- *
- * myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50);
- *
- * Each graphics API call generates a command object (see below). The last command to be created can be accessed via
- * {{#crossLink "Graphics/command:property"}}{{/crossLink}}:
- *
- * var fillCommand = myGraphics.beginFill("red").command;
- * // ... later, update the fill style/color:
- * fillCommand.style = "blue";
- * // or change it to a bitmap fill:
- * fillCommand.bitmap(myImage);
- *
- * For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you
- * need to manage path creation manually, and ensure that fill/stroke is applied to a defined path:
- *
- * // start a new path. Graphics.beginCmd is a reusable BeginPath instance:
- * myGraphics.append(createjs.Graphics.beginCmd);
- * // we need to define the path before applying the fill:
- * var circle = new createjs.Graphics.Circle(0,0,30);
- * myGraphics.append(circle);
- * // fill the path we just defined:
- * var fill = new createjs.Graphics.Fill("red");
- * myGraphics.append(fill);
- *
- * These approaches can be used together, for example to insert a custom command:
- *
- * myGraphics.beginFill("red");
- * var customCommand = new CustomSpiralCommand(etc);
- * myGraphics.append(customCommand);
- * myGraphics.beginFill("blue");
- * myGraphics.drawCircle(0, 0, 30);
- *
- * See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands.
- *
- * Tiny API
- * The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the
- * Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS
- * to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected
- * descriptions in the docs.
- *
- *
- * Tiny | Method | Tiny | Method |
- * mt | {{#crossLink "Graphics/moveTo"}}{{/crossLink}} |
- * lt | {{#crossLink "Graphics/lineTo"}}{{/crossLink}} |
- * a/at | {{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} |
- * bt | {{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} |
- * qt | {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo) |
- * r | {{#crossLink "Graphics/rect"}}{{/crossLink}} |
- * cp | {{#crossLink "Graphics/closePath"}}{{/crossLink}} |
- * c | {{#crossLink "Graphics/clear"}}{{/crossLink}} |
- * f | {{#crossLink "Graphics/beginFill"}}{{/crossLink}} |
- * lf | {{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} |
- * rf | {{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} |
- * bf | {{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} |
- * ef | {{#crossLink "Graphics/endFill"}}{{/crossLink}} |
- * ss / sd | {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} / {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} |
- * s | {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} |
- * ls | {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} |
- * rs | {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} |
- * bs | {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} |
- * es | {{#crossLink "Graphics/endStroke"}}{{/crossLink}} |
- * dr | {{#crossLink "Graphics/drawRect"}}{{/crossLink}} |
- * rr | {{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} |
- * rc | {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} |
- * dc | {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} |
- * de | {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} |
- * dp | {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} |
- * p | {{#crossLink "Graphics/decodePath"}}{{/crossLink}} |
- *
- *
- * Here is the above example, using the tiny API instead.
- *
- * myGraphics.s("red").f("blue").r(20, 20, 100, 50);
- *
- * @class Graphics
- * @constructor
- **/
- function Graphics() {
-
-
- // public properties
- /**
- * Holds a reference to the last command that was created or appended. For example, you could retain a reference
- * to a Fill command in order to dynamically update the color later by using:
- *
- * var myFill = myGraphics.beginFill("red").command;
- * // update color later:
- * myFill.style = "yellow";
- *
- * @property command
- * @type Object
- **/
- this.command = null;
-
-
- // private properties
- /**
- * @property _stroke
- * @protected
- * @type {Stroke}
- **/
- this._stroke = null;
-
- /**
- * @property _strokeStyle
- * @protected
- * @type {StrokeStyle}
- **/
- this._strokeStyle = null;
-
- /**
- * @property _oldStrokeStyle
- * @protected
- * @type {StrokeStyle}
- **/
- this._oldStrokeStyle = null;
-
- /**
- * @property _strokeDash
- * @protected
- * @type {StrokeDash}
- **/
- this._strokeDash = null;
-
- /**
- * @property _oldStrokeDash
- * @protected
- * @type {StrokeDash}
- **/
- this._oldStrokeDash = null;
-
- /**
- * @property _strokeIgnoreScale
- * @protected
- * @type Boolean
- **/
- this._strokeIgnoreScale = false;
-
- /**
- * @property _fill
- * @protected
- * @type {Fill}
- **/
- this._fill = null;
-
- /**
- * @property _instructions
- * @protected
- * @type {Array}
- **/
- this._instructions = [];
-
- /**
- * Indicates the last instruction index that was committed.
- * @property _commitIndex
- * @protected
- * @type {Number}
- **/
- this._commitIndex = 0;
-
- /**
- * Uncommitted instructions.
- * @property _activeInstructions
- * @protected
- * @type {Array}
- **/
- this._activeInstructions = [];
-
- /**
- * This indicates that there have been changes to the activeInstruction list since the last updateInstructions call.
- * @property _dirty
- * @protected
- * @type {Boolean}
- * @default false
- **/
- this._dirty = false;
-
- /**
- * Index to draw from if a store operation has happened.
- * @property _storeIndex
- * @protected
- * @type {Number}
- * @default 0
- **/
- this._storeIndex = 0;
-
- // setup:
- this.clear();
- }
- var p = Graphics.prototype;
- var G = Graphics; // shortcut
-
-// static public methods:
- /**
- * Returns a CSS compatible color string based on the specified RGB numeric color values in the format
- * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example,
- *
- * createjs.Graphics.getRGB(50, 100, 150, 0.5);
- * // Returns "rgba(50,100,150,0.5)"
- *
- * It also supports passing a single hex color value as the first param, and an optional alpha value as the second
- * param. For example,
- *
- * createjs.Graphics.getRGB(0xFF00FF, 0.2);
- * // Returns "rgba(255,0,255,0.2)"
- *
- * @method getRGB
- * @static
- * @param {Number} r The red component for the color, between 0 and 0xFF (255).
- * @param {Number} g The green component for the color, between 0 and 0xFF (255).
- * @param {Number} b The blue component for the color, between 0 and 0xFF (255).
- * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
- * @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format
- * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)".
- **/
- Graphics.getRGB = function(r, g, b, alpha) {
- if (r != null && b == null) {
- alpha = g;
- b = r&0xFF;
- g = r>>8&0xFF;
- r = r>>16&0xFF;
- }
- if (alpha == null) {
- return "rgb("+r+","+g+","+b+")";
- } else {
- return "rgba("+r+","+g+","+b+","+alpha+")";
- }
- };
-
- /**
- * Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)",
- * or if alpha is null then in the format "hsl(360,100,100)".
- *
- * createjs.Graphics.getHSL(150, 100, 70);
- * // Returns "hsl(150,100,70)"
- *
- * @method getHSL
- * @static
- * @param {Number} hue The hue component for the color, between 0 and 360.
- * @param {Number} saturation The saturation component for the color, between 0 and 100.
- * @param {Number} lightness The lightness component for the color, between 0 and 100.
- * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
- * @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format
- * "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)".
- **/
- Graphics.getHSL = function(hue, saturation, lightness, alpha) {
- if (alpha == null) {
- return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)";
- } else {
- return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")";
- }
- };
-
-
-// static properties:
- /**
- * A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid
- * unnecessary instantiation.
- * @property beginCmd
- * @type {Graphics.BeginPath}
- * @static
- **/
- // defined at the bottom of this file.
-
- /**
- * Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}.
- * @property BASE_64
- * @static
- * @final
- * @readonly
- * @type {Object}
- **/
- Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63};
-
- /**
- * Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
- * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
- * "butt", 1 to "round", and 2 to "square".
- * For example, to set the line caps to "square":
- *
- * myGraphics.ss(16, 2);
- *
- * @property STROKE_CAPS_MAP
- * @static
- * @final
- * @readonly
- * @type {Array}
- **/
- Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"];
-
- /**
- * Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
- * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
- * "miter", 1 to "round", and 2 to "bevel".
- * For example, to set the line joints to "bevel":
- *
- * myGraphics.ss(16, 0, 2);
- *
- * @property STROKE_JOINTS_MAP
- * @static
- * @final
- * @readonly
- * @type {Array}
- **/
- Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"];
-
- /**
- * @property _ctx
- * @static
- * @protected
- * @type {CanvasRenderingContext2D}
- **/
- var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
- if (canvas.getContext) {
- Graphics._ctx = canvas.getContext("2d");
- canvas.width = canvas.height = 1;
- }
-
-
-// getter / setters:
- /**
- * Use the {{#crossLink "Graphics/instructions:property"}}{{/crossLink}} property instead.
- * @method _getInstructions
- * @protected
- * @return {Array} The instructions array, useful for chaining
- **/
- p._getInstructions = function() {
- this._updateInstructions();
- return this._instructions;
- };
- // Graphics.getInstructions is @deprecated. Remove for 1.1+
- p.getInstructions = createjs.deprecate(p._getInstructions, "Graphics.getInstructions");
-
- /**
- * Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect)
- * Modifying the returned array directly is not recommended, and is likely to result in unexpected behaviour.
- *
- * This property is mainly intended for introspection of the instructions (ex. for graphics export).
- * @property instructions
- * @type {Array}
- * @readonly
- **/
- try {
- Object.defineProperties(p, {
- instructions: { get: p._getInstructions }
- });
- } catch (e) {}
-
-
-// public methods:
- /**
- * Returns true if this Graphics instance has no drawing commands.
- * @method isEmpty
- * @return {Boolean} Returns true if this Graphics instance has no drawing commands.
- **/
- p.isEmpty = function() {
- return !(this._instructions.length || this._activeInstructions.length);
- };
-
- /**
- * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data.
- **/
- p.draw = function(ctx, data) {
- this._updateInstructions();
- var instr = this._instructions;
- for (var i=this._storeIndex, l=instr.length; iDisplayObject.mask to draw the clipping path, for example.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method drawAsPath
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- **/
- p.drawAsPath = function(ctx) {
- this._updateInstructions();
- var instr, instrs = this._instructions;
- for (var i=this._storeIndex, l=instrs.length; i
- * whatwg spec.
- * @method lineTo
- * @param {Number} x The x coordinate the drawing point should draw to.
- * @param {Number} y The y coordinate the drawing point should draw to.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.lineTo = function(x, y) {
- return this.append(new G.LineTo(x,y));
- };
-
- /**
- * Draws an arc with the specified control points and radius. For detailed information, read the
- *
- * whatwg spec. A tiny API method "at" also exists.
- * @method arcTo
- * @param {Number} x1
- * @param {Number} y1
- * @param {Number} x2
- * @param {Number} y2
- * @param {Number} radius
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.arcTo = function(x1, y1, x2, y2, radius) {
- return this.append(new G.ArcTo(x1, y1, x2, y2, radius));
- };
-
- /**
- * Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For
- * example, to draw a full circle with a radius of 20 centered at (100, 100):
- *
- * arc(100, 100, 20, 0, Math.PI*2);
- *
- * For detailed information, read the
- * whatwg spec.
- * A tiny API method "a" also exists.
- * @method arc
- * @param {Number} x
- * @param {Number} y
- * @param {Number} radius
- * @param {Number} startAngle Measured in radians.
- * @param {Number} endAngle Measured in radians.
- * @param {Boolean} anticlockwise
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
- return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise));
- };
-
- /**
- * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed
- * information, read the
- * whatwg spec. A tiny API method "qt" also exists.
- * @method quadraticCurveTo
- * @param {Number} cpx
- * @param {Number} cpy
- * @param {Number} x
- * @param {Number} y
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.quadraticCurveTo = function(cpx, cpy, x, y) {
- return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y));
- };
-
- /**
- * Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x,
- * cp2y). For detailed information, read the
- *
- * whatwg spec. A tiny API method "bt" also exists.
- * @method bezierCurveTo
- * @param {Number} cp1x
- * @param {Number} cp1y
- * @param {Number} cp2x
- * @param {Number} cp2y
- * @param {Number} x
- * @param {Number} y
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
- return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y));
- };
-
- /**
- * Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke.
- * For detailed information, read the
- *
- * whatwg spec. A tiny API method "r" also exists.
- * @method rect
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w Width of the rectangle
- * @param {Number} h Height of the rectangle
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.rect = function(x, y, w, h) {
- return this.append(new G.Rect(x, y, w, h));
- };
-
- /**
- * Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified
- * since the fill or stroke was last set. A tiny API method "cp" also exists.
- * @method closePath
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.closePath = function() {
- return this._activeInstructions.length ? this.append(new G.ClosePath()) : this;
- };
-
-
-// public methods that roughly map to Adobe Flash/Animate graphics APIs:
- /**
- * Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need
- * to be redefined to draw shapes following a clear call. A tiny API method "c" also exists.
- * @method clear
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.clear = function() {
- this._instructions.length = this._activeInstructions.length = this._commitIndex = 0;
- this._strokeStyle = this._oldStrokeStyle = this._stroke = this._fill = this._strokeDash = this._oldStrokeDash = null;
- this._dirty = this._strokeIgnoreScale = false;
- return this;
- };
-
- /**
- * Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists.
- * @method beginFill
- * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
- * null will result in no fill.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginFill = function(color) {
- return this._setFill(color ? new G.Fill(color) : null);
- };
-
- /**
- * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
- * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
- * square to display it:
- *
- * myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
- *
- * A tiny API method "lf" also exists.
- * @method beginLinearGradientFill
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
- * drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
- * the first color to 10% then interpolating to the second color at 90%.
- * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
- * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) {
- return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1));
- };
-
- /**
- * Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to
- * blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it:
- *
- * myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50);
- *
- * A tiny API method "rf" also exists.
- * @method beginRadialGradientFill
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
- * a gradient drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
- * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {Number} x0 Center position of the inner circle that defines the gradient.
- * @param {Number} y0 Center position of the inner circle that defines the gradient.
- * @param {Number} r0 Radius of the inner circle that defines the gradient.
- * @param {Number} x1 Center position of the outer circle that defines the gradient.
- * @param {Number} y1 Center position of the outer circle that defines the gradient.
- * @param {Number} r1 Radius of the outer circle that defines the gradient.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
- return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
- };
-
- /**
- * Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also
- * exists.
- * @method beginBitmapFill
- * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
- * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
- * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
- * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
- * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
- * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
- * will be applied relative to the parent transform.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginBitmapFill = function(image, repetition, matrix) {
- return this._setFill(new G.Fill(null,matrix).bitmap(image, repetition));
- };
-
- /**
- * Ends the current sub-path, and begins a new one with no fill. Functionally identical to beginFill(null)
.
- * A tiny API method "ef" also exists.
- * @method endFill
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.endFill = function() {
- return this.beginFill();
- };
-
- /**
- * Sets the stroke style. Like all drawing methods, this can be chained, so you can define
- * the stroke style and color in a single line of code like so:
- *
- * myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");
- *
- * A tiny API method "ss" also exists.
- * @method setStrokeStyle
- * @param {Number} thickness The width of the stroke.
- * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
- * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
- * the tiny API.
- * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
- * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
- * for use with the tiny API.
- * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
- * controls at what point a mitered joint will be clipped.
- * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
- * of active transformations.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) {
- this._updateInstructions(true);
- this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale);
-
- // ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery:
- if (this._stroke) { this._stroke.ignoreScale = ignoreScale; }
- this._strokeIgnoreScale = ignoreScale;
- return this;
- };
-
- /**
- * Sets or clears the stroke dash pattern.
- *
- * myGraphics.setStrokeDash([20, 10], 0);
- *
- * A tiny API method `sd` also exists.
- * @method setStrokeDash
- * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
- * For example, `[20,10]` would create a pattern of 20 pixel lines with 10 pixel gaps between them.
- * Passing null or an empty array will clear the existing stroke dash.
- * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.setStrokeDash = function(segments, offset) {
- this._updateInstructions(true);
- this._strokeDash = this.command = new G.StrokeDash(segments, offset);
- return this;
- };
-
- /**
- * Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists.
- * @method beginStroke
- * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
- * null will result in no stroke.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginStroke = function(color) {
- return this._setStroke(color ? new G.Stroke(color) : null);
- };
-
- /**
- * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
- * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
- * square to display it:
- *
- * myGraphics.setStrokeStyle(10).
- * beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
- *
- * A tiny API method "ls" also exists.
- * @method beginLinearGradientStroke
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
- * a gradient drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
- * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
- * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) {
- return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1));
- };
-
- /**
- * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to
- * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it:
- *
- * myGraphics.setStrokeStyle(10)
- * .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50)
- * .drawRect(50, 90, 150, 110);
- *
- * A tiny API method "rs" also exists.
- * @method beginRadialGradientStroke
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
- * a gradient drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
- * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
- * to 100%.
- * @param {Number} x0 Center position of the inner circle that defines the gradient.
- * @param {Number} y0 Center position of the inner circle that defines the gradient.
- * @param {Number} r0 Radius of the inner circle that defines the gradient.
- * @param {Number} x1 Center position of the outer circle that defines the gradient.
- * @param {Number} y1 Center position of the outer circle that defines the gradient.
- * @param {Number} r1 Radius of the outer circle that defines the gradient.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
- return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
- };
-
- /**
- * Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills,
- * strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs"
- * also exists.
- * @method beginBitmapStroke
- * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
- * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
- * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
- * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.beginBitmapStroke = function(image, repetition) {
- // NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width.
- return this._setStroke(new G.Stroke().bitmap(image, repetition));
- };
-
- /**
- * Ends the current sub-path, and begins a new one with no stroke. Functionally identical to beginStroke(null)
.
- * A tiny API method "es" also exists.
- * @method endStroke
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.endStroke = function() {
- return this.beginStroke();
- };
-
- /**
- * Maps the familiar ActionScript curveTo()
method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}}
- * method.
- * @method curveTo
- * @param {Number} cpx
- * @param {Number} cpy
- * @param {Number} x
- * @param {Number} y
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.curveTo = p.quadraticCurveTo;
-
- /**
- *
- * Maps the familiar ActionScript drawRect()
method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}}
- * method.
- * @method drawRect
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w Width of the rectangle
- * @param {Number} h Height of the rectangle
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.drawRect = p.rect;
-
- /**
- * Draws a rounded rectangle with all corners with the specified radius.
- * @method drawRoundRect
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w
- * @param {Number} h
- * @param {Number} radius Corner radius.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.drawRoundRect = function(x, y, w, h, radius) {
- return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius);
- };
-
- /**
- * Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API
- * method "rc" also exists.
- * @method drawRoundRectComplex
- * @param {Number} x The horizontal coordinate to draw the round rect.
- * @param {Number} y The vertical coordinate to draw the round rect.
- * @param {Number} w The width of the round rect.
- * @param {Number} h The height of the round rect.
- * @param {Number} radiusTL Top left corner radius.
- * @param {Number} radiusTR Top right corner radius.
- * @param {Number} radiusBR Bottom right corner radius.
- * @param {Number} radiusBL Bottom left corner radius.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
- return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL));
- };
-
- /**
- * Draws a circle with the specified radius at (x, y).
- *
- * var g = new createjs.Graphics();
- * g.setStrokeStyle(1);
- * g.beginStroke(createjs.Graphics.getRGB(0,0,0));
- * g.beginFill(createjs.Graphics.getRGB(255,0,0));
- * g.drawCircle(0,0,3);
- *
- * var s = new createjs.Shape(g);
- * s.x = 100;
- * s.y = 100;
- *
- * stage.addChild(s);
- * stage.update();
- *
- * A tiny API method "dc" also exists.
- * @method drawCircle
- * @param {Number} x x coordinate center point of circle.
- * @param {Number} y y coordinate center point of circle.
- * @param {Number} radius Radius of circle.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.drawCircle = function(x, y, radius) {
- return this.append(new G.Circle(x, y, radius));
- };
-
- /**
- * Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}},
- * except the width and height can be different. A tiny API method "de" also exists.
- * @method drawEllipse
- * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
- * which draws from center.
- * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
- * which draws from the center.
- * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
- * number.
- * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.drawEllipse = function(x, y, w, h) {
- return this.append(new G.Ellipse(x, y, w, h));
- };
-
- /**
- * Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of
- * points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a
- * radius of 50:
- *
- * myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90);
- * // Note: -90 makes the first point vertical
- *
- * A tiny API method "dp" also exists.
- *
- * @method drawPolyStar
- * @param {Number} x Position of the center of the shape.
- * @param {Number} y Position of the center of the shape.
- * @param {Number} radius The outer radius of the shape.
- * @param {Number} sides The number of points on the star or sides on the polygon.
- * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
- * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
- * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
- * directly to the right of the center.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) {
- return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle));
- };
-
- /**
- * Appends a graphics command object to the graphics queue. Command objects expose an "exec" method
- * that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into
- * {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw.
- *
- * This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert
- * built-in or custom graphics commands. For example:
- *
- * // attach data to our shape, so we can access it during the draw:
- * myShape.color = "red";
- *
- * // append a Circle command object:
- * myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30));
- *
- * // append a custom command object with an exec method that sets the fill style
- * // based on the shape's data, and then fills the circle.
- * myShape.graphics.append({exec:function(ctx, shape) {
- * ctx.fillStyle = shape.color;
- * ctx.fill();
- * }});
- *
- * @method append
- * @param {Object} command A graphics command object exposing an "exec" method.
- * @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.append = function(command, clean) {
- this._activeInstructions.push(command);
- this.command = command;
- if (!clean) { this._dirty = true; }
- return this;
- };
-
- /**
- * Decodes a compact encoded path string into a series of draw instructions.
- * This format is not intended to be human readable, and is meant for use by authoring tools.
- * The format uses a base64 character set, with each character representing 6 bits, to define a series of draw
- * commands.
- *
- * Each command is comprised of a single "header" character followed by a variable number of alternating x and y
- * position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the
- * type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4
- * indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the
- * latter. Bits 5 and 6 are currently unused.
- *
- * Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo)
- * parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the
- * 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed
- * by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the
- * case of move operations which are absolute, this value is a delta from the previous x or y position (as
- * appropriate).
- *
- * For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0.
- *
A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per
- * parameter.
- *
n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits
- * indicate 1500 tenths of a pixel.
- *
AA - 000000000000. Absolute y position of 0.
- *
I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter.
- *
Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to
- * provide an absolute position of +150.0px.
- *
AAA - 000000000000000000. A y delta value of 0.
- *
- * A tiny API method "p" also exists.
- * @method decodePath
- * @param {String} str The path string to decode.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.decodePath = function(str) {
- var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath];
- var paramCount = [2, 2, 4, 6, 0];
- var i=0, l=str.length;
- var params = [];
- var x=0, y=0;
- var base64 = Graphics.BASE_64;
-
- while (i>3; // highest order bits 1-3 code for operation.
- var f = instructions[fi];
- // check that we have a valid instruction & that the unused bits are empty:
- if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); }
- var pl = paramCount[fi];
- if (!fi) { x=y=0; } // move operations reset the position.
- params.length = 0;
- i++;
- var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation.
- for (var p=0; p>5) ? -1 : 1;
- num = ((num&31)<<6)|(base64[str.charAt(i+1)]);
- if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); }
- num = sign*num/10;
- if (p%2) { x = (num += x); }
- else { y = (num += y); }
- params[p] = num;
- i += charCount;
- }
- f.apply(this,params);
- }
- return this;
- };
-
- /**
- * Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to
- * the existing store. This also affects `drawAsPath()`.
- *
- * This is useful in cases where you are creating vector graphics in an iterative manner (ex. generative art), so
- * that only new graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all
- * of the vector instructions for later use (ex. scaling, modifying, or exporting).
- *
- * Note that calling store() will force the active path (if any) to be ended in a manner similar to changing
- * the fill or stroke.
- *
- * For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of
- * segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}},
- * and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG.
- *
- * // set up cache:
- * myShape.cache(0,0,500,500,scale);
- *
- * // when the user drags, draw a new line:
- * myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY);
- * // then draw it into the existing cache:
- * myShape.updateCache("source-over");
- * // store the new line, so it isn't redrawn next time:
- * myShape.store();
- *
- * // then, when the window resizes, we can re-render at a different scale:
- * // first, unstore all our lines:
- * myShape.unstore();
- * // then cache using the new scale:
- * myShape.cache(0,0,500,500,newScale);
- * // finally, store the existing commands again:
- * myShape.store();
- *
- * @method store
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.store = function() {
- this._updateInstructions(true);
- this._storeIndex = this._instructions.length;
- return this;
- };
-
- /**
- * Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}}
- * so that they will be executed in subsequent draw calls.
- *
- * @method unstore
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.unstore = function() {
- this._storeIndex = 0;
- return this;
- };
-
- /**
- * Returns a clone of this Graphics instance. Note that the individual command objects are not cloned.
- * @method clone
- * @return {Graphics} A clone of the current Graphics instance.
- **/
- p.clone = function() {
- var o = new Graphics();
- o.command = this.command;
- o._stroke = this._stroke;
- o._strokeStyle = this._strokeStyle;
- o._strokeDash = this._strokeDash;
- o._strokeIgnoreScale = this._strokeIgnoreScale;
- o._fill = this._fill;
- o._instructions = this._instructions.slice();
- o._commitIndex = this._commitIndex;
- o._activeInstructions = this._activeInstructions.slice();
- o._dirty = this._dirty;
- o._storeIndex = this._storeIndex;
- return o;
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Graphics]";
- };
-
-
-// tiny API:
- /**
- * Shortcut to moveTo.
- * @method mt
- * @param {Number} x The x coordinate the drawing point should move to.
- * @param {Number} y The y coordinate the drawing point should move to.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
- * @chainable
- * @protected
- **/
- p.mt = p.moveTo;
-
- /**
- * Shortcut to lineTo.
- * @method lt
- * @param {Number} x The x coordinate the drawing point should draw to.
- * @param {Number} y The y coordinate the drawing point should draw to.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.lt = p.lineTo;
-
- /**
- * Shortcut to arcTo.
- * @method at
- * @param {Number} x1
- * @param {Number} y1
- * @param {Number} x2
- * @param {Number} y2
- * @param {Number} radius
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.at = p.arcTo;
-
- /**
- * Shortcut to bezierCurveTo.
- * @method bt
- * @param {Number} cp1x
- * @param {Number} cp1y
- * @param {Number} cp2x
- * @param {Number} cp2y
- * @param {Number} x
- * @param {Number} y
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.bt = p.bezierCurveTo;
-
- /**
- * Shortcut to quadraticCurveTo / curveTo.
- * @method qt
- * @param {Number} cpx
- * @param {Number} cpy
- * @param {Number} x
- * @param {Number} y
- * @protected
- * @chainable
- **/
- p.qt = p.quadraticCurveTo;
-
- /**
- * Shortcut to arc.
- * @method a
- * @param {Number} x
- * @param {Number} y
- * @param {Number} radius
- * @param {Number} startAngle Measured in radians.
- * @param {Number} endAngle Measured in radians.
- * @param {Boolean} anticlockwise
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @protected
- * @chainable
- **/
- p.a = p.arc;
-
- /**
- * Shortcut to rect.
- * @method r
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w Width of the rectangle
- * @param {Number} h Height of the rectangle
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.r = p.rect;
-
- /**
- * Shortcut to closePath.
- * @method cp
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.cp = p.closePath;
-
- /**
- * Shortcut to clear.
- * @method c
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.c = p.clear;
-
- /**
- * Shortcut to beginFill.
- * @method f
- * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
- * null will result in no fill.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.f = p.beginFill;
-
- /**
- * Shortcut to beginLinearGradientFill.
- * @method lf
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
- * drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
- * the first color to 10% then interpolating to the second color at 90%.
- * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
- * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.lf = p.beginLinearGradientFill;
-
- /**
- * Shortcut to beginRadialGradientFill.
- * @method rf
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
- * a gradient drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
- * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {Number} x0 Center position of the inner circle that defines the gradient.
- * @param {Number} y0 Center position of the inner circle that defines the gradient.
- * @param {Number} r0 Radius of the inner circle that defines the gradient.
- * @param {Number} x1 Center position of the outer circle that defines the gradient.
- * @param {Number} y1 Center position of the outer circle that defines the gradient.
- * @param {Number} r1 Radius of the outer circle that defines the gradient.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.rf = p.beginRadialGradientFill;
-
- /**
- * Shortcut to beginBitmapFill.
- * @method bf
- * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
- * as the pattern.
- * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
- * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
- * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
- * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
- * will be applied relative to the parent transform.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.bf = p.beginBitmapFill;
-
- /**
- * Shortcut to endFill.
- * @method ef
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.ef = p.endFill;
-
- /**
- * Shortcut to setStrokeStyle.
- * @method ss
- * @param {Number} thickness The width of the stroke.
- * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
- * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
- * the tiny API.
- * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
- * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
- * for use with the tiny API.
- * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
- * controls at what point a mitered joint will be clipped.
- * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
- * of active transformations.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.ss = p.setStrokeStyle;
-
- /**
- * Shortcut to setStrokeDash.
- * @method sd
- * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
- * For example, [20,10] would create a pattern of 20 pixel lines with 10 pixel gaps between them.
- * Passing null or an empty array will clear any existing dash.
- * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.sd = p.setStrokeDash;
-
- /**
- * Shortcut to beginStroke.
- * @method s
- * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
- * null will result in no stroke.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.s = p.beginStroke;
-
- /**
- * Shortcut to beginLinearGradientStroke.
- * @method ls
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
- * a gradient drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
- * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
- * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
- * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
- * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.ls = p.beginLinearGradientStroke;
-
- /**
- * Shortcut to beginRadialGradientStroke.
- * @method rs
- * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
- * a gradient drawing from red to blue.
- * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
- * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
- * to 100%.
- * @param {Number} x0 Center position of the inner circle that defines the gradient.
- * @param {Number} y0 Center position of the inner circle that defines the gradient.
- * @param {Number} r0 Radius of the inner circle that defines the gradient.
- * @param {Number} x1 Center position of the outer circle that defines the gradient.
- * @param {Number} y1 Center position of the outer circle that defines the gradient.
- * @param {Number} r1 Radius of the outer circle that defines the gradient.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.rs = p.beginRadialGradientStroke;
-
- /**
- * Shortcut to beginBitmapStroke.
- * @method bs
- * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
- * as the pattern.
- * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
- * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.bs = p.beginBitmapStroke;
-
- /**
- * Shortcut to endStroke.
- * @method es
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.es = p.endStroke;
-
- /**
- * Shortcut to drawRect.
- * @method dr
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w Width of the rectangle
- * @param {Number} h Height of the rectangle
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.dr = p.drawRect;
-
- /**
- * Shortcut to drawRoundRect.
- * @method rr
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w
- * @param {Number} h
- * @param {Number} radius Corner radius.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.rr = p.drawRoundRect;
-
- /**
- * Shortcut to drawRoundRectComplex.
- * @method rc
- * @param {Number} x The horizontal coordinate to draw the round rect.
- * @param {Number} y The vertical coordinate to draw the round rect.
- * @param {Number} w The width of the round rect.
- * @param {Number} h The height of the round rect.
- * @param {Number} radiusTL Top left corner radius.
- * @param {Number} radiusTR Top right corner radius.
- * @param {Number} radiusBR Bottom right corner radius.
- * @param {Number} radiusBL Bottom left corner radius.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.rc = p.drawRoundRectComplex;
-
- /**
- * Shortcut to drawCircle.
- * @method dc
- * @param {Number} x x coordinate center point of circle.
- * @param {Number} y y coordinate center point of circle.
- * @param {Number} radius Radius of circle.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.dc = p.drawCircle;
-
- /**
- * Shortcut to drawEllipse.
- * @method de
- * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
- * which draws from center.
- * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
- * which draws from the center.
- * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
- * number.
- * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.de = p.drawEllipse;
-
- /**
- * Shortcut to drawPolyStar.
- * @method dp
- * @param {Number} x Position of the center of the shape.
- * @param {Number} y Position of the center of the shape.
- * @param {Number} radius The outer radius of the shape.
- * @param {Number} sides The number of points on the star or sides on the polygon.
- * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
- * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
- * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
- * directly to the right of the center.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.dp = p.drawPolyStar;
-
- /**
- * Shortcut to decodePath.
- * @method p
- * @param {String} str The path string to decode.
- * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
- * @chainable
- * @protected
- **/
- p.p = p.decodePath;
-
-
-// private methods:
- /**
- * @method _updateInstructions
- * @param commit
- * @protected
- **/
- p._updateInstructions = function(commit) {
- var instr = this._instructions, active = this._activeInstructions, commitIndex = this._commitIndex;
-
- if (this._dirty && active.length) {
- instr.length = commitIndex; // remove old, uncommitted commands
- instr.push(Graphics.beginCmd);
-
- var l = active.length, ll = instr.length;
- instr.length = ll+l;
- for (var i=0; i= 2) {
- var o = this.style = Graphics._ctx.createPattern(image, repetition || "");
- o.props = {image: image, repetition: repetition, type: "bitmap"};
- }
- return this;
- };
- p.path = false;
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class Stroke
- * @constructor
- * @param {Object} style A valid Context2D fillStyle.
- * @param {Boolean} ignoreScale
- **/
- /**
- * A valid Context2D strokeStyle.
- * @property style
- * @type Object
- */
- /**
- * @property ignoreScale
- * @type Boolean
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- p = (G.Stroke = function(style, ignoreScale) {
- this.style = style;
- this.ignoreScale = ignoreScale;
- }).prototype;
- p.exec = function(ctx) {
- if (!this.style) { return; }
- ctx.strokeStyle = this.style;
- if (this.ignoreScale) { ctx.save(); ctx.setTransform(1,0,0,1,0,0); }
- ctx.stroke();
- if (this.ignoreScale) { ctx.restore(); }
- };
- /**
- * Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
- * See {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} for more information.
- * @method linearGradient
- * @param {Array} colors
- * @param {Array} ratios
- * @param {Number} x0
- * @param {Number} y0
- * @param {Number} x1
- * @param {Number} y1
- * @return {Fill} Returns this Stroke object for chaining or assignment.
- */
- p.linearGradient = G.Fill.prototype.linearGradient;
- /**
- * Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
- * See {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} for more information.
- * @method radialGradient
- * @param {Array} colors
- * @param {Array} ratios
- * @param {Number} x0
- * @param {Number} y0
- * @param {Number} r0
- * @param {Number} x1
- * @param {Number} y1
- * @param {Number} r1
- * @return {Fill} Returns this Stroke object for chaining or assignment.
- */
- p.radialGradient = G.Fill.prototype.radialGradient;
- /**
- * Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
- * See {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} for more information.
- * @method bitmap
- * @param {HTMLImageElement} image
- * @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
- * @return {Fill} Returns this Stroke object for chaining or assignment.
- */
- p.bitmap = G.Fill.prototype.bitmap;
- p.path = false;
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class StrokeStyle
- * @constructor
- * @param {Number} width
- * @param {String} [caps=butt]
- * @param {String} [joints=miter]
- * @param {Number} [miterLimit=10]
- * @param {Boolean} [ignoreScale=false]
- **/
- /**
- * @property width
- * @type Number
- */
- /**
- * One of: butt, round, square
- * @property caps
- * @type String
- */
- /**
- * One of: round, bevel, miter
- * @property joints
- * @type String
- */
- /**
- * @property miterLimit
- * @type Number
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- p = (G.StrokeStyle = function(width, caps, joints, miterLimit, ignoreScale) {
- this.width = width;
- this.caps = caps;
- this.joints = joints;
- this.miterLimit = miterLimit;
- this.ignoreScale = ignoreScale;
- }).prototype;
- p.exec = function(ctx) {
- ctx.lineWidth = (this.width == null ? "1" : this.width);
- ctx.lineCap = (this.caps == null ? "butt" : (isNaN(this.caps) ? this.caps : Graphics.STROKE_CAPS_MAP[this.caps]));
- ctx.lineJoin = (this.joints == null ? "miter" : (isNaN(this.joints) ? this.joints : Graphics.STROKE_JOINTS_MAP[this.joints]));
- ctx.miterLimit = (this.miterLimit == null ? "10" : this.miterLimit);
- ctx.ignoreScale = (this.ignoreScale == null ? false : this.ignoreScale);
- };
- p.path = false;
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class StrokeDash
- * @constructor
- * @param {Array} [segments]
- * @param {Number} [offset=0]
- **/
- /**
- * @property segments
- * @type Array
- */
- /**
- * @property offset
- * @type Number
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- (G.StrokeDash = function(segments, offset) {
- this.segments = segments;
- this.offset = offset||0;
- }).prototype.exec = function(ctx) {
- if (ctx.setLineDash) { // feature detection.
- ctx.setLineDash(this.segments|| G.StrokeDash.EMPTY_SEGMENTS); // instead of [] to reduce churn.
- ctx.lineDashOffset = this.offset||0;
- }
- };
- /**
- * The default value for segments (ie. no dash).
- * @property EMPTY_SEGMENTS
- * @static
- * @final
- * @readonly
- * @protected
- * @type {Array}
- **/
- G.StrokeDash.EMPTY_SEGMENTS = [];
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class RoundRect
- * @constructor
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w
- * @param {Number} h
- * @param {Number} radiusTL
- * @param {Number} radiusTR
- * @param {Number} radiusBR
- * @param {Number} radiusBL
- **/
- /**
- * @property x
- * @type Number
- */
- /**
- * @property y
- * @type Number
- */
- /**
- * @property w
- * @type Number
- */
- /**
- * @property h
- * @type Number
- */
- /**
- * @property radiusTL
- * @type Number
- */
- /**
- * @property radiusTR
- * @type Number
- */
- /**
- * @property radiusBR
- * @type Number
- */
- /**
- * @property radiusBL
- * @type Number
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- (G.RoundRect = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
- this.x = x; this.y = y;
- this.w = w; this.h = h;
- this.radiusTL = radiusTL; this.radiusTR = radiusTR;
- this.radiusBR = radiusBR; this.radiusBL = radiusBL;
- }).prototype.exec = function(ctx) {
- var max = (w max) { rTL = max; }
- if (rTR < 0) { rTR *= (mTR=-1); }
- if (rTR > max) { rTR = max; }
- if (rBR < 0) { rBR *= (mBR=-1); }
- if (rBR > max) { rBR = max; }
- if (rBL < 0) { rBL *= (mBL=-1); }
- if (rBL > max) { rBL = max; }
-
- ctx.moveTo(x+w-rTR, y);
- ctx.arcTo(x+w+rTR*mTR, y-rTR*mTR, x+w, y+rTR, rTR);
- ctx.lineTo(x+w, y+h-rBR);
- ctx.arcTo(x+w+rBR*mBR, y+h+rBR*mBR, x+w-rBR, y+h, rBR);
- ctx.lineTo(x+rBL, y+h);
- ctx.arcTo(x-rBL*mBL, y+h+rBL*mBL, x, y+h-rBL, rBL);
- ctx.lineTo(x, y+rTL);
- ctx.arcTo(x-rTL*mTL, y-rTL*mTL, x+rTL, y, rTL);
- ctx.closePath();
- };
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class Circle
- * @constructor
- * @param {Number} x
- * @param {Number} y
- * @param {Number} radius
- **/
- /**
- * @property x
- * @type Number
- */
- /**
- * @property y
- * @type Number
- */
- /**
- * @property radius
- * @type Number
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- (G.Circle = function(x, y, radius) {
- this.x = x; this.y = y;
- this.radius = radius;
- }).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); };
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class Ellipse
- * @constructor
- * @param {Number} x
- * @param {Number} y
- * @param {Number} w
- * @param {Number} h
- **/
- /**
- * @property x
- * @type Number
- */
- /**
- * @property y
- * @type Number
- */
- /**
- * @property w
- * @type Number
- */
- /**
- * @property h
- * @type Number
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- (G.Ellipse = function(x, y, w, h) {
- this.x = x; this.y = y;
- this.w = w; this.h = h;
- }).prototype.exec = function(ctx) {
- var x = this.x, y = this.y;
- var w = this.w, h = this.h;
-
- var k = 0.5522848;
- var ox = (w / 2) * k;
- var oy = (h / 2) * k;
- var xe = x + w;
- var ye = y + h;
- var xm = x + w / 2;
- var ym = y + h / 2;
-
- ctx.moveTo(x, ym);
- ctx.bezierCurveTo(x, ym-oy, xm-ox, y, xm, y);
- ctx.bezierCurveTo(xm+ox, y, xe, ym-oy, xe, ym);
- ctx.bezierCurveTo(xe, ym+oy, xm+ox, ye, xm, ye);
- ctx.bezierCurveTo(xm-ox, ye, x, ym+oy, x, ym);
- };
-
- /**
- * Graphics command object. See {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
- * @class PolyStar
- * @constructor
- * @param {Number} x
- * @param {Number} y
- * @param {Number} radius
- * @param {Number} sides
- * @param {Number} pointSize
- * @param {Number} angle
- **/
- /**
- * @property x
- * @type Number
- */
- /**
- * @property y
- * @type Number
- */
- /**
- * @property radius
- * @type Number
- */
- /**
- * @property sides
- * @type Number
- */
- /**
- * @property pointSize
- * @type Number
- */
- /**
- * @property angle
- * @type Number
- */
- /**
- * Execute the Graphics command in the provided Canvas context.
- * @method exec
- * @param {CanvasRenderingContext2D} ctx The canvas rendering context
- */
- (G.PolyStar = function(x, y, radius, sides, pointSize, angle) {
- this.x = x; this.y = y;
- this.radius = radius;
- this.sides = sides;
- this.pointSize = pointSize;
- this.angle = angle;
- }).prototype.exec = function(ctx) {
- var x = this.x, y = this.y;
- var radius = this.radius;
- var angle = (this.angle||0)/180*Math.PI;
- var sides = this.sides;
- var ps = 1-(this.pointSize||0);
- var a = Math.PI/sides;
-
- ctx.moveTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius);
- for (var i=0; iExample
+ *
+ * createjs.Ticker.addEventListener("tick", handleTick);
+ * function handleTick(event) {
+ * // Actions carried out each tick (aka frame)
+ * if (!event.paused) {
+ * // Actions carried out when the Ticker is not paused.
+ * }
+ * }
+ *
+ * @class Ticker
+ * @uses EventDispatcher
+ * @static
**/
- function DisplayObject() {
- this.EventDispatcher_constructor();
+ function Ticker() {
+ throw "Ticker cannot be instantiated.";
+ }
- // public properties:
- /**
- * The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque.
- * @property alpha
- * @type {Number}
- * @default 1
- **/
- this.alpha = 1;
+// constants:
+ /**
+ * In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It
+ * uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and
+ * dispatches the tick when the time is within a certain threshold.
+ *
+ * This mode has a higher variance for time between frames than {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}},
+ * but does not require that content be time based as with {{#crossLink "Ticker/RAF:property"}}{{/crossLink}} while
+ * gaining the benefits of that API (screen synch, background throttling).
+ *
+ * Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so
+ * framerates of 10, 12, 15, 20, and 30 work well.
+ *
+ * Falls back to {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
+ * supported.
+ * @property RAF_SYNCHED
+ * @static
+ * @type {String}
+ * @default "synched"
+ * @readonly
+ **/
+ Ticker.RAF_SYNCHED = "synched";
- /**
- * If a cache is active, this returns the canvas that holds the image of this display object. See {{#crossLink "DisplayObject/cache:method"}}{{/crossLink}}
- * for more information. Use this to display the result of a cache. This will be a HTMLCanvasElement unless special cache rules have been deliberately enabled for this cache.
- * @property cacheCanvas
- * @type {HTMLCanvasElement | Object}
- * @default null
- * @readonly
- **/
- this.cacheCanvas = null;
+ /**
+ * In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely.
+ * Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based.
+ * You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
+ * event object's "delta" properties to make this easier.
+ *
+ * Falls back on {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
+ * supported.
+ * @property RAF
+ * @static
+ * @type {String}
+ * @default "raf"
+ * @readonly
+ **/
+ Ticker.RAF = "raf";
- /**
- * If a cache has been made, this returns the class that is managing the cacheCanvas and its properties. See {{#crossLink "BitmapCache"}}{{/crossLink}}
- * for more information. Use this to control, inspect, and change the cache. In special circumstances this may be a modified or subclassed BitmapCache.
- * @property bitmapCache
- * @type {BitmapCache}
- * @default null
- * @readonly
- **/
- this.bitmapCache = null;
+ /**
+ * In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not
+ * provide the benefits of requestAnimationFrame (screen synch, background throttling).
+ * @property TIMEOUT
+ * @static
+ * @type {String}
+ * @default "timeout"
+ * @readonly
+ **/
+ Ticker.TIMEOUT = "timeout";
- /**
- * Unique ID for this display object. Makes display objects easier for some uses.
- * @property id
+
+// static events:
+ /**
+ * Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using
+ * {{#crossLink "Ticker/paused:property"}}{{/crossLink}}.
+ *
+ * Example
+ *
+ * createjs.Ticker.addEventListener("tick", handleTick);
+ * function handleTick(event) {
+ * console.log("Paused:", event.paused, event.delta);
+ * }
+ *
+ * @event tick
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @param {Boolean} paused Indicates whether the ticker is currently paused.
+ * @param {Number} delta The time elapsed in ms since the last tick.
+ * @param {Number} time The total time in ms since Ticker was initialized.
+ * @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example,
+ * you could determine the amount of time that the Ticker has been paused since initialization with `time-runTime`.
+ * @since 0.6.0
+ */
+
+
+// public static properties:
+ /**
+ * Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See
+ * {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}}, {{#crossLink "Ticker/RAF:property"}}{{/crossLink}}, and
+ * {{#crossLink "Ticker/RAF_SYNCHED:property"}}{{/crossLink}} for mode details.
+ * @property timingMode
+ * @static
+ * @type {String}
+ * @default Ticker.TIMEOUT
+ **/
+ Ticker.timingMode = null;
+
+ /**
+ * Specifies a maximum value for the delta property in the tick event object. This is useful when building time
+ * based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep,
+ * alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value
+ * (ex. maxDelta=50 when running at 40fps).
+ *
+ * This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta
+ * when using both delta and other values.
+ *
+ * If 0, there is no maximum.
+ * @property maxDelta
+ * @static
+ * @type {number}
+ * @default 0
+ */
+ Ticker.maxDelta = 0;
+
+ /**
+ * When the ticker is paused, all listeners will still receive a tick event, but the paused
property
+ * of the event will be `true`. Also, while paused the `runTime` will not increase. See {{#crossLink "Ticker/tick:event"}}{{/crossLink}},
+ * {{#crossLink "Ticker/getTime"}}{{/crossLink}}, and {{#crossLink "Ticker/getEventTime"}}{{/crossLink}} for more
+ * info.
+ *
+ * Example
+ *
+ * createjs.Ticker.addEventListener("tick", handleTick);
+ * createjs.Ticker.paused = true;
+ * function handleTick(event) {
+ * console.log(event.paused,
+ * createjs.Ticker.getTime(false),
+ * createjs.Ticker.getTime(true));
+ * }
+ *
+ * @property paused
+ * @static
+ * @type {Boolean}
+ * @default false
+ **/
+ Ticker.paused = false;
+
+
+// mix-ins:
+ // EventDispatcher methods:
+ Ticker.removeEventListener = null;
+ Ticker.removeAllEventListeners = null;
+ Ticker.dispatchEvent = null;
+ Ticker.hasEventListener = null;
+ Ticker._listeners = null;
+ createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods.
+ Ticker._addEventListener = Ticker.addEventListener;
+ Ticker.addEventListener = function() {
+ !Ticker._inited&&Ticker.init();
+ return Ticker._addEventListener.apply(Ticker, arguments);
+ };
+
+
+// private static properties:
+ /**
+ * @property _inited
+ * @static
+ * @type {Boolean}
+ * @private
+ **/
+ Ticker._inited = false;
+
+ /**
+ * @property _startTime
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._startTime = 0;
+
+ /**
+ * @property _pausedTime
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._pausedTime=0;
+
+ /**
+ * The number of ticks that have passed
+ * @property _ticks
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._ticks = 0;
+
+ /**
+ * The number of ticks that have passed while Ticker has been paused
+ * @property _pausedTicks
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._pausedTicks = 0;
+
+ /**
+ * @property _interval
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._interval = 50;
+
+ /**
+ * @property _lastTime
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._lastTime = 0;
+
+ /**
+ * @property _times
+ * @static
+ * @type {Array}
+ * @private
+ **/
+ Ticker._times = null;
+
+ /**
+ * @property _tickTimes
+ * @static
+ * @type {Array}
+ * @private
+ **/
+ Ticker._tickTimes = null;
+
+ /**
+ * Stores the timeout or requestAnimationFrame id.
+ * @property _timerId
+ * @static
+ * @type {Number}
+ * @private
+ **/
+ Ticker._timerId = null;
+
+ /**
+ * True if currently using requestAnimationFrame, false if using setTimeout. This may be different than timingMode
+ * if that property changed and a tick hasn't fired.
+ * @property _raf
+ * @static
+ * @type {Boolean}
+ * @private
+ **/
+ Ticker._raf = true;
+
+
+// static getter / setters:
+ /**
+ * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
+ * @method _setInterval
+ * @private
+ * @static
+ * @param {Number} interval
+ **/
+ Ticker._setInterval = function(interval) {
+ Ticker._interval = interval;
+ if (!Ticker._inited) { return; }
+ Ticker._setupTick();
+ };
+ // Ticker.setInterval is @deprecated. Remove for 1.1+
+ Ticker.setInterval = createjs.deprecate(Ticker._setInterval, "Ticker.setInterval");
+
+ /**
+ * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
+ * @method _getInterval
+ * @private
+ * @static
+ * @return {Number}
+ **/
+ Ticker._getInterval = function() {
+ return Ticker._interval;
+ };
+ // Ticker.getInterval is @deprecated. Remove for 1.1+
+ Ticker.getInterval = createjs.deprecate(Ticker._getInterval, "Ticker.getInterval");
+
+ /**
+ * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
+ * @method _setFPS
+ * @private
+ * @static
+ * @param {Number} value
+ **/
+ Ticker._setFPS = function(value) {
+ Ticker._setInterval(1000/value);
+ };
+ // Ticker.setFPS is @deprecated. Remove for 1.1+
+ Ticker.setFPS = createjs.deprecate(Ticker._setFPS, "Ticker.setFPS");
+
+ /**
+ * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
+ * @method _getFPS
+ * @static
+ * @private
+ * @return {Number}
+ **/
+ Ticker._getFPS = function() {
+ return 1000/Ticker._interval;
+ };
+ // Ticker.getFPS is @deprecated. Remove for 1.1+
+ Ticker.getFPS = createjs.deprecate(Ticker._getFPS, "Ticker.getFPS");
+
+ /**
+ * Indicates the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
+ * Note that actual time between ticks may be more than specified depending on CPU load.
+ * This property is ignored if the ticker is using the `RAF` timing mode.
+ * @property interval
+ * @static
+ * @type {Number}
+ **/
+
+ /**
+ * Indicates the target frame rate in frames per second (FPS). Effectively just a shortcut to `interval`, where
+ * `framerate == 1000/interval`.
+ * @property framerate
+ * @static
+ * @type {Number}
+ **/
+ try {
+ Object.defineProperties(Ticker, {
+ interval: { get: Ticker._getInterval, set: Ticker._setInterval },
+ framerate: { get: Ticker._getFPS, set: Ticker._setFPS }
+ });
+ } catch (e) { console.log(e); }
+
+
+// public static methods:
+ /**
+ * Starts the tick. This is called automatically when the first listener is added.
+ * @method init
+ * @static
+ **/
+ Ticker.init = function() {
+ if (Ticker._inited) { return; }
+ Ticker._inited = true;
+ Ticker._times = [];
+ Ticker._tickTimes = [];
+ Ticker._startTime = Ticker._getTime();
+ Ticker._times.push(Ticker._lastTime = 0);
+ Ticker.interval = Ticker._interval;
+ };
+
+ /**
+ * Stops the Ticker and removes all listeners. Use init() to restart the Ticker.
+ * @method reset
+ * @static
+ **/
+ Ticker.reset = function() {
+ if (Ticker._raf) {
+ var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame;
+ f&&f(Ticker._timerId);
+ } else {
+ clearTimeout(Ticker._timerId);
+ }
+ Ticker.removeAllEventListeners("tick");
+ Ticker._timerId = Ticker._times = Ticker._tickTimes = null;
+ Ticker._startTime = Ticker._lastTime = Ticker._ticks = Ticker._pausedTime = 0;
+ Ticker._inited = false;
+ };
+
+ /**
+ * Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS
+ * because it only measures the time spent within the tick execution stack.
+ *
+ * Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between
+ * the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that
+ * there may be up to 35ms of "idle" time between the end of one tick and the start of the next.
+ *
+ * Example 2: With a target FPS of 30, {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} returns 10fps, which
+ * indicates an average of 100ms between the end of one tick and the end of the next. However, {{#crossLink "Ticker/getMeasuredTickTime"}}{{/crossLink}}
+ * returns 20ms. This would indicate that something other than the tick is using ~80ms (another script, DOM
+ * rendering, etc).
+ * @method getMeasuredTickTime
+ * @static
+ * @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick.
+ * Defaults to the number of ticks per second. To get only the last tick's time, pass in 1.
+ * @return {Number} The average time spent in a tick in milliseconds.
+ **/
+ Ticker.getMeasuredTickTime = function(ticks) {
+ var ttl=0, times=Ticker._tickTimes;
+ if (!times || times.length < 1) { return -1; }
+
+ // by default, calculate average for the past ~1 second:
+ ticks = Math.min(times.length, ticks||(Ticker._getFPS()|0));
+ for (var i=0; i= (Ticker._interval-1)*0.97) {
+ Ticker._tick();
+ }
+ };
+
+ /**
+ * @method _handleRAF
+ * @static
+ * @private
+ **/
+ Ticker._handleRAF = function() {
+ Ticker._timerId = null;
+ Ticker._setupTick();
+ Ticker._tick();
+ };
+
+ /**
+ * @method _handleTimeout
+ * @static
+ * @private
+ **/
+ Ticker._handleTimeout = function() {
+ Ticker._timerId = null;
+ Ticker._setupTick();
+ Ticker._tick();
+ };
+
+ /**
+ * @method _setupTick
+ * @static
+ * @private
+ **/
+ Ticker._setupTick = function() {
+ if (Ticker._timerId != null) { return; } // avoid duplicates
+
+ var mode = Ticker.timingMode;
+ if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) {
+ var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
+ if (f) {
+ Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch);
+ Ticker._raf = true;
+ return;
+ }
+ }
+ Ticker._raf = false;
+ Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval);
+ };
+
+ /**
+ * @method _tick
+ * @static
+ * @private
+ **/
+ Ticker._tick = function() {
+ var paused = Ticker.paused;
+ var time = Ticker._getTime();
+ var elapsedTime = time-Ticker._lastTime;
+ Ticker._lastTime = time;
+ Ticker._ticks++;
+
+ if (paused) {
+ Ticker._pausedTicks++;
+ Ticker._pausedTime += elapsedTime;
+ }
+
+ if (Ticker.hasEventListener("tick")) {
+ var event = new createjs.Event("tick");
+ var maxDelta = Ticker.maxDelta;
+ event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime;
+ event.paused = paused;
+ event.time = time;
+ event.runTime = time-Ticker._pausedTime;
+ Ticker.dispatchEvent(event);
+ }
+
+ Ticker._tickTimes.unshift(Ticker._getTime()-time);
+ while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); }
+
+ Ticker._times.unshift(time);
+ while (Ticker._times.length > 100) { Ticker._times.pop(); }
+ };
+
+ /**
+ * @method _getTime
+ * @static
+ * @private
+ **/
+ var w=window, now=w.performance.now || w.performance.mozNow || w.performance.msNow || w.performance.oNow || w.performance.webkitNow;
+ Ticker._getTime = function() {
+ return ((now&&now.call(w.performance))||(new Date().getTime())) - Ticker._startTime;
+ };
+
+
+ createjs.Ticker = Ticker;
+}());
+
+//##############################################################################
+// VideoBuffer.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * When an HTML video seeks, including when looping, there is an indeterminate period before a new frame is available.
+ * This can result in the video blinking or flashing when it is drawn to a canvas. The VideoBuffer class resolves
+ * this issue by drawing each frame to an off-screen canvas and preserving the prior frame during a seek.
+ *
+ * var myBuffer = new createjs.VideoBuffer(myVideo);
+ * var myBitmap = new Bitmap(myBuffer);
+ *
+ * @class VideoBuffer
+ * @param {HTMLVideoElement} video The HTML video element to buffer.
+ * @constructor
+ **/
+ function VideoBuffer(video) {
+
+ // private properties:
+ /**
+ * Used by Bitmap to determine when the video buffer is ready to be drawn. Not intended for general use.
+ * @property readyState
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this.readyState = video.readyState;
+
+ /**
+ * @property _video
+ * @protected
+ * @type {HTMLVideoElement}
+ * @default 0
+ **/
+ this._video = video;
+
+ /**
+ * @property _canvas
+ * @protected
+ * @type {HTMLCanvasElement}
+ * @default 0
+ **/
+ this._canvas = null;
+
+ /**
+ * @property _lastTime
+ * @protected
* @type {Number}
* @default -1
**/
- this.id = createjs.UID.get();
+ this._lastTime = -1;
+
+ if (this.readyState < 2) { video.addEventListener("canplaythrough", this._videoReady.bind(this)); } //once:true isn't supported everywhere, but its a non-critical optimization here.
+ }
+ var p = VideoBuffer.prototype;
+
+
+// public methods:
+ /**
+ * Gets an HTML canvas element showing the current video frame, or the previous frame if in a seek / loop.
+ * Primarily for use by {{#crossLink "Bitmap"}}{{/crossLink}}.
+ * @method getImage
+ **/
+ p.getImage = function() {
+ if (this.readyState < 2) { return; }
+ var canvas=this._canvas, video = this._video;
+ if (!canvas) {
+ canvas = this._canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
+ canvas.width = video.videoWidth;
+ canvas.height = video.videoHeight;
+ }
+ if (video.readyState >= 2 && video.currentTime !== this._lastTime) {
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0,0,canvas.width,canvas.height);
+ ctx.drawImage(video,0,0,canvas.width,canvas.height);
+ this._lastTime = video.currentTime;
+ }
+ return canvas;
+ };
+
+// private methods:
+ /**
+ * @method _videoReady
+ * @protected
+ **/
+ p._videoReady = function() {
+ this.readyState = 2;
+ };
+
+
+ createjs.VideoBuffer = VideoBuffer;
+}());
+
+//##############################################################################
+// MouseEvent.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties,
+ * see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings.
+ * @class MouseEvent
+ * @param {String} type The event type.
+ * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
+ * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
+ * @param {Number} stageX The normalized x position relative to the stage.
+ * @param {Number} stageY The normalized y position relative to the stage.
+ * @param {MouseEvent} nativeEvent The native DOM event related to this mouse event.
+ * @param {Number} pointerID The unique id for the pointer.
+ * @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment.
+ * @param {Number} rawX The raw x position relative to the stage.
+ * @param {Number} rawY The raw y position relative to the stage.
+ * @param {DisplayObject} relatedTarget The secondary target for the event.
+ * @extends Event
+ * @constructor
+ **/
+ function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY, relatedTarget) {
+ this.Event_constructor(type, bubbles, cancelable);
+
+
+ // public properties:
+ /**
+ * The normalized x position on the stage. This will always be within the range 0 to stage width.
+ * @property stageX
+ * @type Number
+ */
+ this.stageX = stageX;
+
+ /**
+ * The normalized y position on the stage. This will always be within the range 0 to stage height.
+ * @property stageY
+ * @type Number
+ **/
+ this.stageY = stageY;
+
+ /**
+ * The raw x position relative to the stage. Normally this will be the same as the stageX value, unless
+ * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
+ * @property rawX
+ * @type Number
+ */
+ this.rawX = (rawX==null)?stageX:rawX;
+
+ /**
+ * The raw y position relative to the stage. Normally this will be the same as the stageY value, unless
+ * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
+ * @property rawY
+ * @type Number
+ */
+ this.rawY = (rawY==null)?stageY:rawY;
+
+ /**
+ * The native MouseEvent generated by the browser. The properties and API for this
+ * event may differ between browsers. This property will be null if the
+ * EaselJS property was not directly generated from a native MouseEvent.
+ * @property nativeEvent
+ * @type HtmlMouseEvent
+ * @default null
+ **/
+ this.nativeEvent = nativeEvent;
+
+ /**
+ * The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system
+ * supplied id value.
+ * @property pointerID
+ * @type {Number}
+ */
+ this.pointerID = pointerID;
+
+ /**
+ * Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse.
+ * For touch pointers, the first pointer in the current stack will be considered the primary pointer.
+ * @property primary
+ * @type {Boolean}
+ */
+ this.primary = !!primary;
+
+ /**
+ * The secondary target for the event, if applicable. This is used for mouseout/rollout
+ * events to indicate the object that the mouse entered from, mouseover/rollover for the object the mouse exited,
+ * and stagemousedown/stagemouseup events for the object that was the under the cursor, if any.
+ *
+ * Only valid interaction targets will be returned (ie. objects with mouse listeners or a cursor set).
+ * @property relatedTarget
+ * @type {DisplayObject}
+ */
+ this.relatedTarget = relatedTarget;
+ }
+ var p = createjs.extend(MouseEvent, createjs.Event);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// getter / setters:
+ /**
+ * Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
+ * @property localX
+ * @type {Number}
+ * @readonly
+ */
+ p._get_localX = function() {
+ return this.currentTarget.globalToLocal(this.rawX, this.rawY).x;
+ };
+
+ /**
+ * Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
+ * @property localY
+ * @type {Number}
+ * @readonly
+ */
+ p._get_localY = function() {
+ return this.currentTarget.globalToLocal(this.rawX, this.rawY).y;
+ };
+
+ /**
+ * Indicates whether the event was generated by a touch input (versus a mouse input).
+ * @property isTouch
+ * @type {Boolean}
+ * @readonly
+ */
+ p._get_isTouch = function() {
+ return this.pointerID !== -1;
+ };
+
+
+ try {
+ Object.defineProperties(p, {
+ localX: { get: p._get_localX },
+ localY: { get: p._get_localY },
+ isTouch: { get: p._get_isTouch }
+ });
+ } catch (e) {} // TODO: use Log
+
+
+// public methods:
+ /**
+ * Returns a clone of the MouseEvent instance.
+ * @method clone
+ * @return {MouseEvent} a clone of the MouseEvent instance.
+ **/
+ p.clone = function() {
+ return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY);
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]";
+ };
+
+
+ createjs.MouseEvent = createjs.promote(MouseEvent, "Event");
+}());
+
+//##############################################################################
+// Matrix2D.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Represents an affine transformation matrix, and provides tools for constructing and concatenating matrices.
+ *
+ * This matrix can be visualized as:
+ *
+ * [ a c tx
+ * b d ty
+ * 0 0 1 ]
+ *
+ * Note the locations of b and c.
+ *
+ * @class Matrix2D
+ * @param {Number} [a=1] Specifies the a property for the new matrix.
+ * @param {Number} [b=0] Specifies the b property for the new matrix.
+ * @param {Number} [c=0] Specifies the c property for the new matrix.
+ * @param {Number} [d=1] Specifies the d property for the new matrix.
+ * @param {Number} [tx=0] Specifies the tx property for the new matrix.
+ * @param {Number} [ty=0] Specifies the ty property for the new matrix.
+ * @constructor
+ **/
+ function Matrix2D(a, b, c, d, tx, ty) {
+ this.setValues(a,b,c,d,tx,ty);
+
+ // public properties:
+ // assigned in the setValues method.
+ /**
+ * Position (0, 0) in a 3x3 affine transformation matrix.
+ * @property a
+ * @type Number
+ **/
+
+ /**
+ * Position (0, 1) in a 3x3 affine transformation matrix.
+ * @property b
+ * @type Number
+ **/
+
+ /**
+ * Position (1, 0) in a 3x3 affine transformation matrix.
+ * @property c
+ * @type Number
+ **/
+
+ /**
+ * Position (1, 1) in a 3x3 affine transformation matrix.
+ * @property d
+ * @type Number
+ **/
+
+ /**
+ * Position (2, 0) in a 3x3 affine transformation matrix.
+ * @property tx
+ * @type Number
+ **/
+
+ /**
+ * Position (2, 1) in a 3x3 affine transformation matrix.
+ * @property ty
+ * @type Number
+ **/
+ }
+ var p = Matrix2D.prototype;
+
+// constants:
+ /**
+ * Multiplier for converting degrees to radians. Used internally by Matrix2D.
+ * @property DEG_TO_RAD
+ * @static
+ * @final
+ * @type Number
+ * @readonly
+ **/
+ Matrix2D.DEG_TO_RAD = Math.PI/180;
+
+
+// static public properties:
+ /**
+ * An identity matrix, representing a null transformation.
+ * @property identity
+ * @static
+ * @type Matrix2D
+ * @readonly
+ **/
+ Matrix2D.identity = null; // set at bottom of class definition.
+
+
+// public methods:
+ /**
+ * Sets the specified values on this instance.
+ * @method setValues
+ * @param {Number} [a=1] Specifies the a property for the new matrix.
+ * @param {Number} [b=0] Specifies the b property for the new matrix.
+ * @param {Number} [c=0] Specifies the c property for the new matrix.
+ * @param {Number} [d=1] Specifies the d property for the new matrix.
+ * @param {Number} [tx=0] Specifies the tx property for the new matrix.
+ * @param {Number} [ty=0] Specifies the ty property for the new matrix.
+ * @return {Matrix2D} This instance. Useful for chaining method calls.
+ */
+ p.setValues = function(a, b, c, d, tx, ty) {
+ // don't forget to update docs in the constructor if these change:
+ this.a = (a == null) ? 1 : a;
+ this.b = b || 0;
+ this.c = c || 0;
+ this.d = (d == null) ? 1 : d;
+ this.tx = tx || 0;
+ this.ty = ty || 0;
+ return this;
+ };
+
+ /**
+ * Appends the specified matrix properties to this matrix. All parameters are required.
+ * This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
+ * @method append
+ * @param {Number} a
+ * @param {Number} b
+ * @param {Number} c
+ * @param {Number} d
+ * @param {Number} tx
+ * @param {Number} ty
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.append = function(a, b, c, d, tx, ty) {
+ var a1 = this.a;
+ var b1 = this.b;
+ var c1 = this.c;
+ var d1 = this.d;
+ if (a != 1 || b != 0 || c != 0 || d != 1) {
+ this.a = a1*a+c1*b;
+ this.b = b1*a+d1*b;
+ this.c = a1*c+c1*d;
+ this.d = b1*c+d1*d;
+ }
+ this.tx = a1*tx+c1*ty+this.tx;
+ this.ty = b1*tx+d1*ty+this.ty;
+ return this;
+ };
+
+ /**
+ * Prepends the specified matrix properties to this matrix.
+ * This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
+ * All parameters are required.
+ * @method prepend
+ * @param {Number} a
+ * @param {Number} b
+ * @param {Number} c
+ * @param {Number} d
+ * @param {Number} tx
+ * @param {Number} ty
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.prepend = function(a, b, c, d, tx, ty) {
+ var a1 = this.a;
+ var c1 = this.c;
+ var tx1 = this.tx;
+
+ this.a = a*a1+c*this.b;
+ this.b = b*a1+d*this.b;
+ this.c = a*c1+c*this.d;
+ this.d = b*c1+d*this.d;
+ this.tx = a*tx1+c*this.ty+tx;
+ this.ty = b*tx1+d*this.ty+ty;
+ return this;
+ };
+
+ /**
+ * Appends the specified matrix to this matrix.
+ * This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
+ * @method appendMatrix
+ * @param {Matrix2D} matrix
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.appendMatrix = function(matrix) {
+ return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
+ };
+
+ /**
+ * Prepends the specified matrix to this matrix.
+ * This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
+ * For example, you could calculate the combined transformation for a child object using:
+ *
+ * var o = myDisplayObject;
+ * var mtx = o.getMatrix();
+ * while (o = o.parent) {
+ * // prepend each parent's transformation in turn:
+ * o.prependMatrix(o.getMatrix());
+ * }
+ * @method prependMatrix
+ * @param {Matrix2D} matrix
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.prependMatrix = function(matrix) {
+ return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
+ };
+
+ /**
+ * Generates matrix properties from the specified display object transform properties, and appends them to this matrix.
+ * For example, you can use this to generate a matrix representing the transformations of a display object:
+ *
+ * var mtx = new createjs.Matrix2D();
+ * mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation);
+ * @method appendTransform
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} scaleX
+ * @param {Number} scaleY
+ * @param {Number} rotation
+ * @param {Number} skewX
+ * @param {Number} skewY
+ * @param {Number} regX Optional.
+ * @param {Number} regY Optional.
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
+ if (rotation%360) {
+ var r = rotation*Matrix2D.DEG_TO_RAD;
+ var cos = Math.cos(r);
+ var sin = Math.sin(r);
+ } else {
+ cos = 1;
+ sin = 0;
+ }
+
+ if (skewX || skewY) {
+ // TODO: can this be combined into a single append operation?
+ skewX *= Matrix2D.DEG_TO_RAD;
+ skewY *= Matrix2D.DEG_TO_RAD;
+ this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
+ this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
+ } else {
+ this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
+ }
+
+ if (regX || regY) {
+ // append the registration offset:
+ this.tx -= regX*this.a+regY*this.c;
+ this.ty -= regX*this.b+regY*this.d;
+ }
+ return this;
+ };
+
+ /**
+ * Generates matrix properties from the specified display object transform properties, and prepends them to this matrix.
+ * For example, you could calculate the combined transformation for a child object using:
+ *
+ * var o = myDisplayObject;
+ * var mtx = new createjs.Matrix2D();
+ * do {
+ * // prepend each parent's transformation in turn:
+ * mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
+ * } while (o = o.parent);
+ *
+ * Note that the above example would not account for {{#crossLink "DisplayObject/transformMatrix:property"}}{{/crossLink}}
+ * values. See {{#crossLink "Matrix2D/prependMatrix"}}{{/crossLink}} for an example that does.
+ * @method prependTransform
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} scaleX
+ * @param {Number} scaleY
+ * @param {Number} rotation
+ * @param {Number} skewX
+ * @param {Number} skewY
+ * @param {Number} regX Optional.
+ * @param {Number} regY Optional.
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
+ if (rotation%360) {
+ var r = rotation*Matrix2D.DEG_TO_RAD;
+ var cos = Math.cos(r);
+ var sin = Math.sin(r);
+ } else {
+ cos = 1;
+ sin = 0;
+ }
+
+ if (regX || regY) {
+ // prepend the registration offset:
+ this.tx -= regX; this.ty -= regY;
+ }
+ if (skewX || skewY) {
+ // TODO: can this be combined into a single prepend operation?
+ skewX *= Matrix2D.DEG_TO_RAD;
+ skewY *= Matrix2D.DEG_TO_RAD;
+ this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
+ this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
+ } else {
+ this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
+ }
+ return this;
+ };
+
+ /**
+ * Applies a clockwise rotation transformation to the matrix.
+ * @method rotate
+ * @param {Number} angle The angle to rotate by, in degrees. To use a value in radians, multiply it by `180/Math.PI`.
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.rotate = function(angle) {
+ angle = angle*Matrix2D.DEG_TO_RAD;
+ var cos = Math.cos(angle);
+ var sin = Math.sin(angle);
+
+ var a1 = this.a;
+ var b1 = this.b;
+
+ this.a = a1*cos+this.c*sin;
+ this.b = b1*cos+this.d*sin;
+ this.c = -a1*sin+this.c*cos;
+ this.d = -b1*sin+this.d*cos;
+ return this;
+ };
+
+ /**
+ * Applies a skew transformation to the matrix.
+ * @method skew
+ * @param {Number} skewX The amount to skew horizontally in degrees. To use a value in radians, multiply it by `180/Math.PI`.
+ * @param {Number} skewY The amount to skew vertically in degrees.
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ */
+ p.skew = function(skewX, skewY) {
+ skewX = skewX*Matrix2D.DEG_TO_RAD;
+ skewY = skewY*Matrix2D.DEG_TO_RAD;
+ this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0);
+ return this;
+ };
+
+ /**
+ * Applies a scale transformation to the matrix.
+ * @method scale
+ * @param {Number} x The amount to scale horizontally. E.G. a value of 2 will double the size in the X direction, and 0.5 will halve it.
+ * @param {Number} y The amount to scale vertically.
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.scale = function(x, y) {
+ this.a *= x;
+ this.b *= x;
+ this.c *= y;
+ this.d *= y;
+ //this.tx *= x;
+ //this.ty *= y;
+ return this;
+ };
+
+ /**
+ * Translates the matrix on the x and y axes.
+ * @method translate
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.translate = function(x, y) {
+ this.tx += this.a*x + this.c*y;
+ this.ty += this.b*x + this.d*y;
+ return this;
+ };
+
+ /**
+ * Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation).
+ * @method identity
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.identity = function() {
+ this.a = this.d = 1;
+ this.b = this.c = this.tx = this.ty = 0;
+ return this;
+ };
+
+ /**
+ * Inverts the matrix, causing it to perform the opposite transformation.
+ * @method invert
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ **/
+ p.invert = function() {
+ var a1 = this.a;
+ var b1 = this.b;
+ var c1 = this.c;
+ var d1 = this.d;
+ var tx1 = this.tx;
+ var n = a1*d1-b1*c1;
+
+ this.a = d1/n;
+ this.b = -b1/n;
+ this.c = -c1/n;
+ this.d = a1/n;
+ this.tx = (c1*this.ty-d1*tx1)/n;
+ this.ty = -(a1*this.ty-b1*tx1)/n;
+ return this;
+ };
+
+ /**
+ * Returns true if the matrix is an identity matrix.
+ * @method isIdentity
+ * @return {Boolean}
+ **/
+ p.isIdentity = function() {
+ return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
+ };
+
+ /**
+ * Returns true if this matrix is equal to the specified matrix (all property values are equal).
+ * @method equals
+ * @param {Matrix2D} matrix The matrix to compare.
+ * @return {Boolean}
+ **/
+ p.equals = function(matrix) {
+ return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d;
+ };
+
+ /**
+ * Transforms a point according to this matrix.
+ * @method transformPoint
+ * @param {Number} x The x component of the point to transform.
+ * @param {Number} y The y component of the point to transform.
+ * @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned.
+ * @return {Point} This matrix. Useful for chaining method calls.
+ **/
+ p.transformPoint = function(x, y, pt) {
+ pt = pt||{};
+ pt.x = x*this.a+y*this.c+this.tx;
+ pt.y = x*this.b+y*this.d+this.ty;
+ return pt;
+ };
+
+ /**
+ * Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values
+ * may not match the transform properties you used to generate the matrix, though they will produce the same visual
+ * results.
+ * @method decompose
+ * @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned.
+ * @return {Object} The target, or a new generic object with the transform properties applied.
+ */
+ p.decompose = function(target) {
+ // TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative
+ if (target == null) { target = {}; }
+ target.x = this.tx;
+ target.y = this.ty;
+ target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
+ target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);
+
+ var skewX = Math.atan2(-this.c, this.d);
+ var skewY = Math.atan2(this.b, this.a);
+
+ var delta = Math.abs(1-skewX/skewY);
+ if (delta < 0.00001) { // effectively identical, can use rotation:
+ target.rotation = skewY/Matrix2D.DEG_TO_RAD;
+ if (this.a < 0 && this.d >= 0) {
+ target.rotation += (target.rotation <= 0) ? 180 : -180;
+ }
+ target.skewX = target.skewY = 0;
+ } else {
+ target.skewX = skewX/Matrix2D.DEG_TO_RAD;
+ target.skewY = skewY/Matrix2D.DEG_TO_RAD;
+ }
+ return target;
+ };
+
+ /**
+ * Copies all properties from the specified matrix to this matrix.
+ * @method copy
+ * @param {Matrix2D} matrix The matrix to copy properties from.
+ * @return {Matrix2D} This matrix. Useful for chaining method calls.
+ */
+ p.copy = function(matrix) {
+ return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
+ };
+
+ /**
+ * Returns a clone of the Matrix2D instance.
+ * @method clone
+ * @return {Matrix2D} a clone of the Matrix2D instance.
+ **/
+ p.clone = function() {
+ return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty);
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]";
+ };
+
+ // this has to be populated after the class is defined:
+ Matrix2D.identity = new Matrix2D();
+
+
+ createjs.Matrix2D = Matrix2D;
+}());
+
+//##############################################################################
+// DisplayProps.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+ /**
+ * Used for calculating and encapsulating display related properties.
+ * @class DisplayProps
+ * @param {Number} [visible=true] Visible value.
+ * @param {Number} [alpha=1] Alpha value.
+ * @param {Number} [shadow=null] A Shadow instance or null.
+ * @param {Number} [compositeOperation=null] A compositeOperation value or null.
+ * @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix.
+ * @constructor
+ **/
+ function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) {
+ this.setValues(visible, alpha, shadow, compositeOperation, matrix);
+
+ // public properties:
+ // assigned in the setValues method.
+ /**
+ * Property representing the alpha that will be applied to a display object.
+ * @property alpha
+ * @type Number
+ **/
+
+ /**
+ * Property representing the shadow that will be applied to a display object.
+ * @property shadow
+ * @type Shadow
+ **/
+
+ /**
+ * Property representing the compositeOperation that will be applied to a display object.
+ * You can find a list of valid composite operations at:
+ * https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing
+ * @property compositeOperation
+ * @type String
+ **/
+
+ /**
+ * Property representing the value for visible that will be applied to a display object.
+ * @property visible
+ * @type Boolean
+ **/
+
+ /**
+ * The transformation matrix that will be applied to a display object.
+ * @property matrix
+ * @type Matrix2D
+ **/
+ }
+ var p = DisplayProps.prototype;
+
+// initialization:
+ /**
+ * Reinitializes the instance with the specified values.
+ * @method setValues
+ * @param {Number} [visible=true] Visible value.
+ * @param {Number} [alpha=1] Alpha value.
+ * @param {Number} [shadow=null] A Shadow instance or null.
+ * @param {Number} [compositeOperation=null] A compositeOperation value or null.
+ * @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix.
+ * @return {DisplayProps} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) {
+ this.visible = visible == null ? true : !!visible;
+ this.alpha = alpha == null ? 1 : alpha;
+ this.shadow = shadow;
+ this.compositeOperation = compositeOperation;
+ this.matrix = matrix || (this.matrix&&this.matrix.identity()) || new createjs.Matrix2D();
+ return this;
+ };
+
+// public methods:
+ /**
+ * Appends the specified display properties. This is generally used to apply a child's properties its parent's.
+ * @method append
+ * @param {Boolean} visible desired visible value
+ * @param {Number} alpha desired alpha value
+ * @param {Shadow} shadow desired shadow value
+ * @param {String} compositeOperation desired composite operation value
+ * @param {Matrix2D} [matrix] a Matrix2D instance
+ * @return {DisplayProps} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.append = function(visible, alpha, shadow, compositeOperation, matrix) {
+ this.alpha *= alpha;
+ this.shadow = shadow || this.shadow;
+ this.compositeOperation = compositeOperation || this.compositeOperation;
+ this.visible = this.visible && visible;
+ matrix&&this.matrix.appendMatrix(matrix);
+ return this;
+ };
+
+ /**
+ * Prepends the specified display properties. This is generally used to apply a parent's properties to a child's.
+ * For example, to get the combined display properties that would be applied to a child, you could use:
+ *
+ * var o = myDisplayObject;
+ * var props = new createjs.DisplayProps();
+ * do {
+ * // prepend each parent's props in turn:
+ * props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix());
+ * } while (o = o.parent);
+ *
+ * @method prepend
+ * @param {Boolean} visible desired visible value
+ * @param {Number} alpha desired alpha value
+ * @param {Shadow} shadow desired shadow value
+ * @param {String} compositeOperation desired composite operation value
+ * @param {Matrix2D} [matrix] a Matrix2D instance
+ * @return {DisplayProps} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.prepend = function(visible, alpha, shadow, compositeOperation, matrix) {
+ this.alpha *= alpha;
+ this.shadow = this.shadow || shadow;
+ this.compositeOperation = this.compositeOperation || compositeOperation;
+ this.visible = this.visible && visible;
+ matrix&&this.matrix.prependMatrix(matrix);
+ return this;
+ };
+
+ /**
+ * Resets this instance and its matrix to default values.
+ * @method identity
+ * @return {DisplayProps} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.identity = function() {
+ this.visible = true;
+ this.alpha = 1;
+ this.shadow = this.compositeOperation = null;
+ this.matrix.identity();
+ return this;
+ };
+
+ /**
+ * Returns a clone of the DisplayProps instance. Clones the associated matrix.
+ * @method clone
+ * @return {DisplayProps} a clone of the DisplayProps instance.
+ **/
+ p.clone = function() {
+ return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone());
+ };
+
+// private methods:
+
+ createjs.DisplayProps = DisplayProps;
+})();
+
+//##############################################################################
+// Point.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Represents a point on a 2 dimensional x / y coordinate system.
+ *
+ * Example
+ *
+ * var point = new createjs.Point(0, 100);
+ *
+ * @class Point
+ * @param {Number} [x=0] X position.
+ * @param {Number} [y=0] Y position.
+ * @constructor
+ **/
+ function Point(x, y) {
+ this.setValues(x, y);
+
+
+ // public properties:
+ // assigned in the setValues method.
+ /**
+ * X position.
+ * @property x
+ * @type Number
+ **/
+
+ /**
+ * Y position.
+ * @property y
+ * @type Number
+ **/
+ }
+ var p = Point.prototype;
+
+// public methods:
+ /**
+ * Sets the specified values on this instance.
+ * @method setValues
+ * @param {Number} [x=0] X position.
+ * @param {Number} [y=0] Y position.
+ * @return {Point} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.setValues = function(x, y) {
+ this.x = x||0;
+ this.y = y||0;
+ return this;
+ };
+
+ /**
+ * Copies all properties from the specified point to this point.
+ * @method copy
+ * @param {Point} point The point to copy properties from.
+ * @return {Point} This point. Useful for chaining method calls.
+ * @chainable
+ */
+ p.copy = function(point) {
+ this.x = point.x;
+ this.y = point.y;
+ return this;
+ };
+
+ /**
+ * Returns a clone of the Point instance.
+ * @method clone
+ * @return {Point} a clone of the Point instance.
+ **/
+ p.clone = function() {
+ return new Point(this.x, this.y);
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Point (x="+this.x+" y="+this.y+")]";
+ };
+
+
+ createjs.Point = Point;
+}());
+
+//##############################################################################
+// Rectangle.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Represents a rectangle as defined by the points (x, y) and (x+width, y+height).
+ *
+ * Example
+ *
+ * var rect = new createjs.Rectangle(0, 0, 100, 100);
+ *
+ * @class Rectangle
+ * @param {Number} [x=0] X position.
+ * @param {Number} [y=0] Y position.
+ * @param {Number} [width=0] The width of the Rectangle.
+ * @param {Number} [height=0] The height of the Rectangle.
+ * @constructor
+ **/
+ function Rectangle(x, y, width, height) {
+ this.setValues(x, y, width, height);
+
+
+ // public properties:
+ // assigned in the setValues method.
+ /**
+ * X position.
+ * @property x
+ * @type Number
+ **/
+
+ /**
+ * Y position.
+ * @property y
+ * @type Number
+ **/
+
+ /**
+ * Width.
+ * @property width
+ * @type Number
+ **/
+
+ /**
+ * Height.
+ * @property height
+ * @type Number
+ **/
+ }
+ var p = Rectangle.prototype;
+
+// public methods:
+ /**
+ * Sets the specified values on this instance.
+ * @method setValues
+ * @param {Number} [x=0] X position.
+ * @param {Number} [y=0] Y position.
+ * @param {Number} [width=0] The width of the Rectangle.
+ * @param {Number} [height=0] The height of the Rectangle.
+ * @return {Rectangle} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.setValues = function(x, y, width, height) {
+ // don't forget to update docs in the constructor if these change:
+ this.x = x||0;
+ this.y = y||0;
+ this.width = width||0;
+ this.height = height||0;
+ return this;
+ };
+
+ /**
+ * Extends the rectangle's bounds to include the described point or rectangle.
+ * @method extend
+ * @param {Number} x X position of the point or rectangle.
+ * @param {Number} y Y position of the point or rectangle.
+ * @param {Number} [width=0] The width of the rectangle.
+ * @param {Number} [height=0] The height of the rectangle.
+ * @return {Rectangle} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.extend = function(x, y, width, height) {
+ width = width||0;
+ height = height||0;
+ if (x+width > this.x+this.width) { this.width = x+width-this.x; }
+ if (y+height > this.y+this.height) { this.height = y+height-this.y; }
+ if (x < this.x) { this.width += this.x-x; this.x = x; }
+ if (y < this.y) { this.height += this.y-y; this.y = y; }
+ return this;
+ };
+
+ /**
+ * Adds the specified padding to the rectangle's bounds.
+ * @method pad
+ * @param {Number} top
+ * @param {Number} left
+ * @param {Number} bottom
+ * @param {Number} right
+ * @return {Rectangle} This instance. Useful for chaining method calls.
+ * @chainable
+ */
+ p.pad = function(top, left, bottom, right) {
+ this.x -= left;
+ this.y -= top;
+ this.width += left+right;
+ this.height += top+bottom;
+ return this;
+ };
+
+ /**
+ * Copies all properties from the specified rectangle to this rectangle.
+ * @method copy
+ * @param {Rectangle} rectangle The rectangle to copy properties from.
+ * @return {Rectangle} This rectangle. Useful for chaining method calls.
+ * @chainable
+ */
+ p.copy = function(rectangle) {
+ return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+ };
+
+ /**
+ * Returns true if this rectangle fully encloses the described point or rectangle.
+ * @method contains
+ * @param {Number} x X position of the point or rectangle.
+ * @param {Number} y Y position of the point or rectangle.
+ * @param {Number} [width=0] The width of the rectangle.
+ * @param {Number} [height=0] The height of the rectangle.
+ * @return {Boolean} True if the described point or rectangle is contained within this rectangle.
+ */
+ p.contains = function(x, y, width, height) {
+ width = width||0;
+ height = height||0;
+ return (x >= this.x && x+width <= this.x+this.width && y >= this.y && y+height <= this.y+this.height);
+ };
+
+ /**
+ * Returns a new rectangle which contains this rectangle and the specified rectangle.
+ * @method union
+ * @param {Rectangle} rect The rectangle to calculate a union with.
+ * @return {Rectangle} A new rectangle describing the union.
+ */
+ p.union = function(rect) {
+ return this.clone().extend(rect.x, rect.y, rect.width, rect.height);
+ };
+
+ /**
+ * Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle,
+ * or null if they do not intersect.
+ * @method intersection
+ * @param {Rectangle} rect The rectangle to calculate an intersection with.
+ * @return {Rectangle} A new rectangle describing the intersection or null.
+ */
+ p.intersection = function(rect) {
+ var x1 = rect.x, y1 = rect.y, x2 = x1+rect.width, y2 = y1+rect.height;
+ if (this.x > x1) { x1 = this.x; }
+ if (this.y > y1) { y1 = this.y; }
+ if (this.x + this.width < x2) { x2 = this.x + this.width; }
+ if (this.y + this.height < y2) { y2 = this.y + this.height; }
+ return (x2 <= x1 || y2 <= y1) ? null : new Rectangle(x1, y1, x2-x1, y2-y1);
+ };
+
+ /**
+ * Returns true if the specified rectangle intersects (has any overlap) with this rectangle.
+ * @method intersects
+ * @param {Rectangle} rect The rectangle to compare.
+ * @return {Boolean} True if the rectangles intersect.
+ */
+ p.intersects = function(rect) {
+ return (rect.x <= this.x+this.width && this.x <= rect.x+rect.width && rect.y <= this.y+this.height && this.y <= rect.y + rect.height);
+ };
+
+ /**
+ * Returns true if the width or height are equal or less than 0.
+ * @method isEmpty
+ * @return {Boolean} True if the rectangle is empty.
+ */
+ p.isEmpty = function() {
+ return this.width <= 0 || this.height <= 0;
+ };
+
+ /**
+ * Returns a clone of the Rectangle instance.
+ * @method clone
+ * @return {Rectangle} a clone of the Rectangle instance.
+ **/
+ p.clone = function() {
+ return new Rectangle(this.x, this.y, this.width, this.height);
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]";
+ };
+
+
+ createjs.Rectangle = Rectangle;
+}());
+
+//##############################################################################
+// ButtonHelper.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or
+ * {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and
+ * automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}},
+ * to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame.
+ *
+ * The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent
+ * garbage collection.
+ *
+ * Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
+ *
+ * Example
+ *
+ * var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit");
+ * myInstance.addEventListener("click", handleClick);
+ * function handleClick(event) {
+ * // Click Happened.
+ * }
+ *
+ * @class ButtonHelper
+ * @param {Sprite|MovieClip} target The instance to manage.
+ * @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button.
+ * @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button.
+ * @param {String} [downLabel="down"] The label or animation to go to when the user presses the button.
+ * @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing
+ * states.
+ * @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined,
+ * then the button's visible states will be used instead. Note that the same instance as the "target" argument can be
+ * used for the hitState.
+ * @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is
+ * null, then the default state of the hitArea will be used. *
+ * @constructor
+ */
+ function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) {
+ if (!target.addEventListener) { return; }
+
+
+ // public properties:
+ /**
+ * The target for this button helper.
+ * @property target
+ * @type MovieClip | Sprite
+ * @readonly
+ **/
+ this.target = target;
+
+ /**
+ * The label name or frame number to display when the user mouses out of the target. Defaults to "over".
+ * @property overLabel
+ * @type String | Number
+ **/
+ this.overLabel = overLabel == null ? "over" : overLabel;
+
+ /**
+ * The label name or frame number to display when the user mouses over the target. Defaults to "out".
+ * @property outLabel
+ * @type String | Number
+ **/
+ this.outLabel = outLabel == null ? "out" : outLabel;
+
+ /**
+ * The label name or frame number to display when the user presses on the target. Defaults to "down".
+ * @property downLabel
+ * @type String | Number
+ **/
+ this.downLabel = downLabel == null ? "down" : downLabel;
+
+ /**
+ * If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false.
+ * @property play
+ * @default false
+ * @type Boolean
+ **/
+ this.play = play;
+
+
+ // private properties
+ /**
+ * @property _isPressed
+ * @type Boolean
+ * @protected
+ **/
+ this._isPressed = false;
+
+ /**
+ * @property _isOver
+ * @type Boolean
+ * @protected
+ **/
+ this._isOver = false;
+
+ /**
+ * @property _enabled
+ * @type Boolean
+ * @protected
+ **/
+ this._enabled = false;
+
+ // setup:
+ target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes.
+ this.enabled = true;
+ this.handleEvent({});
+ if (hitArea) {
+ if (hitLabel) {
+ hitArea.actionsEnabled = false;
+ hitArea.gotoAndStop&&hitArea.gotoAndStop(hitLabel);
+ }
+ target.hitArea = hitArea;
+ }
+ }
+ var p = ButtonHelper.prototype;
+
+// getter / setters:
+ /**
+ * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
+ * @method setEnabled
+ * @param {Boolean} value The enabled property to set the instance to.
+ * @[rptected
+ * @protected
+ **/
+ p._setEnabled = function(value) {
+ if (value == this._enabled) { return; }
+ var o = this.target;
+ this._enabled = value;
+ if (value) {
+ o.cursor = "pointer";
+ o.addEventListener("rollover", this);
+ o.addEventListener("rollout", this);
+ o.addEventListener("mousedown", this);
+ o.addEventListener("pressup", this);
+ if (o._reset) { o.__reset = o._reset; o._reset = this._reset;}
+ } else {
+ o.cursor = null;
+ o.removeEventListener("rollover", this);
+ o.removeEventListener("rollout", this);
+ o.removeEventListener("mousedown", this);
+ o.removeEventListener("pressup", this);
+ if (o.__reset) { o._reset = o.__reset; delete(o.__reset); }
+ }
+ };
+ // ButtonHelper.setEnabled is @deprecated. Remove for 1.1+
+ p.setEnabled = createjs.deprecate(p._setEnabled, "ButtonHelper.setEnabled");
+
+ /**
+ * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
+ * @method getEnabled
+ * @protected
+ * @return {Boolean}
+ **/
+ p._getEnabled = function() {
+ return this._enabled;
+ };
+ // ButtonHelper.getEnabled is @deprecated. Remove for 1.1+
+ p.getEnabled = createjs.deprecate(p._getEnabled, "ButtonHelper.getEnabled");
+
+ /**
+ * Enables or disables the button functionality on the target.
+ * @property enabled
+ * @type {Boolean}
+ **/
+ try {
+ Object.defineProperties(p, {
+ enabled: { get: p._getEnabled, set: p._setEnabled }
+ });
+ } catch (e) {} // TODO: use Log
+
+
+// public methods:
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[ButtonHelper]";
+ };
+
+
+// private methods:
+ /**
+ * @method handleEvent
+ * @param {Object} evt The mouse event to handle.
+ * @protected
+ **/
+ p.handleEvent = function(evt) {
+ var label, t = this.target, type = evt.type;
+ if (type == "mousedown") {
+ this._isPressed = true;
+ label = this.downLabel;
+ } else if (type == "pressup") {
+ this._isPressed = false;
+ label = this._isOver ? this.overLabel : this.outLabel;
+ } else if (type == "rollover") {
+ this._isOver = true;
+ label = this._isPressed ? this.downLabel : this.overLabel;
+ } else { // rollout and default
+ this._isOver = false;
+ label = this._isPressed ? this.overLabel : this.outLabel;
+ }
+ if (this.play) {
+ t.gotoAndPlay&&t.gotoAndPlay(label);
+ } else {
+ t.gotoAndStop&&t.gotoAndStop(label);
+ }
+ };
+
+ /**
+ * Injected into target. Preserves the paused state through a reset.
+ * @method _reset
+ * @protected
+ **/
+ p._reset = function() {
+ // TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures.
+ var p = this.paused;
+ this.__reset();
+ this.paused = p;
+ };
+
+
+ createjs.ButtonHelper = ButtonHelper;
+}());
+
+//##############################################################################
+// Shadow.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}}
+ * via its shadow
property.
+ *
+ * Example
+ *
+ * myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10);
+ *
+ * @class Shadow
+ * @constructor
+ * @param {String} color The color of the shadow. This can be any valid CSS color value.
+ * @param {Number} offsetX The x offset of the shadow in pixels.
+ * @param {Number} offsetY The y offset of the shadow in pixels.
+ * @param {Number} blur The size of the blurring effect.
+ **/
+ function Shadow(color, offsetX, offsetY, blur) {
+
+
+ // public properties:
+ /**
+ * The color of the shadow. This can be any valid CSS color value.
+ * @property color
+ * @type String
+ * @default null
+ */
+ this.color = color||"black";
+
+ /** The x offset of the shadow.
+ * @property offsetX
+ * @type Number
+ * @default 0
+ */
+ this.offsetX = offsetX||0;
+
+ /** The y offset of the shadow.
+ * @property offsetY
+ * @type Number
+ * @default 0
+ */
+ this.offsetY = offsetY||0;
+
+ /** The blur of the shadow.
+ * @property blur
+ * @type Number
+ * @default 0
+ */
+ this.blur = blur||0;
+ }
+ var p = Shadow.prototype;
+
+// static public properties:
+ /**
+ * An identity shadow object (all properties are set to 0).
+ * @property identity
+ * @type Shadow
+ * @static
+ * @final
+ * @readonly
+ **/
+ Shadow.identity = new Shadow("transparent", 0, 0, 0);
+
+
+// public methods:
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Shadow]";
+ };
+
+ /**
+ * Returns a clone of this Shadow instance.
+ * @method clone
+ * @return {Shadow} A clone of the current Shadow instance.
+ **/
+ p.clone = function() {
+ return new Shadow(this.color, this.offsetX, this.offsetY, this.blur);
+ };
+
+
+ createjs.Shadow = Shadow;
+}());
+
+//##############################################################################
+// SpriteSheet.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually
+ * animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100
+ * images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high).
+ *
+ * The data passed to the SpriteSheet constructor defines:
+ *
+ * - The source image or images to use.
+ * - The positions of individual image frames.
+ * - Sequences of frames that form named animations. Optional.
+ * - The target playback framerate. Optional.
+ *
+ * SpriteSheet Format
+ * SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties
+ * (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON.
+ *
+ * images
+ * An array of source images. Images can be either an HTMlimage
+ * instance, or a uri to an image. The former is recommended to control preloading.
+ *
+ * images: [image1, "path/to/image2.png"],
+ *
+ * frames
+ * Defines the individual frames. There are two supported formats for frame data:
+ * When all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`,
+ * and `count` properties.
+ *
+ *
+ * - `width` & `height` are required and specify the dimensions of the frames
+ * - `regX` & `regY` indicate the registration point or "origin" of the frames
+ * - `spacing` indicate the spacing between frames
+ * - `margin` specify the margin around the image(s)
+ * - `count` allows you to specify the total number of frames in the spritesheet; if omitted, this will
+ * be calculated based on the dimensions of the source images and the frames. Frames will be assigned
+ * indexes based on their position in the source images (left to right, top to bottom).
+ *
+ *
+ * frames: {width:64, height:64, count:20, regX: 32, regY:64, spacing:0, margin:0}
+ *
+ * If the frames are of different sizes, use an array of frame definitions. Each definition is itself an array
+ * with 4 required and 3 optional entries, in the order:
+ *
+ *
+ * - The first four, `x`, `y`, `width`, and `height` are required and define the frame rectangle.
+ * - The fifth, `imageIndex`, specifies the index of the source image (defaults to 0)
+ * - The last two, `regX` and `regY` specify the registration point of the frame
+ *
+ *
+ * frames: [
+ * // x, y, width, height, imageIndex*, regX*, regY*
+ * [64, 0, 96, 64],
+ * [0, 0, 64, 64, 1, 32, 32]
+ * // etc.
+ * ]
+ *
+ * animations
+ * Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an
+ * animation of the same name. Each animation must specify the frames to play, and may
+ * also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and
+ * the name of the `next` animation to sequence to after it completes.
+ *
+ * There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate:
+ *
+ * - for a single frame animation, you can simply specify the frame index
+ *
+ * animations: {
+ * sit: 7
+ * }
+ *
+ *
+ * -
+ * for an animation of consecutive frames, you can use an array with two required, and two optional entries
+ * in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive.
+ *
+ * animations: {
+ * // start, end, next*, speed*
+ * run: [0, 8],
+ * jump: [9, 12, "run", 2]
+ * }
+ *
+ *
+ * -
+ * for non-consecutive frames, you can use an object with a `frames` property defining an array of frame
+ * indexes to play in order. The object can also specify `next` and `speed` properties.
+ *
+ * animations: {
+ * walk: {
+ * frames: [1,2,3,3,2,1]
+ * },
+ * shoot: {
+ * frames: [1,4,5,6],
+ * next: "walk",
+ * speed: 0.5
+ * }
+ * }
+ *
+ *
+ *
+ * Note: the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency`
+ * property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in
+ * earlier versions, but is 4x normal speed in EaselJS 0.7.0+.
+ *
+ * framerate
+ * Optional. Indicates the default framerate to play this spritesheet at in frames per second. See
+ * {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information.
+ *
+ * framerate: 20
+ *
+ * Note that the Sprite framerate will only work if the stage update method is provided with the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
+ * event generated by the {{#crossLink "Ticker"}}{{/crossLink}}.
+ *
+ * createjs.Ticker.on("tick", handleTick);
+ * function handleTick(event) {
+ * stage.update(event);
+ * }
+ *
+ * Example
+ * To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three
+ * animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and
+ * sequencing back to run.
+ *
+ * var data = {
+ * images: ["sprites.jpg"],
+ * frames: {width:50, height:50},
+ * animations: {
+ * stand:0,
+ * run:[1,5],
+ * jump:[6,8,"run"]
+ * }
+ * };
+ * var spriteSheet = new createjs.SpriteSheet(data);
+ * var animation = new createjs.Sprite(spriteSheet, "run");
+ *
+ * Generating SpriteSheet Images
+ * Spritesheets can be created manually by combining images in PhotoShop, and specifying the frame size or
+ * coordinates manually, however there are a number of tools that facilitate this.
+ *
+ * - Exporting SpriteSheets or HTML5 content from Adobe Flash/Animate supports the EaselJS SpriteSheet format.
+ * - The popular Texture Packer has
+ * EaselJS support.
+ *
- SWF animations in Adobe Flash/Animate can be exported to SpriteSheets using Zoë
+ *
+ *
+ * Cross Origin Issues
+ * Warning: Images loaded cross-origin will throw cross-origin security errors when interacted with
+ * using:
+ *
+ * - a mouse
+ * - methods such as {{#crossLink "Container/getObjectUnderPoint"}}{{/crossLink}}
+ * - Filters (see {{#crossLink "Filter"}}{{/crossLink}})
+ * - caching (see {{#crossLink "DisplayObject/cache"}}{{/crossLink}})
+ *
+ * You can get around this by setting `crossOrigin` property on your images before passing them to EaselJS, or
+ * setting the `crossOrigin` property on PreloadJS' LoadQueue or LoadItems.
+ *
+ * var image = new Image();
+ * img.crossOrigin="Anonymous";
+ * img.src = "http://server-with-CORS-support.com/path/to/image.jpg";
+ *
+ * If you pass string paths to SpriteSheets, they will not work cross-origin. The server that stores the image must
+ * support cross-origin requests, or this will not work. For more information, check out
+ * CORS overview on MDN.
+ *
+ * @class SpriteSheet
+ * @constructor
+ * @param {Object} data An object describing the SpriteSheet data.
+ * @extends EventDispatcher
+ **/
+ function SpriteSheet(data) {
+ this.EventDispatcher_constructor();
+
+
+ // public properties:
+ /**
+ * Indicates whether all images are finished loading.
+ * @property complete
+ * @type Boolean
+ * @readonly
+ **/
+ this.complete = true;
+
+ /**
+ * Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See the Sprite class
+ * {{#crossLink "Sprite/framerate:property"}}{{/crossLink}} for more information.
+ * @property framerate
+ * @type Number
+ **/
+ this.framerate = 0;
+
+
+ // private properties:
+ /**
+ * @property _animations
+ * @protected
+ * @type Array
+ **/
+ this._animations = null;
+
+ /**
+ * @property _frames
+ * @protected
+ * @type Array
+ **/
+ this._frames = null;
+
+ /**
+ * @property _images
+ * @protected
+ * @type Array
+ **/
+ this._images = null;
+
+ /**
+ * @property _data
+ * @protected
+ * @type Object
+ **/
+ this._data = null;
+
+ /**
+ * @property _loadCount
+ * @protected
+ * @type Number
+ **/
+ this._loadCount = 0;
+
+ // only used for simple frame defs:
+ /**
+ * @property _frameHeight
+ * @protected
+ * @type Number
+ **/
+ this._frameHeight = 0;
+
+ /**
+ * @property _frameWidth
+ * @protected
+ * @type Number
+ **/
+ this._frameWidth = 0;
+
+ /**
+ * @property _numFrames
+ * @protected
+ * @type Number
+ **/
+ this._numFrames = 0;
+
+ /**
+ * @property _regX
+ * @protected
+ * @type Number
+ **/
+ this._regX = 0;
+
+ /**
+ * @property _regY
+ * @protected
+ * @type Number
+ **/
+ this._regY = 0;
+
+ /**
+ * @property _spacing
+ * @protected
+ * @type Number
+ **/
+ this._spacing = 0;
+
+ /**
+ * @property _margin
+ * @protected
+ * @type Number
+ **/
+ this._margin = 0;
+
+ // setup:
+ this._parseData(data);
+ }
+ var p = createjs.extend(SpriteSheet, createjs.EventDispatcher);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// events:
+ /**
+ * Dispatched when all images are loaded. Note that this only fires if the images
+ * were not fully loaded when the sprite sheet was initialized. You should check the complete property
+ * to prior to adding a listener. Ex.
+ *
+ * var sheet = new createjs.SpriteSheet(data);
+ * if (!sheet.complete) {
+ * // not preloaded, listen for the complete event:
+ * sheet.addEventListener("complete", handler);
+ * }
+ *
+ * @event complete
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when getFrame is called with a valid frame index. This is primarily intended for use by {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}}
+ * when doing on-demand rendering.
+ * @event getframe
+ * @param {Number} index The frame index.
+ * @param {Object} frame The frame object that getFrame will return.
+ */
+
+ /**
+ * Dispatched when an image encounters an error. A SpriteSheet will dispatch an error event for each image that
+ * encounters an error, and will still dispatch a {{#crossLink "SpriteSheet/complete:event"}}{{/crossLink}}
+ * event once all images are finished processing, even if an error is encountered.
+ * @event error
+ * @param {String} src The source of the image that failed to load.
+ * @since 0.8.2
+ */
+
+
+// getter / setters:
+ /**
+ * Use the {{#crossLink "SpriteSheet/animations:property"}}{{/crossLink}} property instead.
+ * @method _getAnimations
+ * @protected
+ * @return {Array}
+ **/
+ p._getAnimations = function() {
+ return this._animations.slice();
+ };
+ // SpriteSheet.getAnimations is @deprecated. Remove for 1.1+
+ p.getAnimations = createjs.deprecate(p._getAnimations, "SpriteSheet.getAnimations");
+
+ /**
+ * Returns an array of all available animation names available on this sprite sheet as strings.
+ * @property animations
+ * @type {Array}
+ * @readonly
+ **/
+ try {
+ Object.defineProperties(p, {
+ animations: { get: p._getAnimations }
+ });
+ } catch (e) {}
+
+
+// public methods:
+ /**
+ * Returns the total number of frames in the specified animation, or in the whole sprite
+ * sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and
+ * the images have not been fully loaded.
+ * @method getNumFrames
+ * @param {String} animation The name of the animation to get a frame count for.
+ * @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted.
+ */
+ p.getNumFrames = function(animation) {
+ if (animation == null) {
+ return this._frames ? this._frames.length : this._numFrames || 0;
+ } else {
+ var data = this._data[animation];
+ if (data == null) { return 0; }
+ else { return data.frames.length; }
+ }
+ };
+
+ /**
+ * Returns an object defining the specified animation. The returned object contains:
+ * - frames: an array of the frame ids in the animation
+ * - speed: the playback speed for this animation
+ * - name: the name of the animation
+ * - next: the default animation to play next. If the animation loops, the name and next property will be the
+ * same.
+ *
+ * @method getAnimation
+ * @param {String} name The name of the animation to get.
+ * @return {Object} a generic object with frames, speed, name, and next properties.
+ **/
+ p.getAnimation = function(name) {
+ return this._data[name];
+ };
+
+ /**
+ * Returns an object specifying the image and source rect of the specified frame. The returned object has:
+ * - an image property holding a reference to the image object in which the frame is found
+ * - a rect property containing a Rectangle instance which defines the boundaries for the frame within that
+ * image.
+ * - A regX and regY property corresponding to the regX/Y values for the frame.
+ *
+ * @method getFrame
+ * @param {Number} frameIndex The index of the frame.
+ * @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist.
+ **/
+ p.getFrame = function(frameIndex) {
+ var frame;
+ if (this._frames && (frame=this._frames[frameIndex])) { return frame; }
+ return null;
+ };
+
+ /**
+ * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative
+ * to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return:
+ *
+ * [x=-50, y=-40, width=90, height=70]
+ *
+ * @method getFrameBounds
+ * @param {Number} frameIndex The index of the frame.
+ * @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created.
+ * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded.
+ **/
+ p.getFrameBounds = function(frameIndex, rectangle) {
+ var frame = this.getFrame(frameIndex);
+ return frame ? (rectangle||new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[SpriteSheet]";
+ };
+
+ /**
+ * SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it.
+ * @method clone
+ **/
+ p.clone = function() {
+ throw("SpriteSheet cannot be cloned.")
+ };
+
+// private methods:
+ /**
+ * @method _parseData
+ * @param {Object} data An object describing the SpriteSheet data.
+ * @protected
+ **/
+ p._parseData = function(data) {
+ var i,l,o,a;
+ if (data == null) { return; }
+
+ this.framerate = data.framerate||0;
+
+ // parse images:
+ if (data.images && (l=data.images.length) > 0) {
+ a = this._images = [];
+ for (i=0; i= maxFrames) { break imgLoop; }
+ frameCount++;
+ this._frames.push({
+ image: img,
+ rect: new createjs.Rectangle(x, y, frameWidth, frameHeight),
+ regX: this._regX,
+ regY: this._regY
+ });
+ x += frameWidth+spacing;
+ }
+ y += frameHeight+spacing;
+ }
+ }
+ this._numFrames = frameCount;
+ };
+
+
+ createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher");
+}());
+
+//##############################################################################
+// Graphics.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a
+ * specified context. Note that you can use Graphics without any dependency on the EaselJS framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}}
+ * directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the
+ * context of an EaselJS display list.
+ *
+ * There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or
+ * instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}.
+ * The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes.
+ *
+ * var g = new createjs.Graphics();
+ * g.setStrokeStyle(1);
+ * g.beginStroke("#000000");
+ * g.beginFill("red");
+ * g.drawCircle(0,0,30);
+ *
+ * All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example,
+ * the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill:
+ *
+ * myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50);
+ *
+ * Each graphics API call generates a command object (see below). The last command to be created can be accessed via
+ * {{#crossLink "Graphics/command:property"}}{{/crossLink}}:
+ *
+ * var fillCommand = myGraphics.beginFill("red").command;
+ * // ... later, update the fill style/color:
+ * fillCommand.style = "blue";
+ * // or change it to a bitmap fill:
+ * fillCommand.bitmap(myImage);
+ *
+ * For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you
+ * need to manage path creation manually, and ensure that fill/stroke is applied to a defined path:
+ *
+ * // start a new path. Graphics.beginCmd is a reusable BeginPath instance:
+ * myGraphics.append(createjs.Graphics.beginCmd);
+ * // we need to define the path before applying the fill:
+ * var circle = new createjs.Graphics.Circle(0,0,30);
+ * myGraphics.append(circle);
+ * // fill the path we just defined:
+ * var fill = new createjs.Graphics.Fill("red");
+ * myGraphics.append(fill);
+ *
+ * These approaches can be used together, for example to insert a custom command:
+ *
+ * myGraphics.beginFill("red");
+ * var customCommand = new CustomSpiralCommand(etc);
+ * myGraphics.append(customCommand);
+ * myGraphics.beginFill("blue");
+ * myGraphics.drawCircle(0, 0, 30);
+ *
+ * See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands.
+ *
+ * Tiny API
+ * The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the
+ * Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS
+ * to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected
+ * descriptions in the docs.
+ *
+ *
+ * Tiny | Method | Tiny | Method |
+ * mt | {{#crossLink "Graphics/moveTo"}}{{/crossLink}} |
+ * lt | {{#crossLink "Graphics/lineTo"}}{{/crossLink}} |
+ * a/at | {{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} |
+ * bt | {{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} |
+ * qt | {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo) |
+ * r | {{#crossLink "Graphics/rect"}}{{/crossLink}} |
+ * cp | {{#crossLink "Graphics/closePath"}}{{/crossLink}} |
+ * c | {{#crossLink "Graphics/clear"}}{{/crossLink}} |
+ * f | {{#crossLink "Graphics/beginFill"}}{{/crossLink}} |
+ * lf | {{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} |
+ * rf | {{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} |
+ * bf | {{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} |
+ * ef | {{#crossLink "Graphics/endFill"}}{{/crossLink}} |
+ * ss / sd | {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} / {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} |
+ * s | {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} |
+ * ls | {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} |
+ * rs | {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} |
+ * bs | {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} |
+ * es | {{#crossLink "Graphics/endStroke"}}{{/crossLink}} |
+ * dr | {{#crossLink "Graphics/drawRect"}}{{/crossLink}} |
+ * rr | {{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} |
+ * rc | {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} |
+ * dc | {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} |
+ * de | {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} |
+ * dp | {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} |
+ * p | {{#crossLink "Graphics/decodePath"}}{{/crossLink}} |
+ *
+ *
+ * Here is the above example, using the tiny API instead.
+ *
+ * myGraphics.s("red").f("blue").r(20, 20, 100, 50);
+ *
+ * @class Graphics
+ * @constructor
+ **/
+ function Graphics() {
+
+
+ // public properties
+ /**
+ * Holds a reference to the last command that was created or appended. For example, you could retain a reference
+ * to a Fill command in order to dynamically update the color later by using:
+ *
+ * var myFill = myGraphics.beginFill("red").command;
+ * // update color later:
+ * myFill.style = "yellow";
+ *
+ * @property command
+ * @type Object
+ **/
+ this.command = null;
+
+
+ // private properties
+ /**
+ * @property _stroke
+ * @protected
+ * @type {Stroke}
+ **/
+ this._stroke = null;
+
+ /**
+ * @property _strokeStyle
+ * @protected
+ * @type {StrokeStyle}
+ **/
+ this._strokeStyle = null;
+
+ /**
+ * @property _oldStrokeStyle
+ * @protected
+ * @type {StrokeStyle}
+ **/
+ this._oldStrokeStyle = null;
+
+ /**
+ * @property _strokeDash
+ * @protected
+ * @type {StrokeDash}
+ **/
+ this._strokeDash = null;
+
+ /**
+ * @property _oldStrokeDash
+ * @protected
+ * @type {StrokeDash}
+ **/
+ this._oldStrokeDash = null;
+
+ /**
+ * @property _strokeIgnoreScale
+ * @protected
+ * @type Boolean
+ **/
+ this._strokeIgnoreScale = false;
+
+ /**
+ * @property _fill
+ * @protected
+ * @type {Fill}
+ **/
+ this._fill = null;
+
+ /**
+ * @property _instructions
+ * @protected
+ * @type {Array}
+ **/
+ this._instructions = [];
+
+ /**
+ * Indicates the last instruction index that was committed.
+ * @property _commitIndex
+ * @protected
+ * @type {Number}
+ **/
+ this._commitIndex = 0;
+
+ /**
+ * Uncommitted instructions.
+ * @property _activeInstructions
+ * @protected
+ * @type {Array}
+ **/
+ this._activeInstructions = [];
+
+ /**
+ * This indicates that there have been changes to the activeInstruction list since the last updateInstructions call.
+ * @property _dirty
+ * @protected
+ * @type {Boolean}
+ * @default false
+ **/
+ this._dirty = false;
+
+ /**
+ * Index to draw from if a store operation has happened.
+ * @property _storeIndex
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this._storeIndex = 0;
+
+ // setup:
+ this.clear();
+ }
+ var p = Graphics.prototype;
+ var G = Graphics; // shortcut
+
+// static public methods:
+ /**
+ * Returns a CSS compatible color string based on the specified RGB numeric color values in the format
+ * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example,
+ *
+ * createjs.Graphics.getRGB(50, 100, 150, 0.5);
+ * // Returns "rgba(50,100,150,0.5)"
+ *
+ * It also supports passing a single hex color value as the first param, and an optional alpha value as the second
+ * param. For example,
+ *
+ * createjs.Graphics.getRGB(0xFF00FF, 0.2);
+ * // Returns "rgba(255,0,255,0.2)"
+ *
+ * @method getRGB
+ * @static
+ * @param {Number} r The red component for the color, between 0 and 0xFF (255).
+ * @param {Number} g The green component for the color, between 0 and 0xFF (255).
+ * @param {Number} b The blue component for the color, between 0 and 0xFF (255).
+ * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
+ * @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format
+ * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)".
+ **/
+ Graphics.getRGB = function(r, g, b, alpha) {
+ if (r != null && b == null) {
+ alpha = g;
+ b = r&0xFF;
+ g = r>>8&0xFF;
+ r = r>>16&0xFF;
+ }
+ if (alpha == null) {
+ return "rgb("+r+","+g+","+b+")";
+ } else {
+ return "rgba("+r+","+g+","+b+","+alpha+")";
+ }
+ };
+
+ /**
+ * Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)",
+ * or if alpha is null then in the format "hsl(360,100,100)".
+ *
+ * createjs.Graphics.getHSL(150, 100, 70);
+ * // Returns "hsl(150,100,70)"
+ *
+ * @method getHSL
+ * @static
+ * @param {Number} hue The hue component for the color, between 0 and 360.
+ * @param {Number} saturation The saturation component for the color, between 0 and 100.
+ * @param {Number} lightness The lightness component for the color, between 0 and 100.
+ * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
+ * @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format
+ * "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)".
+ **/
+ Graphics.getHSL = function(hue, saturation, lightness, alpha) {
+ if (alpha == null) {
+ return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)";
+ } else {
+ return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")";
+ }
+ };
+
+
+// static properties:
+ /**
+ * A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid
+ * unnecessary instantiation.
+ * @property beginCmd
+ * @type {Graphics.BeginPath}
+ * @static
+ **/
+ // defined at the bottom of this file.
+
+ /**
+ * Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}.
+ * @property BASE_64
+ * @static
+ * @final
+ * @readonly
+ * @type {Object}
+ **/
+ Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63};
+
+ /**
+ * Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
+ * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
+ * "butt", 1 to "round", and 2 to "square".
+ * For example, to set the line caps to "square":
+ *
+ * myGraphics.ss(16, 2);
+ *
+ * @property STROKE_CAPS_MAP
+ * @static
+ * @final
+ * @readonly
+ * @type {Array}
+ **/
+ Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"];
+
+ /**
+ * Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
+ * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
+ * "miter", 1 to "round", and 2 to "bevel".
+ * For example, to set the line joints to "bevel":
+ *
+ * myGraphics.ss(16, 0, 2);
+ *
+ * @property STROKE_JOINTS_MAP
+ * @static
+ * @final
+ * @readonly
+ * @type {Array}
+ **/
+ Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"];
+
+ /**
+ * @property _ctx
+ * @static
+ * @protected
+ * @type {CanvasRenderingContext2D}
+ **/
+ var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
+ if (canvas.getContext) {
+ Graphics._ctx = canvas.getContext("2d");
+ canvas.width = canvas.height = 1;
+ }
+
+
+// getter / setters:
+ /**
+ * Use the {{#crossLink "Graphics/instructions:property"}}{{/crossLink}} property instead.
+ * @method _getInstructions
+ * @protected
+ * @return {Array} The instructions array, useful for chaining
+ **/
+ p._getInstructions = function() {
+ this._updateInstructions();
+ return this._instructions;
+ };
+ // Graphics.getInstructions is @deprecated. Remove for 1.1+
+ p.getInstructions = createjs.deprecate(p._getInstructions, "Graphics.getInstructions");
+
+ /**
+ * Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect)
+ * Modifying the returned array directly is not recommended, and is likely to result in unexpected behaviour.
+ *
+ * This property is mainly intended for introspection of the instructions (ex. for graphics export).
+ * @property instructions
+ * @type {Array}
+ * @readonly
+ **/
+ try {
+ Object.defineProperties(p, {
+ instructions: { get: p._getInstructions }
+ });
+ } catch (e) {}
+
+
+// public methods:
+ /**
+ * Returns true if this Graphics instance has no drawing commands.
+ * @method isEmpty
+ * @return {Boolean} Returns true if this Graphics instance has no drawing commands.
+ **/
+ p.isEmpty = function() {
+ return !(this._instructions.length || this._activeInstructions.length);
+ };
+
+ /**
+ * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data.
+ **/
+ p.draw = function(ctx, data) {
+ this._updateInstructions();
+ var instr = this._instructions;
+ for (var i=this._storeIndex, l=instr.length; iDisplayObject.mask to draw the clipping path, for example.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method drawAsPath
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ **/
+ p.drawAsPath = function(ctx) {
+ this._updateInstructions();
+ var instr, instrs = this._instructions;
+ for (var i=this._storeIndex, l=instrs.length; i
+ * whatwg spec.
+ * @method lineTo
+ * @param {Number} x The x coordinate the drawing point should draw to.
+ * @param {Number} y The y coordinate the drawing point should draw to.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.lineTo = function(x, y) {
+ return this.append(new G.LineTo(x,y));
+ };
+
+ /**
+ * Draws an arc with the specified control points and radius. For detailed information, read the
+ *
+ * whatwg spec. A tiny API method "at" also exists.
+ * @method arcTo
+ * @param {Number} x1
+ * @param {Number} y1
+ * @param {Number} x2
+ * @param {Number} y2
+ * @param {Number} radius
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.arcTo = function(x1, y1, x2, y2, radius) {
+ return this.append(new G.ArcTo(x1, y1, x2, y2, radius));
+ };
+
+ /**
+ * Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For
+ * example, to draw a full circle with a radius of 20 centered at (100, 100):
+ *
+ * arc(100, 100, 20, 0, Math.PI*2);
+ *
+ * For detailed information, read the
+ * whatwg spec.
+ * A tiny API method "a" also exists.
+ * @method arc
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} radius
+ * @param {Number} startAngle Measured in radians.
+ * @param {Number} endAngle Measured in radians.
+ * @param {Boolean} anticlockwise
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
+ return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise));
+ };
+
+ /**
+ * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed
+ * information, read the
+ * whatwg spec. A tiny API method "qt" also exists.
+ * @method quadraticCurveTo
+ * @param {Number} cpx
+ * @param {Number} cpy
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.quadraticCurveTo = function(cpx, cpy, x, y) {
+ return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y));
+ };
+
+ /**
+ * Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x,
+ * cp2y). For detailed information, read the
+ *
+ * whatwg spec. A tiny API method "bt" also exists.
+ * @method bezierCurveTo
+ * @param {Number} cp1x
+ * @param {Number} cp1y
+ * @param {Number} cp2x
+ * @param {Number} cp2y
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
+ return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y));
+ };
+
+ /**
+ * Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke.
+ * For detailed information, read the
+ *
+ * whatwg spec. A tiny API method "r" also exists.
+ * @method rect
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w Width of the rectangle
+ * @param {Number} h Height of the rectangle
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.rect = function(x, y, w, h) {
+ return this.append(new G.Rect(x, y, w, h));
+ };
+
+ /**
+ * Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified
+ * since the fill or stroke was last set. A tiny API method "cp" also exists.
+ * @method closePath
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.closePath = function() {
+ return this._activeInstructions.length ? this.append(new G.ClosePath()) : this;
+ };
+
+
+// public methods that roughly map to Adobe Flash/Animate graphics APIs:
+ /**
+ * Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need
+ * to be redefined to draw shapes following a clear call. A tiny API method "c" also exists.
+ * @method clear
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.clear = function() {
+ this._instructions.length = this._activeInstructions.length = this._commitIndex = 0;
+ this._strokeStyle = this._oldStrokeStyle = this._stroke = this._fill = this._strokeDash = this._oldStrokeDash = null;
+ this._dirty = this._strokeIgnoreScale = false;
+ return this;
+ };
+
+ /**
+ * Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists.
+ * @method beginFill
+ * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
+ * null will result in no fill.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginFill = function(color) {
+ return this._setFill(color ? new G.Fill(color) : null);
+ };
+
+ /**
+ * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
+ * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
+ * square to display it:
+ *
+ * myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
+ *
+ * A tiny API method "lf" also exists.
+ * @method beginLinearGradientFill
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
+ * drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
+ * the first color to 10% then interpolating to the second color at 90%.
+ * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
+ * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) {
+ return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1));
+ };
+
+ /**
+ * Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to
+ * blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it:
+ *
+ * myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50);
+ *
+ * A tiny API method "rf" also exists.
+ * @method beginRadialGradientFill
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
+ * a gradient drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
+ * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
+ * @param {Number} x0 Center position of the inner circle that defines the gradient.
+ * @param {Number} y0 Center position of the inner circle that defines the gradient.
+ * @param {Number} r0 Radius of the inner circle that defines the gradient.
+ * @param {Number} x1 Center position of the outer circle that defines the gradient.
+ * @param {Number} y1 Center position of the outer circle that defines the gradient.
+ * @param {Number} r1 Radius of the outer circle that defines the gradient.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
+ return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
+ };
+
+ /**
+ * Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also
+ * exists.
+ * @method beginBitmapFill
+ * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
+ * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
+ * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
+ * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
+ * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
+ * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
+ * will be applied relative to the parent transform.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginBitmapFill = function(image, repetition, matrix) {
+ return this._setFill(new G.Fill(null,matrix).bitmap(image, repetition));
+ };
+
+ /**
+ * Ends the current sub-path, and begins a new one with no fill. Functionally identical to beginFill(null)
.
+ * A tiny API method "ef" also exists.
+ * @method endFill
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.endFill = function() {
+ return this.beginFill();
+ };
+
+ /**
+ * Sets the stroke style. Like all drawing methods, this can be chained, so you can define
+ * the stroke style and color in a single line of code like so:
+ *
+ * myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");
+ *
+ * A tiny API method "ss" also exists.
+ * @method setStrokeStyle
+ * @param {Number} thickness The width of the stroke.
+ * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
+ * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
+ * the tiny API.
+ * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
+ * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
+ * for use with the tiny API.
+ * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
+ * controls at what point a mitered joint will be clipped.
+ * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
+ * of active transformations.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) {
+ this._updateInstructions(true);
+ this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale);
+
+ // ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery:
+ if (this._stroke) { this._stroke.ignoreScale = ignoreScale; }
+ this._strokeIgnoreScale = ignoreScale;
+ return this;
+ };
+
+ /**
+ * Sets or clears the stroke dash pattern.
+ *
+ * myGraphics.setStrokeDash([20, 10], 0);
+ *
+ * A tiny API method `sd` also exists.
+ * @method setStrokeDash
+ * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
+ * For example, `[20,10]` would create a pattern of 20 pixel lines with 10 pixel gaps between them.
+ * Passing null or an empty array will clear the existing stroke dash.
+ * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.setStrokeDash = function(segments, offset) {
+ this._updateInstructions(true);
+ this._strokeDash = this.command = new G.StrokeDash(segments, offset);
+ return this;
+ };
+
+ /**
+ * Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists.
+ * @method beginStroke
+ * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
+ * null will result in no stroke.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginStroke = function(color) {
+ return this._setStroke(color ? new G.Stroke(color) : null);
+ };
+
+ /**
+ * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
+ * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
+ * square to display it:
+ *
+ * myGraphics.setStrokeStyle(10).
+ * beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
+ *
+ * A tiny API method "ls" also exists.
+ * @method beginLinearGradientStroke
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
+ * a gradient drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
+ * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
+ * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
+ * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) {
+ return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1));
+ };
+
+ /**
+ * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to
+ * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it:
+ *
+ * myGraphics.setStrokeStyle(10)
+ * .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50)
+ * .drawRect(50, 90, 150, 110);
+ *
+ * A tiny API method "rs" also exists.
+ * @method beginRadialGradientStroke
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
+ * a gradient drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
+ * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
+ * to 100%.
+ * @param {Number} x0 Center position of the inner circle that defines the gradient.
+ * @param {Number} y0 Center position of the inner circle that defines the gradient.
+ * @param {Number} r0 Radius of the inner circle that defines the gradient.
+ * @param {Number} x1 Center position of the outer circle that defines the gradient.
+ * @param {Number} y1 Center position of the outer circle that defines the gradient.
+ * @param {Number} r1 Radius of the outer circle that defines the gradient.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
+ return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
+ };
+
+ /**
+ * Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills,
+ * strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs"
+ * also exists.
+ * @method beginBitmapStroke
+ * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
+ * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
+ * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
+ * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.beginBitmapStroke = function(image, repetition) {
+ // NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width.
+ return this._setStroke(new G.Stroke().bitmap(image, repetition));
+ };
+
+ /**
+ * Ends the current sub-path, and begins a new one with no stroke. Functionally identical to beginStroke(null)
.
+ * A tiny API method "es" also exists.
+ * @method endStroke
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.endStroke = function() {
+ return this.beginStroke();
+ };
+
+ /**
+ * Maps the familiar ActionScript curveTo()
method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}}
+ * method.
+ * @method curveTo
+ * @param {Number} cpx
+ * @param {Number} cpy
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.curveTo = p.quadraticCurveTo;
+
+ /**
+ *
+ * Maps the familiar ActionScript drawRect()
method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}}
+ * method.
+ * @method drawRect
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w Width of the rectangle
+ * @param {Number} h Height of the rectangle
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.drawRect = p.rect;
+
+ /**
+ * Draws a rounded rectangle with all corners with the specified radius.
+ * @method drawRoundRect
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w
+ * @param {Number} h
+ * @param {Number} radius Corner radius.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.drawRoundRect = function(x, y, w, h, radius) {
+ return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius);
+ };
+
+ /**
+ * Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API
+ * method "rc" also exists.
+ * @method drawRoundRectComplex
+ * @param {Number} x The horizontal coordinate to draw the round rect.
+ * @param {Number} y The vertical coordinate to draw the round rect.
+ * @param {Number} w The width of the round rect.
+ * @param {Number} h The height of the round rect.
+ * @param {Number} radiusTL Top left corner radius.
+ * @param {Number} radiusTR Top right corner radius.
+ * @param {Number} radiusBR Bottom right corner radius.
+ * @param {Number} radiusBL Bottom left corner radius.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
+ return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL));
+ };
+
+ /**
+ * Draws a circle with the specified radius at (x, y).
+ *
+ * var g = new createjs.Graphics();
+ * g.setStrokeStyle(1);
+ * g.beginStroke(createjs.Graphics.getRGB(0,0,0));
+ * g.beginFill(createjs.Graphics.getRGB(255,0,0));
+ * g.drawCircle(0,0,3);
+ *
+ * var s = new createjs.Shape(g);
+ * s.x = 100;
+ * s.y = 100;
+ *
+ * stage.addChild(s);
+ * stage.update();
+ *
+ * A tiny API method "dc" also exists.
+ * @method drawCircle
+ * @param {Number} x x coordinate center point of circle.
+ * @param {Number} y y coordinate center point of circle.
+ * @param {Number} radius Radius of circle.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.drawCircle = function(x, y, radius) {
+ return this.append(new G.Circle(x, y, radius));
+ };
+
+ /**
+ * Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}},
+ * except the width and height can be different. A tiny API method "de" also exists.
+ * @method drawEllipse
+ * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
+ * which draws from center.
+ * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
+ * which draws from the center.
+ * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
+ * number.
+ * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.drawEllipse = function(x, y, w, h) {
+ return this.append(new G.Ellipse(x, y, w, h));
+ };
+
+ /**
+ * Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of
+ * points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a
+ * radius of 50:
+ *
+ * myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90);
+ * // Note: -90 makes the first point vertical
+ *
+ * A tiny API method "dp" also exists.
+ *
+ * @method drawPolyStar
+ * @param {Number} x Position of the center of the shape.
+ * @param {Number} y Position of the center of the shape.
+ * @param {Number} radius The outer radius of the shape.
+ * @param {Number} sides The number of points on the star or sides on the polygon.
+ * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
+ * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
+ * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
+ * directly to the right of the center.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) {
+ return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle));
+ };
+
+ /**
+ * Appends a graphics command object to the graphics queue. Command objects expose an "exec" method
+ * that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into
+ * {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw.
+ *
+ * This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert
+ * built-in or custom graphics commands. For example:
+ *
+ * // attach data to our shape, so we can access it during the draw:
+ * myShape.color = "red";
+ *
+ * // append a Circle command object:
+ * myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30));
+ *
+ * // append a custom command object with an exec method that sets the fill style
+ * // based on the shape's data, and then fills the circle.
+ * myShape.graphics.append({exec:function(ctx, shape) {
+ * ctx.fillStyle = shape.color;
+ * ctx.fill();
+ * }});
+ *
+ * @method append
+ * @param {Object} command A graphics command object exposing an "exec" method.
+ * @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.append = function(command, clean) {
+ this._activeInstructions.push(command);
+ this.command = command;
+ if (!clean) { this._dirty = true; }
+ return this;
+ };
+
+ /**
+ * Decodes a compact encoded path string into a series of draw instructions.
+ * This format is not intended to be human readable, and is meant for use by authoring tools.
+ * The format uses a base64 character set, with each character representing 6 bits, to define a series of draw
+ * commands.
+ *
+ * Each command is comprised of a single "header" character followed by a variable number of alternating x and y
+ * position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the
+ * type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4
+ * indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the
+ * latter. Bits 5 and 6 are currently unused.
+ *
+ * Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo)
+ * parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the
+ * 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed
+ * by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the
+ * case of move operations which are absolute, this value is a delta from the previous x or y position (as
+ * appropriate).
+ *
+ * For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0.
+ *
A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per
+ * parameter.
+ *
n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits
+ * indicate 1500 tenths of a pixel.
+ *
AA - 000000000000. Absolute y position of 0.
+ *
I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter.
+ *
Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to
+ * provide an absolute position of +150.0px.
+ *
AAA - 000000000000000000. A y delta value of 0.
+ *
+ * A tiny API method "p" also exists.
+ * @method decodePath
+ * @param {String} str The path string to decode.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.decodePath = function(str) {
+ var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath];
+ var paramCount = [2, 2, 4, 6, 0];
+ var i=0, l=str.length;
+ var params = [];
+ var x=0, y=0;
+ var base64 = Graphics.BASE_64;
+
+ while (i>3; // highest order bits 1-3 code for operation.
+ var f = instructions[fi];
+ // check that we have a valid instruction & that the unused bits are empty:
+ if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); }
+ var pl = paramCount[fi];
+ if (!fi) { x=y=0; } // move operations reset the position.
+ params.length = 0;
+ i++;
+ var charCount = (n>>2&1)+2; // 4th header bit indicates number size for this operation.
+ for (var p=0; p>5) ? -1 : 1;
+ num = ((num&31)<<6)|(base64[str.charAt(i+1)]);
+ if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); }
+ num = sign*num/10;
+ if (p%2) { x = (num += x); }
+ else { y = (num += y); }
+ params[p] = num;
+ i += charCount;
+ }
+ f.apply(this,params);
+ }
+ return this;
+ };
+
+ /**
+ * Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to
+ * the existing store. This also affects `drawAsPath()`.
+ *
+ * This is useful in cases where you are creating vector graphics in an iterative manner (ex. generative art), so
+ * that only new graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all
+ * of the vector instructions for later use (ex. scaling, modifying, or exporting).
+ *
+ * Note that calling store() will force the active path (if any) to be ended in a manner similar to changing
+ * the fill or stroke.
+ *
+ * For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of
+ * segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}},
+ * and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG.
+ *
+ * // set up cache:
+ * myShape.cache(0,0,500,500,scale);
+ *
+ * // when the user drags, draw a new line:
+ * myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY);
+ * // then draw it into the existing cache:
+ * myShape.updateCache("source-over");
+ * // store the new line, so it isn't redrawn next time:
+ * myShape.store();
+ *
+ * // then, when the window resizes, we can re-render at a different scale:
+ * // first, unstore all our lines:
+ * myShape.unstore();
+ * // then cache using the new scale:
+ * myShape.cache(0,0,500,500,newScale);
+ * // finally, store the existing commands again:
+ * myShape.store();
+ *
+ * @method store
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.store = function() {
+ this._updateInstructions(true);
+ this._storeIndex = this._instructions.length;
+ return this;
+ };
+
+ /**
+ * Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}}
+ * so that they will be executed in subsequent draw calls.
+ *
+ * @method unstore
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.unstore = function() {
+ this._storeIndex = 0;
+ return this;
+ };
+
+ /**
+ * Returns a clone of this Graphics instance. Note that the individual command objects are not cloned.
+ * @method clone
+ * @return {Graphics} A clone of the current Graphics instance.
+ **/
+ p.clone = function() {
+ var o = new Graphics();
+ o.command = this.command;
+ o._stroke = this._stroke;
+ o._strokeStyle = this._strokeStyle;
+ o._strokeDash = this._strokeDash;
+ o._strokeIgnoreScale = this._strokeIgnoreScale;
+ o._fill = this._fill;
+ o._instructions = this._instructions.slice();
+ o._commitIndex = this._commitIndex;
+ o._activeInstructions = this._activeInstructions.slice();
+ o._dirty = this._dirty;
+ o._storeIndex = this._storeIndex;
+ return o;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Graphics]";
+ };
+
+
+// tiny API:
+ /**
+ * Shortcut to moveTo.
+ * @method mt
+ * @param {Number} x The x coordinate the drawing point should move to.
+ * @param {Number} y The y coordinate the drawing point should move to.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
+ * @chainable
+ * @protected
+ **/
+ p.mt = p.moveTo;
+
+ /**
+ * Shortcut to lineTo.
+ * @method lt
+ * @param {Number} x The x coordinate the drawing point should draw to.
+ * @param {Number} y The y coordinate the drawing point should draw to.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.lt = p.lineTo;
+
+ /**
+ * Shortcut to arcTo.
+ * @method at
+ * @param {Number} x1
+ * @param {Number} y1
+ * @param {Number} x2
+ * @param {Number} y2
+ * @param {Number} radius
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.at = p.arcTo;
+
+ /**
+ * Shortcut to bezierCurveTo.
+ * @method bt
+ * @param {Number} cp1x
+ * @param {Number} cp1y
+ * @param {Number} cp2x
+ * @param {Number} cp2y
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.bt = p.bezierCurveTo;
+
+ /**
+ * Shortcut to quadraticCurveTo / curveTo.
+ * @method qt
+ * @param {Number} cpx
+ * @param {Number} cpy
+ * @param {Number} x
+ * @param {Number} y
+ * @protected
+ * @chainable
+ **/
+ p.qt = p.quadraticCurveTo;
+
+ /**
+ * Shortcut to arc.
+ * @method a
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} radius
+ * @param {Number} startAngle Measured in radians.
+ * @param {Number} endAngle Measured in radians.
+ * @param {Boolean} anticlockwise
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @protected
+ * @chainable
+ **/
+ p.a = p.arc;
+
+ /**
+ * Shortcut to rect.
+ * @method r
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w Width of the rectangle
+ * @param {Number} h Height of the rectangle
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.r = p.rect;
+
+ /**
+ * Shortcut to closePath.
+ * @method cp
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.cp = p.closePath;
+
+ /**
+ * Shortcut to clear.
+ * @method c
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.c = p.clear;
+
+ /**
+ * Shortcut to beginFill.
+ * @method f
+ * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
+ * null will result in no fill.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.f = p.beginFill;
+
+ /**
+ * Shortcut to beginLinearGradientFill.
+ * @method lf
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
+ * drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
+ * the first color to 10% then interpolating to the second color at 90%.
+ * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
+ * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.lf = p.beginLinearGradientFill;
+
+ /**
+ * Shortcut to beginRadialGradientFill.
+ * @method rf
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
+ * a gradient drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
+ * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
+ * @param {Number} x0 Center position of the inner circle that defines the gradient.
+ * @param {Number} y0 Center position of the inner circle that defines the gradient.
+ * @param {Number} r0 Radius of the inner circle that defines the gradient.
+ * @param {Number} x1 Center position of the outer circle that defines the gradient.
+ * @param {Number} y1 Center position of the outer circle that defines the gradient.
+ * @param {Number} r1 Radius of the outer circle that defines the gradient.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.rf = p.beginRadialGradientFill;
+
+ /**
+ * Shortcut to beginBitmapFill.
+ * @method bf
+ * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
+ * as the pattern.
+ * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
+ * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
+ * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
+ * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
+ * will be applied relative to the parent transform.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.bf = p.beginBitmapFill;
+
+ /**
+ * Shortcut to endFill.
+ * @method ef
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.ef = p.endFill;
+
+ /**
+ * Shortcut to setStrokeStyle.
+ * @method ss
+ * @param {Number} thickness The width of the stroke.
+ * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
+ * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
+ * the tiny API.
+ * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
+ * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
+ * for use with the tiny API.
+ * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
+ * controls at what point a mitered joint will be clipped.
+ * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
+ * of active transformations.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.ss = p.setStrokeStyle;
+
+ /**
+ * Shortcut to setStrokeDash.
+ * @method sd
+ * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
+ * For example, [20,10] would create a pattern of 20 pixel lines with 10 pixel gaps between them.
+ * Passing null or an empty array will clear any existing dash.
+ * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.sd = p.setStrokeDash;
+
+ /**
+ * Shortcut to beginStroke.
+ * @method s
+ * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
+ * null will result in no stroke.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.s = p.beginStroke;
+
+ /**
+ * Shortcut to beginLinearGradientStroke.
+ * @method ls
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
+ * a gradient drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
+ * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
+ * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
+ * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
+ * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.ls = p.beginLinearGradientStroke;
+
+ /**
+ * Shortcut to beginRadialGradientStroke.
+ * @method rs
+ * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
+ * a gradient drawing from red to blue.
+ * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
+ * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
+ * to 100%.
+ * @param {Number} x0 Center position of the inner circle that defines the gradient.
+ * @param {Number} y0 Center position of the inner circle that defines the gradient.
+ * @param {Number} r0 Radius of the inner circle that defines the gradient.
+ * @param {Number} x1 Center position of the outer circle that defines the gradient.
+ * @param {Number} y1 Center position of the outer circle that defines the gradient.
+ * @param {Number} r1 Radius of the outer circle that defines the gradient.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.rs = p.beginRadialGradientStroke;
+
+ /**
+ * Shortcut to beginBitmapStroke.
+ * @method bs
+ * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
+ * as the pattern.
+ * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
+ * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.bs = p.beginBitmapStroke;
+
+ /**
+ * Shortcut to endStroke.
+ * @method es
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.es = p.endStroke;
+
+ /**
+ * Shortcut to drawRect.
+ * @method dr
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w Width of the rectangle
+ * @param {Number} h Height of the rectangle
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.dr = p.drawRect;
+
+ /**
+ * Shortcut to drawRoundRect.
+ * @method rr
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w
+ * @param {Number} h
+ * @param {Number} radius Corner radius.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.rr = p.drawRoundRect;
+
+ /**
+ * Shortcut to drawRoundRectComplex.
+ * @method rc
+ * @param {Number} x The horizontal coordinate to draw the round rect.
+ * @param {Number} y The vertical coordinate to draw the round rect.
+ * @param {Number} w The width of the round rect.
+ * @param {Number} h The height of the round rect.
+ * @param {Number} radiusTL Top left corner radius.
+ * @param {Number} radiusTR Top right corner radius.
+ * @param {Number} radiusBR Bottom right corner radius.
+ * @param {Number} radiusBL Bottom left corner radius.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.rc = p.drawRoundRectComplex;
+
+ /**
+ * Shortcut to drawCircle.
+ * @method dc
+ * @param {Number} x x coordinate center point of circle.
+ * @param {Number} y y coordinate center point of circle.
+ * @param {Number} radius Radius of circle.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.dc = p.drawCircle;
+
+ /**
+ * Shortcut to drawEllipse.
+ * @method de
+ * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
+ * which draws from center.
+ * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
+ * which draws from the center.
+ * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
+ * number.
+ * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.de = p.drawEllipse;
+
+ /**
+ * Shortcut to drawPolyStar.
+ * @method dp
+ * @param {Number} x Position of the center of the shape.
+ * @param {Number} y Position of the center of the shape.
+ * @param {Number} radius The outer radius of the shape.
+ * @param {Number} sides The number of points on the star or sides on the polygon.
+ * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
+ * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
+ * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
+ * directly to the right of the center.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.dp = p.drawPolyStar;
+
+ /**
+ * Shortcut to decodePath.
+ * @method p
+ * @param {String} str The path string to decode.
+ * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
+ * @chainable
+ * @protected
+ **/
+ p.p = p.decodePath;
+
+
+// private methods:
+ /**
+ * @method _updateInstructions
+ * @param commit
+ * @protected
+ **/
+ p._updateInstructions = function(commit) {
+ var instr = this._instructions, active = this._activeInstructions, commitIndex = this._commitIndex;
+
+ if (this._dirty && active.length) {
+ instr.length = commitIndex; // remove old, uncommitted commands
+ instr.push(Graphics.beginCmd);
+
+ var l = active.length, ll = instr.length;
+ instr.length = ll+l;
+ for (var i=0; i= 2) {
+ var o = this.style = Graphics._ctx.createPattern(image, repetition || "");
+ o.props = {image: image, repetition: repetition, type: "bitmap"};
+ }
+ return this;
+ };
+ p.path = false;
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class Stroke
+ * @constructor
+ * @param {Object} style A valid Context2D fillStyle.
+ * @param {Boolean} ignoreScale
+ **/
+ /**
+ * A valid Context2D strokeStyle.
+ * @property style
+ * @type Object
+ */
+ /**
+ * @property ignoreScale
+ * @type Boolean
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ p = (G.Stroke = function(style, ignoreScale) {
+ this.style = style;
+ this.ignoreScale = ignoreScale;
+ }).prototype;
+ p.exec = function(ctx) {
+ if (!this.style) { return; }
+ ctx.strokeStyle = this.style;
+ if (this.ignoreScale) { ctx.save(); ctx.setTransform(1,0,0,1,0,0); }
+ ctx.stroke();
+ if (this.ignoreScale) { ctx.restore(); }
+ };
+ /**
+ * Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
+ * See {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} for more information.
+ * @method linearGradient
+ * @param {Array} colors
+ * @param {Array} ratios
+ * @param {Number} x0
+ * @param {Number} y0
+ * @param {Number} x1
+ * @param {Number} y1
+ * @return {Fill} Returns this Stroke object for chaining or assignment.
+ */
+ p.linearGradient = G.Fill.prototype.linearGradient;
+ /**
+ * Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
+ * See {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} for more information.
+ * @method radialGradient
+ * @param {Array} colors
+ * @param {Array} ratios
+ * @param {Number} x0
+ * @param {Number} y0
+ * @param {Number} r0
+ * @param {Number} x1
+ * @param {Number} y1
+ * @param {Number} r1
+ * @return {Fill} Returns this Stroke object for chaining or assignment.
+ */
+ p.radialGradient = G.Fill.prototype.radialGradient;
+ /**
+ * Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
+ * See {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} for more information.
+ * @method bitmap
+ * @param {HTMLImageElement} image
+ * @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
+ * @return {Fill} Returns this Stroke object for chaining or assignment.
+ */
+ p.bitmap = G.Fill.prototype.bitmap;
+ p.path = false;
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class StrokeStyle
+ * @constructor
+ * @param {Number} width
+ * @param {String} [caps=butt]
+ * @param {String} [joints=miter]
+ * @param {Number} [miterLimit=10]
+ * @param {Boolean} [ignoreScale=false]
+ **/
+ /**
+ * @property width
+ * @type Number
+ */
+ /**
+ * One of: butt, round, square
+ * @property caps
+ * @type String
+ */
+ /**
+ * One of: round, bevel, miter
+ * @property joints
+ * @type String
+ */
+ /**
+ * @property miterLimit
+ * @type Number
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ p = (G.StrokeStyle = function(width, caps, joints, miterLimit, ignoreScale) {
+ this.width = width;
+ this.caps = caps;
+ this.joints = joints;
+ this.miterLimit = miterLimit;
+ this.ignoreScale = ignoreScale;
+ }).prototype;
+ p.exec = function(ctx) {
+ ctx.lineWidth = (this.width == null ? "1" : this.width);
+ ctx.lineCap = (this.caps == null ? "butt" : (isNaN(this.caps) ? this.caps : Graphics.STROKE_CAPS_MAP[this.caps]));
+ ctx.lineJoin = (this.joints == null ? "miter" : (isNaN(this.joints) ? this.joints : Graphics.STROKE_JOINTS_MAP[this.joints]));
+ ctx.miterLimit = (this.miterLimit == null ? "10" : this.miterLimit);
+ ctx.ignoreScale = (this.ignoreScale == null ? false : this.ignoreScale);
+ };
+ p.path = false;
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class StrokeDash
+ * @constructor
+ * @param {Array} [segments]
+ * @param {Number} [offset=0]
+ **/
+ /**
+ * @property segments
+ * @type Array
+ */
+ /**
+ * @property offset
+ * @type Number
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ (G.StrokeDash = function(segments, offset) {
+ this.segments = segments;
+ this.offset = offset||0;
+ }).prototype.exec = function(ctx) {
+ if (ctx.setLineDash) { // feature detection.
+ ctx.setLineDash(this.segments|| G.StrokeDash.EMPTY_SEGMENTS); // instead of [] to reduce churn.
+ ctx.lineDashOffset = this.offset||0;
+ }
+ };
+ /**
+ * The default value for segments (ie. no dash).
+ * @property EMPTY_SEGMENTS
+ * @static
+ * @final
+ * @readonly
+ * @protected
+ * @type {Array}
+ **/
+ G.StrokeDash.EMPTY_SEGMENTS = [];
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class RoundRect
+ * @constructor
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w
+ * @param {Number} h
+ * @param {Number} radiusTL
+ * @param {Number} radiusTR
+ * @param {Number} radiusBR
+ * @param {Number} radiusBL
+ **/
+ /**
+ * @property x
+ * @type Number
+ */
+ /**
+ * @property y
+ * @type Number
+ */
+ /**
+ * @property w
+ * @type Number
+ */
+ /**
+ * @property h
+ * @type Number
+ */
+ /**
+ * @property radiusTL
+ * @type Number
+ */
+ /**
+ * @property radiusTR
+ * @type Number
+ */
+ /**
+ * @property radiusBR
+ * @type Number
+ */
+ /**
+ * @property radiusBL
+ * @type Number
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ (G.RoundRect = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
+ this.x = x; this.y = y;
+ this.w = w; this.h = h;
+ this.radiusTL = radiusTL; this.radiusTR = radiusTR;
+ this.radiusBR = radiusBR; this.radiusBL = radiusBL;
+ }).prototype.exec = function(ctx) {
+ var max = (w max) { rTL = max; }
+ if (rTR < 0) { rTR *= (mTR=-1); }
+ if (rTR > max) { rTR = max; }
+ if (rBR < 0) { rBR *= (mBR=-1); }
+ if (rBR > max) { rBR = max; }
+ if (rBL < 0) { rBL *= (mBL=-1); }
+ if (rBL > max) { rBL = max; }
+
+ ctx.moveTo(x+w-rTR, y);
+ ctx.arcTo(x+w+rTR*mTR, y-rTR*mTR, x+w, y+rTR, rTR);
+ ctx.lineTo(x+w, y+h-rBR);
+ ctx.arcTo(x+w+rBR*mBR, y+h+rBR*mBR, x+w-rBR, y+h, rBR);
+ ctx.lineTo(x+rBL, y+h);
+ ctx.arcTo(x-rBL*mBL, y+h+rBL*mBL, x, y+h-rBL, rBL);
+ ctx.lineTo(x, y+rTL);
+ ctx.arcTo(x-rTL*mTL, y-rTL*mTL, x+rTL, y, rTL);
+ ctx.closePath();
+ };
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class Circle
+ * @constructor
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} radius
+ **/
+ /**
+ * @property x
+ * @type Number
+ */
+ /**
+ * @property y
+ * @type Number
+ */
+ /**
+ * @property radius
+ * @type Number
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ (G.Circle = function(x, y, radius) {
+ this.x = x; this.y = y;
+ this.radius = radius;
+ }).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); };
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class Ellipse
+ * @constructor
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} w
+ * @param {Number} h
+ **/
+ /**
+ * @property x
+ * @type Number
+ */
+ /**
+ * @property y
+ * @type Number
+ */
+ /**
+ * @property w
+ * @type Number
+ */
+ /**
+ * @property h
+ * @type Number
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ (G.Ellipse = function(x, y, w, h) {
+ this.x = x; this.y = y;
+ this.w = w; this.h = h;
+ }).prototype.exec = function(ctx) {
+ var x = this.x, y = this.y;
+ var w = this.w, h = this.h;
+
+ var k = 0.5522848;
+ var ox = (w / 2) * k;
+ var oy = (h / 2) * k;
+ var xe = x + w;
+ var ye = y + h;
+ var xm = x + w / 2;
+ var ym = y + h / 2;
+
+ ctx.moveTo(x, ym);
+ ctx.bezierCurveTo(x, ym-oy, xm-ox, y, xm, y);
+ ctx.bezierCurveTo(xm+ox, y, xe, ym-oy, xe, ym);
+ ctx.bezierCurveTo(xe, ym+oy, xm+ox, ye, xm, ye);
+ ctx.bezierCurveTo(xm-ox, ye, x, ym+oy, x, ym);
+ };
+
+ /**
+ * Graphics command object. See {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
+ * @class PolyStar
+ * @constructor
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} radius
+ * @param {Number} sides
+ * @param {Number} pointSize
+ * @param {Number} angle
+ **/
+ /**
+ * @property x
+ * @type Number
+ */
+ /**
+ * @property y
+ * @type Number
+ */
+ /**
+ * @property radius
+ * @type Number
+ */
+ /**
+ * @property sides
+ * @type Number
+ */
+ /**
+ * @property pointSize
+ * @type Number
+ */
+ /**
+ * @property angle
+ * @type Number
+ */
+ /**
+ * Execute the Graphics command in the provided Canvas context.
+ * @method exec
+ * @param {CanvasRenderingContext2D} ctx The canvas rendering context
+ */
+ (G.PolyStar = function(x, y, radius, sides, pointSize, angle) {
+ this.x = x; this.y = y;
+ this.radius = radius;
+ this.sides = sides;
+ this.pointSize = pointSize;
+ this.angle = angle;
+ }).prototype.exec = function(ctx) {
+ var x = this.x, y = this.y;
+ var radius = this.radius;
+ var angle = (this.angle||0)/180*Math.PI;
+ var sides = this.sides;
+ var ps = 1-(this.pointSize||0);
+ var a = Math.PI/sides;
+
+ ctx.moveTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius);
+ for (var i=0; iNote: In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please
+ * check out the latest NEXT version in GitHub for an updated version with this issue resolved. The fix will be
+ * provided in the next release of EaselJS.
+ * @property mouseEnabled
+ * @type {Boolean}
+ * @default true
+ **/
+ this.mouseEnabled = true;
+
+ /**
+ * If false, the tick will not run on this display object (or its children). This can provide some performance benefits.
+ * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
+ * on some display objects (ex. Sprite & MovieClip frame advancing, and DOMElement display properties).
+ * @property tickEnabled
+ * @type Boolean
+ * @default true
+ **/
+ this.tickEnabled = true;
+
+ /**
+ * An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for
+ * debugging.
+ * @property name
+ * @type {String}
+ * @default null
+ **/
+ this.name = null;
+
+ /**
+ * A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that
+ * contains this display object, or null if it has not been added
+ * to one.
+ * @property parent
+ * @final
+ * @type {Container}
+ * @default null
+ * @readonly
+ **/
+ this.parent = null;
+
+ /**
+ * The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate
+ * around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50.
+ * Cached object's registration points should be set based on pre-cache conditions, not cached size.
+ * @property regX
+ * @type {Number}
+ * @default 0
+ **/
+ this.regX = 0;
+
+ /**
+ * The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around
+ * its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50.
+ * Cached object's registration points should be set based on pre-cache conditions, not cached size.
+ * @property regY
+ * @type {Number}
+ * @default 0
+ **/
+ this.regY = 0;
+
+ /**
+ * The rotation in degrees for this display object.
+ * @property rotation
+ * @type {Number}
+ * @default 0
+ **/
+ this.rotation = 0;
+
+ /**
+ * The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display
+ * object to twice its nominal width. To horizontally flip an object, set the scale to a negative number.
+ * @property scaleX
+ * @type {Number}
+ * @default 1
+ **/
+ this.scaleX = 1;
+
+ /**
+ * The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display
+ * object to half its nominal height. To vertically flip an object, set the scale to a negative number.
+ * @property scaleY
+ * @type {Number}
+ * @default 1
+ **/
+ this.scaleY = 1;
+
+ /**
+ * The factor to skew this display object horizontally.
+ * @property skewX
+ * @type {Number}
+ * @default 0
+ **/
+ this.skewX = 0;
+
+ /**
+ * The factor to skew this display object vertically.
+ * @property skewY
+ * @type {Number}
+ * @default 0
+ **/
+ this.skewY = 0;
+
+ /**
+ * A shadow object that defines the shadow to render on this display object. Set to `null` to remove a shadow. If
+ * null, this property is inherited from the parent container.
+ * @property shadow
+ * @type {Shadow}
+ * @default null
+ **/
+ this.shadow = null;
+
+ /**
+ * Indicates whether this display object should be rendered to the canvas and included when running the Stage
+ * {{#crossLink "Stage/getObjectsUnderPoint"}}{{/crossLink}} method.
+ * @property visible
+ * @type {Boolean}
+ * @default true
+ **/
+ this.visible = true;
+
+ /**
+ * The x (horizontal) position of the display object, relative to its parent.
+ * @property x
+ * @type {Number}
+ * @default 0
+ **/
+ this.x = 0;
+
+ /** The y (vertical) position of the display object, relative to its parent.
+ * @property y
+ * @type {Number}
+ * @default 0
+ **/
+ this.y = 0;
+
+ /**
+ * If set, defines the transformation for this display object, overriding all other transformation properties
+ * (x, y, rotation, scale, skew).
+ * @property transformMatrix
+ * @type {Matrix2D}
+ * @default null
+ **/
+ this.transformMatrix = null;
+
+ /**
+ * The composite operation indicates how the pixels of this display object will be composited with the elements
+ * behind it. If `null`, this property is inherited from the parent container. For more information, read the
+ *
+ * whatwg spec on compositing. For a list of supported compositeOperation value, visit
+ * the W3C draft on Compositing and Blending.
+ * @property compositeOperation
+ * @type {String}
+ * @default null
+ **/
+ this.compositeOperation = null;
+
+ /**
+ * Indicates whether the display object should be drawn to a whole pixel when
+ * {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole
+ * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true).
+ * @property snapToPixel
+ * @type {Boolean}
+ * @default true
+ **/
+ this.snapToPixel = true;
+
+ /**
+ * An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}}
+ * or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is
+ * cached.
+ * @property filters
+ * @type {Array}
+ * @default null
+ **/
+ this.filters = null;
+
+ /**
+ * A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation
+ * will be applied relative to the display object's parent coordinates (as if it were a child of the parent).
+ * @property mask
+ * @type {Shape}
+ * @default null
+ */
+ this.mask = null;
+
+ /**
+ * A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}.
+ * The hit area will have its transformation applied relative to this display object's coordinate space (as though
+ * the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested
+ * using only its own `alpha` value regardless of the alpha value on the target display object, or the target's
+ * ancestors (parents).
+ *
+ * If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events.
+ * This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false.
+ *
+ * Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}.
+ * @property hitArea
+ * @type {DisplayObject}
+ * @default null
+ */
+ this.hitArea = null;
+
+ /**
+ * A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display
+ * object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to
+ * use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants.
+ * @property cursor
+ * @type {String}
+ * @default null
+ */
+ this.cursor = null;
+
+
+ // private properties:
+ /**
+ * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
+ * @property _cacheScale
+ * @protected
+ * @type {Number}
+ * @default 1
+ * @deprecated
+ **/
+
+ /**
+ * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
+ * @property _cacheDataURLID
+ * @protected
+ * @type {Number}
+ * @default 0
+ * @deprecated
+ */
+
+ /**
+ * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
+ * @property _cacheDataURL
+ * @protected
+ * @type {String}
+ * @default null
+ * @deprecated
+ */
+
+ /**
+ * @property _props
+ * @protected
+ * @type {DisplayObject}
+ * @default null
+ **/
+ this._props = new createjs.DisplayProps();
+
+ /**
+ * @property _rectangle
+ * @protected
+ * @type {Rectangle}
+ * @default null
+ **/
+ this._rectangle = new createjs.Rectangle();
+
+ /**
+ * @property _bounds
+ * @protected
+ * @type {Rectangle}
+ * @default null
+ **/
+ this._bounds = null;
+
+ /**
+ * Where StageGL should look for required display properties, matters only for leaf display objects. Containers
+ * or cached objects won't use this property, it's for native display of terminal elements.
+ * @property _webGLRenderStyle
+ * @protected
+ * @type {number}
+ * @default 0
+ */
+ this._webGLRenderStyle = DisplayObject._StageGL_NONE;
+ }
+ var p = createjs.extend(DisplayObject, createjs.EventDispatcher);
+
+// static properties:
+ /**
+ * Listing of mouse event names. Used in _hasMouseEventListener.
+ * @property _MOUSE_EVENTS
+ * @protected
+ * @static
+ * @type {Array}
+ **/
+ DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"];
+
+ /**
+ * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}}
+ * with cross domain content.
+ * @property suppressCrossDomainErrors
+ * @static
+ * @type {Boolean}
+ * @default false
+ **/
+ DisplayObject.suppressCrossDomainErrors = false;
+
+ /**
+ * @property _snapToPixelEnabled
+ * @protected
+ * @static
+ * @type {Boolean}
+ * @default false
+ **/
+ DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access.
+
+ /**
+ * Enum like property for determining StageGL render lookup, i.e. where to expect properties.
+ * @property _StageGL_NONE
+ * @protected
+ * @static
+ * @type {number}
+ */
+ DisplayObject._StageGL_NONE = 0;
+
+ /**
+ * Enum like property for determining StageGL render lookup, i.e. where to expect properties.
+ * @property _StageGL_SPRITE
+ * @protected
+ * @static
+ * @type {number}
+ */
+ DisplayObject._StageGL_SPRITE = 1;
+
+ /**
+ * Enum like property for determining StageGL render lookup, i.e. where to expect properties.
+ * @property _StageGL_BITMAP
+ * @protected
+ * @static
+ * @type {number}
+ */
+ DisplayObject._StageGL_BITMAP = 2;
+
+ /**
+ * @property _hitTestCanvas
+ * @type {HTMLCanvasElement | Object}
+ * @static
+ * @protected
+ **/
+ /**
+ * @property _hitTestContext
+ * @type {CanvasRenderingContext2D}
+ * @static
+ * @protected
+ **/
+ var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas.
+ if (canvas.getContext) {
+ DisplayObject._hitTestCanvas = canvas;
+ DisplayObject._hitTestContext = canvas.getContext("2d");
+ canvas.width = canvas.height = 1;
+ }
+
+// events:
+ /**
+ * Dispatched when the user presses their left mouse button over the display object. See the
+ * {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event mousedown
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the user presses their left mouse button and then releases it while over the display object.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event click
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the user double clicks their left mouse button over this display object.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event dblclick
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the user's mouse enters this display object. This event must be enabled using
+ * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event mouseover
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the user's mouse leaves this display object. This event must be enabled using
+ * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event mouseout
+ * @since 0.6.0
+ */
+
+ /**
+ * This event is similar to {{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}, with the following
+ * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
+ * aggregate of their content.
+ *
+ * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
+ * shapeA and then directly on to shapeB. With a listener for {{#crossLink "mouseover:event"}}{{/crossLink}} on
+ * myContainer, two events would be received, each targeting a child element:
+ * - when the mouse enters shapeA (target=shapeA)
+ * - when the mouse enters shapeB (target=shapeB)
+ *
+ * However, with a listener for "rollover" instead, only a single event is received when the mouse first enters
+ * the aggregate myContainer content (target=myContainer).
+ *
+ * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event rollover
+ * @since 0.7.0
+ */
+
+ /**
+ * This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following
+ * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
+ * aggregate of their content.
+ *
+ * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
+ * shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}}
+ * on myContainer, two events would be received, each targeting a child element:
+ * - when the mouse leaves shapeA (target=shapeA)
+ * - when the mouse leaves shapeB (target=shapeB)
+ *
+ * However, with a listener for "rollout" instead, only a single event is received when the mouse leaves
+ * the aggregate myContainer content (target=myContainer).
+ *
+ * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event rollout
+ * @since 0.7.0
+ */
+
+ /**
+ * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove
+ * event will be generated on that object whenever the mouse moves until the mouse press is released. This can be
+ * useful for dragging and similar operations.
+ *
+ * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed
+ * (e.g. during a `pressmove` event), a `pressmove` event is still generated. However since it is no longer in the
+ * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive
+ * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressup:event"}}{{/crossLink}}`
+ * or `pressmove` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}),
+ * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`.
+ * @event pressmove
+ * @since 0.7.0
+ */
+
+ /**
+ * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event
+ * will be generated on that object when that mouse press is released. This can be useful for dragging and similar
+ * operations.
+ *
+ * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed
+ * (e.g. during a `pressmove` event), a `pressup` event is still generated. However since it is no longer in the
+ * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive
+ * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressmove:event"}}{{/crossLink}}`
+ * or `pressup` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}),
+ * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`.
+ * @event pressup
+ * @since 0.7.0
+ */
+
+ /**
+ * Dispatched when the display object is added to a parent container.
+ * @event added
+ */
+
+ /**
+ * Dispatched when the display object is removed from its parent container.
+ * @event removed
+ */
+
+ /**
+ * Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the
+ * rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on
+ * the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their
+ * {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being
+ * dispatched on their parent.
+ * @event tick
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For
+ * example if you called stage.update("hello"), then the params would be ["hello"].
+ * @since 0.6.0
+ */
+
+
+// getter / setters:
+ /**
+ * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead.
+ * @method _getStage
+ * @protected
+ * @return {Stage}
+ **/
+ p._getStage = function() {
+ // uses dynamic access to avoid circular dependencies;
+ var o = this, _Stage = createjs["Stage"];
+ while (o.parent) { o = o.parent; }
+ if (o instanceof _Stage) { return o; }
+ return null;
+ };
+ // DisplayObject.getStage is @deprecated. Remove for 1.1+
+ p.getStage = createjs.deprecate(p._getStage, "DisplayObject.getStage");
+
+ /**
+ * Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one.
+ * @property stage
+ * @type {Stage}
+ * @readonly
+ **/
+
+ /**
+ * Returns an ID number that uniquely identifies the current cache for this display object. This can be used to
+ * determine if the cache has changed since a previous check.
+ * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
+ * @property cacheID
+ * @deprecated
+ * @type {Number}
+ * @default 0
+ */
+
+ /**
+ * Set both the {{#crossLink "DisplayObject/scaleX:property"}}{{/crossLink}} and the {{#crossLink "DisplayObject/scaleY"}}{{/crossLink}}
+ * property to the same value. Note that when you get the value, if the `scaleX` and `scaleY` are different values,
+ * it will return only the `scaleX`.
+ * @property scaleX
+ * @type {Number}
+ * @default 1
+ */
+ try {
+ Object.defineProperties(p, {
+ stage: { get: p._getStage },
+ cacheID: {
+ get: function(){ return this.bitmapCache && this.bitmapCache.cacheID },
+ set: function(a){ this.bitmapCache && (this.bitmapCache.cacheID = a) }
+ },
+ scale: {
+ get: function() { return this.scaleX; },
+ set: function(scale) { this.scaleX = this.scaleY = scale; },
+ }
+ });
+ } catch (e) {}
+
+
+// public methods:
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
+ };
+
+ /**
+ * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true
if the draw was handled (useful for overriding functionality).
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
+ * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
+ * @return {Boolean}
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ var cache = this.bitmapCache;
+ if(cache && !ignoreCache) {
+ return cache.draw(ctx);
+ }
+ return false;
+ };
+
+ /**
+ * Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow
+ * to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}.
+ * @method updateContext
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D to update.
+ **/
+ p.updateContext = function(ctx) {
+ var o=this, mask=o.mask, mtx= o._props.matrix;
+
+ if (mask && mask.graphics && !mask.graphics.isEmpty()) {
+ mask.getMatrix(mtx);
+ ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
+
+ mask.graphics.drawAsPath(ctx);
+ ctx.clip();
+
+ mtx.invert();
+ ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
+ }
+
+ this.getMatrix(mtx);
+ var tx = mtx.tx, ty = mtx.ty;
+ if (DisplayObject._snapToPixelEnabled && o.snapToPixel) {
+ tx = tx + (tx < 0 ? -0.5 : 0.5) | 0;
+ ty = ty + (ty < 0 ? -0.5 : 0.5) | 0;
+ }
+ ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty);
+ ctx.globalAlpha *= o.alpha;
+ if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; }
+ if (o.shadow) { this._applyShadow(ctx, o.shadow); }
+ };
+
+ /**
+ * Draws the display object into a new element, which is then used for subsequent draws. Intended for complex content
+ * that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape),
+ * this can provide for much faster rendering because the content does not need to be re-rendered each tick. The
+ * cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must manually
+ * update the cache by calling updateCache()
again. You must specify the cached area via the x, y, w,
+ * and h parameters. This defines the rectangle that will be rendered and cached using this display object's coordinates.
+ *
+ * Example
+ * For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25:
+ *
+ * var shape = new createjs.Shape();
+ * shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25);
+ * shape.cache(-25, -25, 50, 50);
+ *
+ * Note that filters need to be defined before the cache is applied or you will have to call updateCache after
+ * application. Check out the {{#crossLink "Filter"}}{{/crossLink}} class for more information. Some filters
+ * (ex. BlurFilter) may not work as expected in conjunction with the scale param.
+ *
+ * Usually, the resulting cacheCanvas will have the dimensions width * scale, height * scale, however some filters (ex. BlurFilter)
+ * will add padding to the canvas dimensions.
+ *
+ * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}.
+ * This allows for easier interaction and alternate cache methods like WebGL with {{#crossLink "StageGL"}}{{/crossLink}}.
+ * For more information on the options object, see the BitmapCache {{#crossLink "BitmapCache/define"}}{{/crossLink}}.
+ *
+ * @method cache
+ * @param {Number} x The x coordinate origin for the cache region.
+ * @param {Number} y The y coordinate origin for the cache region.
+ * @param {Number} width The width of the cache region.
+ * @param {Number} height The height of the cache region.
+ * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using
+ * myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate
+ * cached elements with greater fidelity. Default is 1.
+ * @param {Object} [options=undefined] Specify additional parameters for the cache logic
+ **/
+ p.cache = function(x, y, width, height, scale, options) {
+ if(!this.bitmapCache){
+ this.bitmapCache = new createjs.BitmapCache();
+ }
+ this.bitmapCache.define(this, x, y, width, height, scale, options);
+ };
+
+ /**
+ * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error.
+ * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object
+ * will be drawn over the existing cache using the specified compositeOperation.
+ *
+ * Example
+ * Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line
+ * will be drawn on top of the old one.
+ *
+ * // Not shown: Creating the shape, and caching it.
+ * shapeInstance.clear();
+ * shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200);
+ * shapeInstance.updateCache();
+ *
+ * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}.
+ * This allows for easier interaction and alternate cache methods like WebGL and {{#crossLink "StageGL"}}{{/crossLink}}.
+ *
+ * @method updateCache
+ * @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it.
+ *
+ * whatwg spec on compositing.
+ **/
+ p.updateCache = function(compositeOperation) {
+ if(!this.bitmapCache) {
+ throw "cache() must be called before updateCache()";
+ }
+ this.bitmapCache.update(compositeOperation);
+ };
+
+ /**
+ * Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information.
+ * @method uncache
+ **/
+ p.uncache = function() {
+ if(this.bitmapCache) {
+ this.bitmapCache.release();
+ this.bitmapCache = undefined;
+ }
+ };
+
+ /**
+ * Returns a data URL for the cache, or null if this display object is not cached.
+ * Only generated if the cache has changed, otherwise returns last result.
+ * @method getCacheDataURL
+ * @return {String} The image data url for the cache.
+ **/
+ p.getCacheDataURL = function() {
+ return this.bitmapCache?this.bitmapCache.getCacheDataURL():null;
+ };
+
+ /**
+ * Transforms the specified x and y position from the coordinate space of the display object
+ * to the global (stage) coordinate space. For example, this could be used to position an HTML label
+ * over a specific point on a nested display object. Returns a Point instance with x and y properties
+ * correlating to the transformed coordinates on the stage.
+ *
+ * Example
+ *
+ * displayObject.x = 300;
+ * displayObject.y = 200;
+ * stage.addChild(displayObject);
+ * var point = displayObject.localToGlobal(100, 100);
+ * // Results in x=400, y=300
+ *
+ * @method localToGlobal
+ * @param {Number} x The x position in the source display object to transform.
+ * @param {Number} y The y position in the source display object to transform.
+ * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
+ * @return {Point} A Point instance with x and y properties correlating to the transformed coordinates
+ * on the stage.
+ **/
+ p.localToGlobal = function(x, y, pt) {
+ return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x,y, pt||new createjs.Point());
+ };
+
+ /**
+ * Transforms the specified x and y position from the global (stage) coordinate space to the
+ * coordinate space of the display object. For example, this could be used to determine
+ * the current mouse position within the display object. Returns a Point instance with x and y properties
+ * correlating to the transformed position in the display object's coordinate space.
+ *
+ * Example
+ *
+ * displayObject.x = 300;
+ * displayObject.y = 200;
+ * stage.addChild(displayObject);
+ * var point = displayObject.globalToLocal(100, 100);
+ * // Results in x=-200, y=-100
+ *
+ * @method globalToLocal
+ * @param {Number} x The x position on the stage to transform.
+ * @param {Number} y The y position on the stage to transform.
+ * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
+ * @return {Point} A Point instance with x and y properties correlating to the transformed position in the
+ * display object's coordinate space.
+ **/
+ p.globalToLocal = function(x, y, pt) {
+ return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x,y, pt||new createjs.Point());
+ };
+
+ /**
+ * Transforms the specified x and y position from the coordinate space of this display object to the coordinate
+ * space of the target display object. Returns a Point instance with x and y properties correlating to the
+ * transformed position in the target's coordinate space. Effectively the same as using the following code with
+ * {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
+ *
+ * var pt = this.localToGlobal(x, y);
+ * pt = target.globalToLocal(pt.x, pt.y);
+ *
+ * @method localToLocal
+ * @param {Number} x The x position in the source display object to transform.
+ * @param {Number} y The y position on the source display object to transform.
+ * @param {DisplayObject} target The target display object to which the coordinates will be transformed.
+ * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
+ * @return {Point} Returns a Point instance with x and y properties correlating to the transformed position
+ * in the target's coordinate space.
+ **/
+ p.localToLocal = function(x, y, target, pt) {
+ pt = this.localToGlobal(x, y, pt);
+ return target.globalToLocal(pt.x, pt.y, pt);
+ };
+
+ /**
+ * Shortcut method to quickly set the transform properties on the display object. All parameters are optional.
+ * Omitted parameters will have the default value set.
+ *
+ * Example
+ *
+ * displayObject.setTransform(100, 100, 2, 2);
+ *
+ * @method setTransform
+ * @param {Number} [x=0] The horizontal translation (x position) in pixels
+ * @param {Number} [y=0] The vertical translation (y position) in pixels
+ * @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1
+ * @param {Number} [scaleY=1] the vertical scale, as a percentage of 1
+ * @param {Number} [rotation=0] The rotation, in degrees
+ * @param {Number} [skewX=0] The horizontal skew factor
+ * @param {Number} [skewY=0] The vertical skew factor
+ * @param {Number} [regX=0] The horizontal registration point in pixels
+ * @param {Number} [regY=0] The vertical registration point in pixels
+ * @return {DisplayObject} Returns this instance. Useful for chaining commands.
+ * @chainable
+ */
+ p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
+ this.x = x || 0;
+ this.y = y || 0;
+ this.scaleX = scaleX == null ? 1 : scaleX;
+ this.scaleY = scaleY == null ? 1 : scaleY;
+ this.rotation = rotation || 0;
+ this.skewX = skewX || 0;
+ this.skewY = skewY || 0;
+ this.regX = regX || 0;
+ this.regY = regY || 0;
+ return this;
+ };
+
+ /**
+ * Returns a matrix based on this object's current transform.
+ * @method getMatrix
+ * @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new
+ * Matrix object is returned.
+ * @return {Matrix2D} A matrix representing this display object's transform.
+ **/
+ p.getMatrix = function(matrix) {
+ var o = this, mtx = matrix&&matrix.identity() || new createjs.Matrix2D();
+ return o.transformMatrix ? mtx.copy(o.transformMatrix) : mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
+ };
+
+ /**
+ * Generates a Matrix2D object representing the combined transform of the display object and all of its
+ * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can
+ * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
+ * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
+ * @method getConcatenatedMatrix
+ * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values.
+ * If null, a new Matrix2D object is returned.
+ * @return {Matrix2D} The combined matrix.
+ **/
+ p.getConcatenatedMatrix = function(matrix) {
+ var o = this, mtx = this.getMatrix(matrix);
+ while (o = o.parent) {
+ mtx.prependMatrix(o.getMatrix(o._props.matrix));
+ }
+ return mtx;
+ };
+
+ /**
+ * Generates a DisplayProps object representing the combined display properties of the object and all of its
+ * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}).
+ * @method getConcatenatedDisplayProps
+ * @param {DisplayProps} [props] A {{#crossLink "DisplayProps"}}{{/crossLink}} object to populate with the calculated values.
+ * If null, a new DisplayProps object is returned.
+ * @return {DisplayProps} The combined display properties.
+ **/
+ p.getConcatenatedDisplayProps = function(props) {
+ props = props ? props.identity() : new createjs.DisplayProps();
+ var o = this, mtx = o.getMatrix(props.matrix);
+ do {
+ props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation);
+
+ // we do this to avoid problems with the matrix being used for both operations when o._props.matrix is passed in as the props param.
+ // this could be simplified (ie. just done as part of the prepend above) if we switched to using a pool.
+ if (o != this) { mtx.prependMatrix(o.getMatrix(o._props.matrix)); }
+ } while (o = o.parent);
+ return props;
+ };
+
+ /**
+ * Tests whether the display object intersects the specified point in local coordinates (ie. draws a pixel with alpha > 0 at
+ * the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object.
+ *
+ * Example
+ *
+ * stage.addEventListener("stagemousedown", handleMouseDown);
+ * function handleMouseDown(event) {
+ * var hit = myShape.hitTest(event.stageX, event.stageY);
+ * }
+ *
+ * Please note that shape-to-shape collision is not currently supported by EaselJS.
+ * @method hitTest
+ * @param {Number} x The x position to check in the display object's local coordinates.
+ * @param {Number} y The y position to check in the display object's local coordinates.
+ * @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject intersect the specified
+ * local Point.
+ */
+ p.hitTest = function(x, y) {
+ var ctx = DisplayObject._hitTestContext;
+ ctx.setTransform(1, 0, 0, 1, -x, -y);
+ this.draw(ctx);
+
+ var hit = this._testHit(ctx);
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.clearRect(0, 0, 2, 2);
+ return hit;
+ };
+
+ /**
+ * Provides a chainable shortcut method for setting a number of properties on the instance.
+ *
+ * Example
+ *
+ * var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25);
+ * var shape = stage.addChild(new Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5});
+ *
+ * @method set
+ * @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
+ * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.)
+ * @chainable
+ */
+ p.set = function(props) {
+ for (var n in props) { this[n] = props[n]; }
+ return this;
+ };
+
+ /**
+ * Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation).
+ * Objects that have been cached will return the bounds of the cache.
+ *
+ * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
+ * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
+ * bounds.
+ *
+ *
+ * All |
+ * All display objects support setting bounds manually using setBounds(). Likewise, display objects that
+ * have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override
+ * the automatic calculations listed below.
+ * |
+ * Bitmap |
+ * Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0).
+ * |
+ * Sprite |
+ * Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified
+ * in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}}
+ * |
+ * Container |
+ * Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds().
+ * |
+ * Shape |
+ * Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds.
+ * |
+ * Text |
+ * Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are
+ * not, especially when using textBaseline values other than "top".
+ * |
+ * BitmapText |
+ * Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close
+ * to (x=0,y=0).
+ * |
+ *
+ *
+ * Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and
+ * are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the
+ * bounds explicitly:
+ *
+ * var bounds = obj.getBounds();
+ * obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
+ * // getBounds will now use the set values, instead of recalculating
+ *
+ * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
+ * values if you need to retain it.
+ *
+ * var myBounds = obj.getBounds().clone();
+ * // OR:
+ * myRect.copy(obj.getBounds());
+ *
+ * @method getBounds
+ * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this
+ * object.
+ **/
+ p.getBounds = function() {
+ if (this._bounds) { return this._rectangle.copy(this._bounds); }
+ var cacheCanvas = this.cacheCanvas;
+ if (cacheCanvas) {
+ var scale = this._cacheScale;
+ return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale);
+ }
+ return null;
+ };
+
+ /**
+ * Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied).
+ * Objects that have been cached will return the transformed bounds of the cache.
+ *
+ * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
+ * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
+ * bounds.
+ *
+ * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
+ * values if you need to retain it.
+ *
+ * Container instances calculate aggregate bounds for all children that return bounds via getBounds.
+ * @method getTransformedBounds
+ * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object.
+ **/
+ p.getTransformedBounds = function() {
+ return this._getBounds();
+ };
+
+ /**
+ * Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape &
+ * Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always
+ * override calculated bounds.
+ *
+ * The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance
+ * with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50).
+ * @method setBounds
+ * @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds.
+ * @param {Number} y The y origin of the bounds.
+ * @param {Number} width The width of the bounds.
+ * @param {Number} height The height of the bounds.
+ **/
+ p.setBounds = function(x, y, width, height) {
+ if (x == null) { this._bounds = x; return; }
+ this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height);
+ };
+
+ /**
+ * Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are
+ * reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements
+ * are copied by reference (masks, individual filter instances, hit area)
+ * @method clone
+ * @return {DisplayObject} A clone of the current DisplayObject instance.
+ **/
+ p.clone = function() {
+ return this._cloneProps(new DisplayObject());
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[DisplayObject (name="+ this.name +")]";
+ };
+
+
+// private methods:
+ /**
+ * Called before the object gets drawn and is a chance to ensure the display state of the object is correct.
+ * Mostly used by {{#crossLink "MovieClip"}}{{/crossLink}} and {{#crossLink "BitmapText"}}{{/crossLink}} to
+ * correct their internal state and children prior to being drawn.
+ *
+ * Is manually called via draw in a {{#crossLink "Stage"}}{{/crossLink}} but is automatically called when
+ * present in a {{#crossLink "StageGL"}}{{/crossLink}} instance.
+ *
+ * @method _updateState
+ * @default null
+ */
+ p._updateState = null;
+
+ // separated so it can be used more easily in subclasses:
+ /**
+ * @method _cloneProps
+ * @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject
+ * instance copied into.
+ * @return {DisplayObject} o
+ * @protected
+ **/
+ p._cloneProps = function(o) {
+ o.alpha = this.alpha;
+ o.mouseEnabled = this.mouseEnabled;
+ o.tickEnabled = this.tickEnabled;
+ o.name = this.name;
+ o.regX = this.regX;
+ o.regY = this.regY;
+ o.rotation = this.rotation;
+ o.scaleX = this.scaleX;
+ o.scaleY = this.scaleY;
+ o.shadow = this.shadow;
+ o.skewX = this.skewX;
+ o.skewY = this.skewY;
+ o.visible = this.visible;
+ o.x = this.x;
+ o.y = this.y;
+ o.compositeOperation = this.compositeOperation;
+ o.snapToPixel = this.snapToPixel;
+ o.filters = this.filters==null?null:this.filters.slice(0);
+ o.mask = this.mask;
+ o.hitArea = this.hitArea;
+ o.cursor = this.cursor;
+ o._bounds = this._bounds;
+ return o;
+ };
+
+ /**
+ * @method _applyShadow
+ * @protected
+ * @param {CanvasRenderingContext2D} ctx
+ * @param {Shadow} shadow
+ **/
+ p._applyShadow = function(ctx, shadow) {
+ shadow = shadow || Shadow.identity;
+ ctx.shadowColor = shadow.color;
+ ctx.shadowOffsetX = shadow.offsetX;
+ ctx.shadowOffsetY = shadow.offsetY;
+ ctx.shadowBlur = shadow.blur;
+ };
+
+ /**
+ * @method _tick
+ * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
+ * @protected
+ **/
+ p._tick = function(evtObj) {
+ // because tick can be really performance sensitive, check for listeners before calling dispatchEvent.
+ var ls = this._listeners;
+ if (ls && ls["tick"]) {
+ // reset & reuse the event object to avoid construction / GC costs:
+ evtObj.target = null;
+ evtObj.propagationStopped = evtObj.immediatePropagationStopped = false;
+ this.dispatchEvent(evtObj);
+ }
+ };
+
+ /**
+ * @method _testHit
+ * @protected
+ * @param {CanvasRenderingContext2D} ctx
+ * @return {Boolean}
+ **/
+ p._testHit = function(ctx) {
+ try {
+ var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
+ } catch (e) {
+ if (!DisplayObject.suppressCrossDomainErrors) {
+ throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
+ }
+ }
+ return hit;
+ };
+
+ /**
+ * @method _getBounds
+ * @param {Matrix2D} matrix
+ * @param {Boolean} ignoreTransform If true, does not apply this object's transform.
+ * @return {Rectangle}
+ * @protected
+ **/
+ p._getBounds = function(matrix, ignoreTransform){
+ return this._transformBounds(this.getBounds(), matrix, ignoreTransform);
+ };
+
+ /**
+ * @method _transformBounds
+ * @param {Rectangle} bounds
+ * @param {Matrix2D} matrix
+ * @param {Boolean} ignoreTransform
+ * @return {Rectangle}
+ * @protected
+ **/
+ p._transformBounds = function(bounds, matrix, ignoreTransform) {
+ if (!bounds) { return bounds; }
+ var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix;
+ mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
+
+ if (x || y) { mtx.appendTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this.
+ if (matrix) { mtx.prependMatrix(matrix); }
+
+ var x_a = width*mtx.a, x_b = width*mtx.b;
+ var y_c = height*mtx.c, y_d = height*mtx.d;
+ var tx = mtx.tx, ty = mtx.ty;
+
+ var minX = tx, maxX = tx, minY = ty, maxY = ty;
+
+ if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
+ if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
+ if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
+
+ if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
+ if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
+ if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
+
+ return bounds.setValues(minX, minY, maxX-minX, maxY-minY);
+ };
+
+ /**
+ * Indicates whether the display object has any mouse event listeners or a cursor.
+ * @method _isMouseOpaque
+ * @return {Boolean}
+ * @protected
+ **/
+ p._hasMouseEventListener = function() {
+ var evts = DisplayObject._MOUSE_EVENTS;
+ for (var i= 0, l=evts.length; itransform and alpha
properties concatenated with their parent
+ * Container.
+ *
+ * For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with x=50
+ * and alpha=0.7
will be rendered to the canvas at x=150
and alpha=0.35
.
+ * Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
+ *
+ * Example
+ *
+ * var container = new createjs.Container();
+ * container.addChild(bitmapInstance, shapeInstance);
+ * container.x = 100;
+ *
+ * @class Container
+ * @extends DisplayObject
+ * @constructor
+ **/
+ function Container() {
+ this.DisplayObject_constructor();
+
+ // public properties:
+ /**
+ * The array of children in the display list. You should usually use the child management methods such as
+ * {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}},
+ * {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is
+ * included for advanced uses.
+ * @property children
+ * @type Array
+ * @default null
+ **/
+ this.children = [];
+
+ /**
+ * Indicates whether the children of this container are independently enabled for mouse/pointer interaction.
+ * If false, the children will be aggregated under the container - for example, a click on a child shape would
+ * trigger a click event on the container.
+ * @property mouseChildren
+ * @type Boolean
+ * @default true
+ **/
+ this.mouseChildren = true;
+
+ /**
+ * If false, the tick will not be propagated to children of this Container. This can provide some performance benefits.
+ * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
+ * on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
+ * @property tickChildren
+ * @type Boolean
+ * @default true
+ **/
+ this.tickChildren = true;
+ }
+ var p = createjs.extend(Container, createjs.DisplayObject);
+
+
+// getter / setters:
+ /**
+ * Use the {{#crossLink "Container/numChildren:property"}}{{/crossLink}} property instead.
+ * @method _getNumChildren
+ * @protected
+ * @return {Number}
+ **/
+ p._getNumChildren = function() {
+ return this.children.length;
+ };
+ // Container.getNumChildren is @deprecated. Remove for 1.1+
+ p.getNumChildren = createjs.deprecate(p._getNumChildren, "Container.getNumChildren");
+
+ /**
+ * Returns the number of children in the container.
+ * @property numChildren
+ * @type {Number}
+ * @readonly
+ **/
+ try {
+ Object.defineProperties(p, {
+ numChildren: { get: p._getNumChildren }
+ });
+ } catch (e) {}
+
+
+// public methods:
+ /**
+ * Constructor alias for backwards compatibility. This method will be removed in future versions.
+ * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
+ * @method initialize
+ * @deprecated in favour of `createjs.promote()`
+ **/
+ p.initialize = Container; // TODO: deprecated.
+
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ var hasContent = this.cacheCanvas || this.children.length;
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
+ };
+
+ /**
+ * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
+
+ // this ensures we don't have issues with display list changes that occur during a draw:
+ var list = this.children.slice();
+ for (var i=0,l=list.length; iExample
+ *
+ * container.addChild(bitmapInstance);
+ *
+ * You can also add multiple children at once:
+ *
+ * container.addChild(bitmapInstance, shapeInstance, textInstance);
+ *
+ * @method addChild
+ * @param {DisplayObject} child The display object to add.
+ * @return {DisplayObject} The child that was added, or the last child if multiple children were added.
+ **/
+ p.addChild = function(child) {
+ if (child == null) { return child; }
+ var l = arguments.length;
+ if (l > 1) {
+ for (var i=0; iExample
+ *
+ * addChildAt(child1, index);
+ *
+ * You can also add multiple children, such as:
+ *
+ * addChildAt(child1, child2, ..., index);
+ *
+ * The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list,
+ * you could use:
+ *
+ * container.addChildAt(myShape, container.getChildIndex(otherShape));
+ *
+ * This would also bump otherShape's index up by one. Fails silently if the index is out of range.
+ *
+ * @method addChildAt
+ * @param {DisplayObject} child The display object to add.
+ * @param {Number} index The index to add the child at.
+ * @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added.
+ **/
+ p.addChildAt = function(child, index) {
+ var l = arguments.length;
+ var indx = arguments[l-1]; // can't use the same name as the index param or it replaces arguments[1]
+ if (indx < 0 || indx > this.children.length) { return arguments[l-2]; }
+ if (l > 2) {
+ for (var i=0; iExample
+ *
+ * container.removeChild(child);
+ *
+ * You can also remove multiple children:
+ *
+ * removeChild(child1, child2, ...);
+ *
+ * Returns true if the child (or children) was removed, or false if it was not in the display list.
+ * @method removeChild
+ * @param {DisplayObject} child The child to remove.
+ * @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list.
+ **/
+ p.removeChild = function(child) {
+ var l = arguments.length;
+ if (l > 1) {
+ var good = true;
+ for (var i=0; iExample
+ *
+ * container.removeChildAt(2);
+ *
+ * You can also remove multiple children:
+ *
+ * container.removeChild(2, 7, ...)
+ *
+ * Returns true if the child (or children) was removed, or false if any index was out of range.
+ * @method removeChildAt
+ * @param {Number} index The index of the child to remove.
+ * @return {Boolean} true if the child (or children) was removed, or false if any index was out of range.
+ **/
+ p.removeChildAt = function(index) {
+ var l = arguments.length;
+ if (l > 1) {
+ var a = [];
+ for (var i=0; iExample
+ *
+ * container.removeAllChildren();
+ *
+ * @method removeAllChildren
+ **/
+ p.removeAllChildren = function() {
+ var kids = this.children;
+ while (kids.length) { this._removeChildAt(0); }
+ };
+
+ /**
+ * Returns the child at the specified index.
+ *
+ * Example
+ *
+ * container.getChildAt(2);
+ *
+ * @method getChildAt
+ * @param {Number} index The index of the child to return.
+ * @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index.
+ **/
+ p.getChildAt = function(index) {
+ return this.children[index];
+ };
+
+ /**
+ * Returns the child with the specified name.
+ * @method getChildByName
+ * @param {String} name The name of the child to return.
+ * @return {DisplayObject} The child with the specified name.
+ **/
+ p.getChildByName = function(name) {
+ var kids = this.children;
+ for (var i=0,l=kids.length;iExample: Display children with a higher y in front.
+ *
+ * var sortFunction = function(obj1, obj2, options) {
+ * if (obj1.y > obj2.y) { return 1; }
+ * if (obj1.y < obj2.y) { return -1; }
+ * return 0;
+ * }
+ * container.sortChildren(sortFunction);
+ *
+ * @method sortChildren
+ * @param {Function} sortFunction the function to use to sort the child list. See JavaScript's Array.sort
+ * documentation for details.
+ **/
+ p.sortChildren = function(sortFunction) {
+ this.children.sort(sortFunction);
+ };
+
+ /**
+ * Returns the index of the specified child in the display list, or -1 if it is not in the display list.
+ *
+ * Example
+ *
+ * var index = container.getChildIndex(child);
+ *
+ * @method getChildIndex
+ * @param {DisplayObject} child The child to return the index of.
+ * @return {Number} The index of the specified child. -1 if the child is not found.
+ **/
+ p.getChildIndex = function(child) {
+ return createjs.indexOf(this.children, child);
+ };
+
+ /**
+ * Swaps the children at the specified indexes. Fails silently if either index is out of range.
+ * @method swapChildrenAt
+ * @param {Number} index1
+ * @param {Number} index2
+ **/
+ p.swapChildrenAt = function(index1, index2) {
+ var kids = this.children;
+ var o1 = kids[index1];
+ var o2 = kids[index2];
+ if (!o1 || !o2) { return; }
+ kids[index1] = o2;
+ kids[index2] = o1;
+ };
+
+ /**
+ * Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this
+ * Container.
+ * @method swapChildren
+ * @param {DisplayObject} child1
+ * @param {DisplayObject} child2
+ **/
+ p.swapChildren = function(child1, child2) {
+ var kids = this.children;
+ var index1,index2;
+ for (var i=0,l=kids.length;i= l) { return; }
+ for (var i=0;i 0 at the
+ * specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all
+ * transform properties including regX/Y.
+ * @method hitTest
+ * @param {Number} x The x position to check in the display object's local coordinates.
+ * @param {Number} y The y position to check in the display object's local coordinates.
+ * @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified
+ * coordinates.
+ **/
+ p.hitTest = function(x, y) {
+ // TODO: optimize to use the fast cache check where possible.
+ return (this.getObjectUnderPoint(x, y) != null);
+ };
+
+ /**
+ * Returns an array of all display objects under the specified coordinates that are in this container's display
+ * list. This routine ignores any display objects with {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
+ * set to `false`. The array will be sorted in order of visual depth, with the top-most display object at index 0.
+ * This uses shape based hit detection, and can be an expensive operation to run, so it is best to use it carefully.
+ * For example, if testing for objects under the mouse, test on tick (instead of on {{#crossLink "DisplayObject/mousemove:event"}}{{/crossLink}}),
+ * and only if the mouse's position has changed.
+ *
+ *
+ * - By default (mode=0) this method evaluates all display objects.
+ * - By setting the `mode` parameter to `1`, the {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
+ * and {{#crossLink "mouseChildren:property"}}{{/crossLink}} properties will be respected.
+ * - Setting the `mode` to `2` additionally excludes display objects that do not have active mouse event
+ * listeners or a {{#crossLink "DisplayObject:cursor:property"}}{{/crossLink}} property. That is, only objects
+ * that would normally intercept mouse interaction will be included. This can significantly improve performance
+ * in some cases by reducing the number of display objects that need to be tested.
+ *
+ *
+ * This method accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}.
+ * @method getObjectsUnderPoint
+ * @param {Number} x The x position in the container to test.
+ * @param {Number} y The y position in the container to test.
+ * @param {Number} [mode=0] The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
+ * @return {Array} An Array of DisplayObjects under the specified coordinates.
+ **/
+ p.getObjectsUnderPoint = function(x, y, mode) {
+ var arr = [];
+ var pt = this.localToGlobal(x, y);
+ this._getObjectsUnderPoint(pt.x, pt.y, arr, mode>0, mode==1);
+ return arr;
+ };
+
+ /**
+ * Similar to {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}, but returns only the top-most display
+ * object. This runs significantly faster than getObjectsUnderPoint()
, but is still potentially an expensive
+ * operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information.
+ * @method getObjectUnderPoint
+ * @param {Number} x The x position in the container to test.
+ * @param {Number} y The y position in the container to test.
+ * @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
+ * @return {DisplayObject} The top-most display object under the specified coordinates.
+ **/
+ p.getObjectUnderPoint = function(x, y, mode) {
+ var pt = this.localToGlobal(x, y);
+ return this._getObjectsUnderPoint(pt.x, pt.y, null, mode>0, mode==1);
+ };
+
+ /**
+ * Docced in superclass.
+ */
+ p.getBounds = function() {
+ return this._getBounds(null, true);
+ };
+
+
+ /**
+ * Docced in superclass.
+ */
+ p.getTransformedBounds = function() {
+ return this._getBounds();
+ };
+
+ /**
+ * Returns a clone of this Container. Some properties that are specific to this instance's current context are
+ * reverted to their defaults (for example .parent).
+ * @method clone
+ * @param {Boolean} [recursive=false] If true, all of the descendants of this container will be cloned recursively. If false, the
+ * properties of the container will be cloned, but the new instance will not have any children.
+ * @return {Container} A clone of the current Container instance.
+ **/
+ p.clone = function(recursive) {
+ var o = this._cloneProps(new Container());
+ if (recursive) { this._cloneChildren(o); }
+ return o;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Container (name="+ this.name +")]";
+ };
+
+
+// private methods:
+ /**
+ * @method _tick
+ * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
+ * @protected
+ **/
+ p._tick = function(evtObj) {
+ if (this.tickChildren) {
+ for (var i=this.children.length-1; i>=0; i--) {
+ var child = this.children[i];
+ if (child.tickEnabled && child._tick) { child._tick(evtObj); }
+ }
+ }
+ this.DisplayObject__tick(evtObj);
+ };
+
+ /**
+ * Recursively clones all children of this container, and adds them to the target container.
+ * @method cloneChildren
+ * @protected
+ * @param {Container} o The target container.
+ **/
+ p._cloneChildren = function(o) {
+ if (o.children.length) { o.removeAllChildren(); }
+ var arr = o.children;
+ for (var i=0, l=this.children.length; i this.children.length-1) { return false; }
+ var child = this.children[index];
+ if (child) { child.parent = null; }
+ this.children.splice(index, 1);
+ if (!silent) { child.dispatchEvent("removed"); }
+ return true;
+ };
+
+ /**
+ * @method _getObjectsUnderPoint
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Array} arr
+ * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners.
+ * @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object.
+ * @param {Number} currentDepth Indicates the current depth of the search.
+ * @return {DisplayObject}
+ * @protected
+ **/
+ p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener, currentDepth) {
+ currentDepth = currentDepth || 0;
+ if (!currentDepth && !this._testMask(this, x, y)) { return null; }
+ var mtx, ctx = createjs.DisplayObject._hitTestContext;
+ activeListener = activeListener || (mouse&&this._hasMouseEventListener());
+
+ // draw children one at a time, and check if we get a hit:
+ var children = this.children, l = children.length;
+ for (var i=l-1; i>=0; i--) {
+ var child = children[i];
+ var hitArea = child.hitArea;
+ if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; }
+ if (!hitArea && !this._testMask(child, x, y)) { continue; }
+
+ // if a child container has a hitArea then we only need to check its hitAre2a, so we can treat it as a normal DO:
+ if (!hitArea && child instanceof Container) {
+ var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth+1);
+ if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; }
+ } else {
+ if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; }
+
+ // TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense.
+ var props = child.getConcatenatedDisplayProps(child._props);
+ mtx = props.matrix;
+
+ if (hitArea) {
+ mtx.appendMatrix(hitArea.getMatrix(hitArea._props.matrix));
+ props.alpha = hitArea.alpha;
+ }
+
+ ctx.globalAlpha = props.alpha;
+ ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
+ (hitArea||child).draw(ctx);
+ if (!this._testHit(ctx)) { continue; }
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.clearRect(0, 0, 2, 2);
+ if (arr) { arr.push(child); }
+ else { return (mouse && !this.mouseChildren) ? this : child; }
+ }
+ }
+ return null;
+ };
+
+ /**
+ * @method _testMask
+ * @param {DisplayObject} target
+ * @param {Number} x
+ * @param {Number} y
+ * @return {Boolean} Indicates whether the x/y is within the masked region.
+ * @protected
+ **/
+ p._testMask = function(target, x, y) {
+ var mask = target.mask;
+ if (!mask || !mask.graphics || mask.graphics.isEmpty()) { return true; }
+
+ var mtx = this._props.matrix, parent = target.parent;
+ mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity();
+ mtx = mask.getMatrix(mask._props.matrix).prependMatrix(mtx);
+
+ var ctx = createjs.DisplayObject._hitTestContext;
+ ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
+
+ // draw the mask as a solid fill:
+ mask.graphics.drawAsPath(ctx);
+ ctx.fillStyle = "#000";
+ ctx.fill();
+
+ if (!this._testHit(ctx)) { return false; }
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.clearRect(0, 0, 2, 2);
+
+ return true;
+ };
+
+ /**
+ * @method _getBounds
+ * @param {Matrix2D} matrix
+ * @param {Boolean} ignoreTransform If true, does not apply this object's transform.
+ * @return {Rectangle}
+ * @protected
+ **/
+ p._getBounds = function(matrix, ignoreTransform) {
+ var bounds = this.DisplayObject_getBounds();
+ if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
+
+ var mtx = this._props.matrix;
+ mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
+ if (matrix) { mtx.prependMatrix(matrix); }
+
+ var l = this.children.length, rect=null;
+ for (var i=0; iExample
+ * This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child
+ * and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}.
+ *
+ * var stage = new createjs.Stage("canvasElementId");
+ * var image = new createjs.Bitmap("imagePath.png");
+ * stage.addChild(image);
+ * createjs.Ticker.addEventListener("tick", handleTick);
+ * function handleTick(event) {
+ * image.x += 10;
+ * stage.update();
+ * }
+ *
+ * @class Stage
+ * @extends Container
+ * @constructor
+ * @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id
+ * of a canvas object in the current document.
+ **/
+ function Stage(canvas) {
+ this.Container_constructor();
+
+
+ // public properties:
+ /**
+ * Indicates whether the stage should automatically clear the canvas before each render. You can set this to false
+ * to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for
+ * example).
+ *
+ * Example
+ *
+ * var stage = new createjs.Stage("canvasId");
+ * stage.autoClear = false;
+ *
+ * @property autoClear
+ * @type Boolean
+ * @default true
+ **/
+ this.autoClear = true;
+
+ /**
+ * The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the
+ * first stage that will be ticked (or they will clear each other's render).
+ *
+ * When changing the canvas property you must disable the events on the old canvas, and enable events on the
+ * new canvas or mouse events will not work as expected. For example:
+ *
+ * myStage.enableDOMEvents(false);
+ * myStage.canvas = anotherCanvas;
+ * myStage.enableDOMEvents(true);
+ *
+ * @property canvas
+ * @type HTMLCanvasElement | Object
+ **/
+ this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas;
+
+ /**
+ * The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
+ * position over the canvas, and mouseInBounds will be set to false.
+ * @property mouseX
+ * @type Number
+ * @readonly
+ **/
+ this.mouseX = 0;
+
+ /**
+ * The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
+ * position over the canvas, and mouseInBounds will be set to false.
+ * @property mouseY
+ * @type Number
+ * @readonly
+ **/
+ this.mouseY = 0;
+
+ /**
+ * Specifies the area of the stage to affect when calling update. This can be use to selectively
+ * re-draw specific regions of the canvas. If null, the whole canvas area is drawn.
+ * @property drawRect
+ * @type {Rectangle}
+ */
+ this.drawRect = null;
+
+ /**
+ * Indicates whether display objects should be rendered on whole pixels. You can set the
+ * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of
+ * display objects to false to enable/disable this behaviour on a per instance basis.
+ * @property snapToPixelEnabled
+ * @type Boolean
+ * @default false
+ **/
+ this.snapToPixelEnabled = false;
+
+ /**
+ * Indicates whether the mouse is currently within the bounds of the canvas.
+ * @property mouseInBounds
+ * @type Boolean
+ * @default false
+ **/
+ this.mouseInBounds = false;
+
+ /**
+ * If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas.
+ * @property tickOnUpdate
+ * @type Boolean
+ * @default true
+ **/
+ this.tickOnUpdate = true;
+
+ /**
+ * If true, mouse move events will continue to be called when the mouse leaves the target canvas. See
+ * {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}}
+ * x/y/rawX/rawY.
+ * @property mouseMoveOutside
+ * @type Boolean
+ * @default false
+ **/
+ this.mouseMoveOutside = false;
+
+
+ /**
+ * Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas.
+ * This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas.
+ * @property preventSelection
+ * @type Boolean
+ * @default true
+ **/
+ this.preventSelection = true;
+
+ /**
+ * The hitArea property is not supported for Stage.
+ * @property hitArea
+ * @type {DisplayObject}
+ * @default null
+ */
+
+
+ // private properties:
+ /**
+ * Holds objects with data for each active pointer id. Each object has the following properties:
+ * x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position)
+ * @property _pointerData
+ * @type {Object}
+ * @private
+ */
+ this._pointerData = {};
+
+ /**
+ * Number of active pointers.
+ * @property _pointerCount
+ * @type {Object}
+ * @private
+ */
+ this._pointerCount = 0;
+
+ /**
+ * The ID of the primary pointer.
+ * @property _primaryPointerID
+ * @type {Object}
+ * @private
+ */
+ this._primaryPointerID = null;
+
+ /**
+ * @property _mouseOverIntervalID
+ * @protected
+ * @type Number
+ **/
+ this._mouseOverIntervalID = null;
+
+ /**
+ * @property _nextStage
+ * @protected
+ * @type Stage
+ **/
+ this._nextStage = null;
+
+ /**
+ * @property _prevStage
+ * @protected
+ * @type Stage
+ **/
+ this._prevStage = null;
+
+
+ // initialize:
+ this.enableDOMEvents(true);
+ }
+ var p = createjs.extend(Stage, createjs.Container);
+
+// events:
+ /**
+ * Dispatched when the user moves the mouse over the canvas.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event stagemousemove
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}}
+ * class for a listing of event properties.
+ * @event stagemousedown
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the user the user presses somewhere on the stage, then releases the mouse button anywhere that the page can detect it (this varies slightly between browsers).
+ * You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds.
+ * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
+ * @event stagemouseup
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false).
+ * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
+ * class for a listing of event properties.
+ * @event mouseleave
+ * @since 0.7.0
+ */
+
+ /**
+ * Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true).
+ * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
+ * class for a listing of event properties.
+ * @event mouseenter
+ * @since 0.7.0
+ */
+
+ /**
+ * Dispatched each update immediately before the tick event is propagated through the display list.
+ * You can call preventDefault on the event object to cancel propagating the tick event.
+ * @event tickstart
+ * @since 0.7.0
+ */
+
+ /**
+ * Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if
+ * tickOnUpdate is false. Precedes the "drawstart" event.
+ * @event tickend
+ * @since 0.7.0
+ */
+
+ /**
+ * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it.
+ * You can call preventDefault on the event object to cancel the draw.
+ * @event drawstart
+ * @since 0.7.0
+ */
+
+ /**
+ * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
+ * @event drawend
+ * @since 0.7.0
+ */
+
+
+// getter / setters:
+ /**
+ * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them.
+ * This can be useful in cases where you have multiple layered canvases and want user interactions
+ * events to pass through. For example, this would relay mouse events from topStage to bottomStage:
+ *
+ * topStage.nextStage = bottomStage;
+ *
+ * To disable relaying, set nextStage to null.
+ *
+ * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings
+ * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled.
+ * Considerations when using roll over in relay targets:
+ * - The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)
+ * - All stages that wish to participate in mouse over interaction must enable them via enableMouseOver
+ * - All relay targets will share the frequency value of the top-most stage
+ *
+ * To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing
+ * 30 as it's desired frequency):
+ * topStage.nextStage = targetStage;
+ * topStage.enableMouseOver(10);
+ * targetStage.enableMouseOver(30);
+ *
+ * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its
+ * DOM events using:
+ *
+ * targetStage.enableDOMEvents(false);
+ *
+ * @property nextStage
+ * @type {Stage}
+ **/
+ p._get_nextStage = function() {
+ return this._nextStage;
+ };
+ p._set_nextStage = function(value) {
+ if (this._nextStage) { this._nextStage._prevStage = null; }
+ if (value) { value._prevStage = this; }
+ this._nextStage = value;
+ };
+
+ try {
+ Object.defineProperties(p, {
+ nextStage: { get: p._get_nextStage, set: p._set_nextStage }
+ });
+ } catch (e) {} // TODO: use Log
+
+
+// public methods:
+ /**
+ * Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}}
+ * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false,
+ * and then render the display list to the canvas.
+ *
+ * @method update
+ * @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property.
+ **/
+ p.update = function(props) {
+ if (!this.canvas) { return; }
+ if (this.tickOnUpdate) { this.tick(props); }
+ if (this.dispatchEvent("drawstart", false, true) === false) { return; }
+ createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
+ var r = this.drawRect, ctx = this.canvas.getContext("2d");
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ if (this.autoClear) {
+ if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); }
+ else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); }
+ }
+ ctx.save();
+ if (this.drawRect) {
+ ctx.beginPath();
+ ctx.rect(r.x, r.y, r.width, r.height);
+ ctx.clip();
+ }
+ this.updateContext(ctx);
+ this.draw(ctx, false);
+ ctx.restore();
+ this.dispatchEvent("drawend");
+ };
+
+ /**
+ * Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
+ * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
+ *
+ * If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is
+ * propagated to listeners.
+ *
+ * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
+ * a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be
+ * passed as the `props` parameter to `tick()`. For example:
+ *
+ * Ticker.on("tick", handleTick);
+ * function handleTick(evtObj) {
+ * // clone the event object from Ticker, and add some custom data to it:
+ * var evt = evtObj.clone().set({greeting:"hello", name:"world"});
+ *
+ * // pass it to stage.update():
+ * myStage.update(evt); // subsequently calls tick() with the same param
+ * }
+ *
+ * // ...
+ * myDisplayObject.on("tick", handleDisplayObjectTick);
+ * function handleDisplayObjectTick(evt) {
+ * console.log(evt.delta); // the delta property from the Ticker tick event object
+ * console.log(evt.greeting, evt.name); // custom data: "hello world"
+ * }
+ *
+ * @method tick
+ * @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property.
+ **/
+ p.tick = function(props) {
+ if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) { return; }
+ var evtObj = new createjs.Event("tick");
+ if (props) {
+ for (var n in props) {
+ if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; }
+ }
+ }
+ this._tick(evtObj);
+ this.dispatchEvent("tickend");
+ };
+
+ /**
+ * Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}}
+ * event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}}
+ * directly, using:
+ *
+ * Ticker.addEventListener("tick", myStage);
+ *
+ * Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to
+ * display object tick handlers, instead of delta
and paused
parameters.
+ * @property handleEvent
+ * @type Function
+ **/
+ p.handleEvent = function(evt) {
+ if (evt.type == "tick") { this.update(evt); }
+ };
+
+ /**
+ * Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`.
+ * @method clear
+ **/
+ p.clear = function() {
+ if (!this.canvas) { return; }
+ var ctx = this.canvas.getContext("2d");
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
+ ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1);
+ };
+
+ /**
+ * Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can
+ * be specified as the src value of an image element.
+ * @method toDataURL
+ * @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color
+ * value is allowed. The default value is a transparent background.
+ * @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type
+ * is passed in, or if the browser does not support the specified MIME type, the default value will be used.
+ * @return {String} a Base64 encoded image.
+ **/
+ p.toDataURL = function(backgroundColor, mimeType) {
+ var data, ctx = this.canvas.getContext('2d'), w = this.canvas.width, h = this.canvas.height;
+
+ if (backgroundColor) {
+ data = ctx.getImageData(0, 0, w, h);
+ var compositeOperation = ctx.globalCompositeOperation;
+ ctx.globalCompositeOperation = "destination-over";
+
+ ctx.fillStyle = backgroundColor;
+ ctx.fillRect(0, 0, w, h);
+ }
+
+ var dataURL = this.canvas.toDataURL(mimeType||"image/png");
+
+ if(backgroundColor) {
+ ctx.putImageData(data, 0, 0);
+ ctx.globalCompositeOperation = compositeOperation;
+ }
+
+ return dataURL;
+ };
+
+ /**
+ * Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}
+ * and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}
+ * and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can
+ * be expensive to generate, so they are disabled by default. The frequency of the events can be controlled
+ * independently of mouse move events via the optional `frequency` parameter.
+ *
+ * Example
+ *
+ * var stage = new createjs.Stage("canvasId");
+ * stage.enableMouseOver(10); // 10 updates per second
+ *
+ * @method enableMouseOver
+ * @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast
+ * mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less
+ * responsive, but uses less CPU.
+ **/
+ p.enableMouseOver = function(frequency) {
+ if (this._mouseOverIntervalID) {
+ clearInterval(this._mouseOverIntervalID);
+ this._mouseOverIntervalID = null;
+ if (frequency == 0) {
+ this._testMouseOver(true);
+ }
+ }
+ if (frequency == null) { frequency = 20; }
+ else if (frequency <= 0) { return; }
+ var o = this;
+ this._mouseOverIntervalID = setInterval(function(){ o._testMouseOver(); }, 1000/Math.min(50,frequency));
+ };
+
+ /**
+ * Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good
+ * practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive
+ * events from the page.
+ *
+ * When changing the canvas property you must disable the events on the old canvas, and enable events on the
+ * new canvas or mouse events will not work as expected. For example:
+ *
+ * myStage.enableDOMEvents(false);
+ * myStage.canvas = anotherCanvas;
+ * myStage.enableDOMEvents(true);
+ *
+ * @method enableDOMEvents
+ * @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true.
+ **/
+
+ p.enableDOMEvents = function(enable) {
+ if (enable == null) { enable = true; }
+ var n, o, ls = this._eventListeners;
+ if (!enable && ls) {
+ for (n in ls) {
+ o = ls[n];
+ if(n!=="mousemove"){
+ o.t.removeEventListener(n, o.f, false);
+ delete this._eventListeners[n];
+ }
+ }
+ } else if (enable && this.canvas) {
+ var t = window.addEventListener ? window : document;
+ var _this = this;
+ if(this._eventListeners===undefined)ls=this._eventListeners={};
+ if(ls["mousemove"]===undefined){
+ ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} };
+ ls["mousemove"].t.addEventListener("mousemove",ls["mousemove"].f, false);
+ }
+ if(ls["mouseup"]===undefined){
+ ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} };
+ ls["mouseup"].t.addEventListener("mouseup",ls["mouseup"].f, false);
+ }
+ if(ls["dblclick"]===undefined){
+ ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} };
+ ls["dblclick"].t.addEventListener("dblclick",ls["dblclick"].f, false);
+ }
+ if(ls["mousedown"]===undefined){
+ ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} };
+ ls["mousedown"].t.addEventListener("mousedown",ls["mousedown"].f, false);
+ }
+ }
+ };
+
+ /**
+ * Stage instances cannot be cloned.
+ * @method clone
+ **/
+ p.clone = function() {
+ throw("Stage cannot be cloned.");
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Stage (name="+ this.name +")]";
+ };
+
+
+// private methods:
+ /**
+ * @method _getElementRect
+ * @protected
+ * @param {HTMLElement} e
+ **/
+ p._getElementRect = function(e) {
+ var bounds;
+ try { bounds = e.getBoundingClientRect(); } // this can fail on disconnected DOM elements in IE9
+ catch (err) { bounds = {top: e.offsetTop, left: e.offsetLeft, width:e.offsetWidth, height:e.offsetHeight}; }
+
+ var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0);
+ var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || document.body.clientTop || 0);
+
+ var styles = window.getComputedStyle ? getComputedStyle(e,null) : e.currentStyle; // IE <9 compatibility.
+ var padL = parseInt(styles.paddingLeft)+parseInt(styles.borderLeftWidth);
+ var padT = parseInt(styles.paddingTop)+parseInt(styles.borderTopWidth);
+ var padR = parseInt(styles.paddingRight)+parseInt(styles.borderRightWidth);
+ var padB = parseInt(styles.paddingBottom)+parseInt(styles.borderBottomWidth);
+
+ // note: in some browsers bounds properties are read only.
+ return {
+ left: bounds.left+offX+padL,
+ right: bounds.right+offX-padR,
+ top: bounds.top+offY+padT,
+ bottom: bounds.bottom+offY-padB
+ }
+ };
+
+ /**
+ * @method _getPointerData
+ * @protected
+ * @param {Number} id
+ **/
+ p._getPointerData = function(id) {
+ var data = this._pointerData[id];
+ if (!data) { data = this._pointerData[id] = {x:0,y:0}; }
+ return data;
+ };
+
+ /**
+ * @method _handleMouseMove
+ * @protected
+ * @param {MouseEvent} e
+ **/
+ p._handleMouseMove = function(e) {
+ if(!e){ e = window.event; }
+ this._handlePointerMove(-1, e, e.pageX, e.pageY);
+ };
+
+ /**
+ * @method _handlePointerMove
+ * @protected
+ * @param {Number} id
+ * @param {Event} e
+ * @param {Number} pageX
+ * @param {Number} pageY
+ * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
+ **/
+ p._handlePointerMove = function(id, e, pageX, pageY, owner) {
+ if (this._prevStage && owner === undefined) { return; } // redundant listener.
+ if (!this.canvas) { return; }
+ var nextStage=this._nextStage, o=this._getPointerData(id);
+
+ var inBounds = o.inBounds;
+ this._updatePointerPosition(id, e, pageX, pageY);
+ if (inBounds || o.inBounds || this.mouseMoveOutside) {
+ if (id === -1 && o.inBounds == !inBounds) {
+ this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e);
+ }
+
+ this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e);
+ this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e);
+ }
+
+ nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null);
+ };
+
+ /**
+ * @method _updatePointerPosition
+ * @protected
+ * @param {Number} id
+ * @param {Event} e
+ * @param {Number} pageX
+ * @param {Number} pageY
+ **/
+ p._updatePointerPosition = function(id, e, pageX, pageY) {
+ var rect = this._getElementRect(this.canvas);
+ pageX -= rect.left;
+ pageY -= rect.top;
+
+ var w = this.canvas.width;
+ var h = this.canvas.height;
+ pageX /= (rect.right-rect.left)/w;
+ pageY /= (rect.bottom-rect.top)/h;
+ var o = this._getPointerData(id);
+ if (o.inBounds = (pageX >= 0 && pageY >= 0 && pageX <= w-1 && pageY <= h-1)) {
+ o.x = pageX;
+ o.y = pageY;
+ } else if (this.mouseMoveOutside) {
+ o.x = pageX < 0 ? 0 : (pageX > w-1 ? w-1 : pageX);
+ o.y = pageY < 0 ? 0 : (pageY > h-1 ? h-1 : pageY);
+ }
+
+ o.posEvtObj = e;
+ o.rawX = pageX;
+ o.rawY = pageY;
+
+ if (id === this._primaryPointerID || id === -1) {
+ this.mouseX = o.x;
+ this.mouseY = o.y;
+ this.mouseInBounds = o.inBounds;
+ }
+ };
+
+ /**
+ * @method _handleMouseUp
+ * @protected
+ * @param {MouseEvent} e
+ **/
+ p._handleMouseUp = function(e) {
+ this._handlePointerUp(-1, e, false);
+ };
+
+ /**
+ * @method _handlePointerUp
+ * @protected
+ * @param {Number} id
+ * @param {Event} e
+ * @param {Boolean} clear
+ * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
+ **/
+ p._handlePointerUp = function(id, e, clear, owner) {
+ var nextStage = this._nextStage, o = this._getPointerData(id);
+ if (this._prevStage && owner === undefined) { return; } // redundant listener.
+
+ var target=null, oTarget = o.target;
+ if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
+
+ if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; }
+
+ if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); }
+ this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e);
+
+ if (clear) {
+ if (id==this._primaryPointerID) { this._primaryPointerID = null; }
+ delete(this._pointerData[id]);
+ } else { o.target = null; }
+
+ nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this);
+ };
+
+ /**
+ * @method _handleMouseDown
+ * @protected
+ * @param {MouseEvent} e
+ **/
+ p._handleMouseDown = function(e) {
+ this._handlePointerDown(-1, e, e.pageX, e.pageY);
+ };
+
+ /**
+ * @method _handlePointerDown
+ * @protected
+ * @param {Number} id
+ * @param {Event} e
+ * @param {Number} pageX
+ * @param {Number} pageY
+ * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
+ **/
+ p._handlePointerDown = function(id, e, pageX, pageY, owner) {
+ if (this.preventSelection) { e.preventDefault(); }
+ if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over.
+
+ if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); }
+ var target = null, nextStage = this._nextStage, o = this._getPointerData(id);
+ if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
+
+ if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; }
+ this._dispatchMouseEvent(target, "mousedown", true, id, o, e);
+
+ nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this);
+ };
+
+ /**
+ * @method _testMouseOver
+ * @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target)
+ * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
+ * @param {Stage} eventTarget The stage that the cursor is actively over.
+ * @protected
+ **/
+ p._testMouseOver = function(clear, owner, eventTarget) {
+ if (this._prevStage && owner === undefined) { return; } // redundant listener.
+
+ var nextStage = this._nextStage;
+ if (!this._mouseOverIntervalID) {
+ // not enabled for mouseover, but should still relay the event.
+ nextStage&&nextStage._testMouseOver(clear, owner, eventTarget);
+ return;
+ }
+ var o = this._getPointerData(-1);
+ // only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs.
+ if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; }
+
+ var e = o.posEvtObj;
+ var isEventTarget = eventTarget || e&&(e.target == this.canvas);
+ var target=null, common = -1, cursor="", t, i, l;
+
+ if (!owner && (clear || this.mouseInBounds && isEventTarget)) {
+ target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true);
+ this._mouseOverX = this.mouseX;
+ this._mouseOverY = this.mouseY;
+ }
+
+ var oldList = this._mouseOverTarget||[];
+ var oldTarget = oldList[oldList.length-1];
+ var list = this._mouseOverTarget = [];
+
+ // generate ancestor list and check for cursor:
+ t = target;
+ while (t) {
+ list.unshift(t);
+ if (!cursor) { cursor = t.cursor; }
+ t = t.parent;
+ }
+ this.canvas.style.cursor = cursor;
+ if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; }
+
+ // find common ancestor:
+ for (i=0,l=list.length; icommon; i--) {
+ this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e, target);
+ }
+
+ for (i=list.length-1; i>common; i--) {
+ this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e, oldTarget);
+ }
+
+ if (oldTarget != target) {
+ this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget);
+ }
+
+ nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this);
+ };
+
+ /**
+ * @method _handleDoubleClick
+ * @protected
+ * @param {MouseEvent} e
+ * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
+ **/
+ p._handleDoubleClick = function(e, owner) {
+ var target=null, nextStage=this._nextStage, o=this._getPointerData(-1);
+ if (!owner) {
+ target = this._getObjectsUnderPoint(o.x, o.y, null, true);
+ this._dispatchMouseEvent(target, "dblclick", true, -1, o, e);
+ }
+ nextStage&&nextStage._handleDoubleClick(e, owner || target && this);
+ };
+
+ /**
+ * @method _dispatchMouseEvent
+ * @protected
+ * @param {DisplayObject} target
+ * @param {String} type
+ * @param {Boolean} bubbles
+ * @param {Number} pointerId
+ * @param {Object} o
+ * @param {MouseEvent} [nativeEvent]
+ * @param {DisplayObject} [relatedTarget]
+ **/
+ p._dispatchMouseEvent = function(target, type, bubbles, pointerId, o, nativeEvent, relatedTarget) {
+ // TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC.
+ if (!target || (!bubbles && !target.hasEventListener(type))) { return; }
+ /*
+ // TODO: account for stage transformations?
+ this._mtx = this.getConcatenatedMatrix(this._mtx).invert();
+ var pt = this._mtx.transformPoint(o.x, o.y);
+ var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID || pointerId==-1, o.rawX, o.rawY);
+ */
+ var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId === this._primaryPointerID || pointerId === -1, o.rawX, o.rawY, relatedTarget);
+ target.dispatchEvent(evt);
+ };
+
+
+ createjs.Stage = createjs.promote(Stage, "Container");
+}());
+
+//##############################################################################
+// StageGL.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/*
+ * README IF EDITING:
+ * Terminology for developers:
+ *
+ * Vertex: a point that help defines a shape, 3 per triangle. Usually has an x,y,z but can have more/less info.
+ * Vertex Property: a piece of information attached to the vertex like a vector3 containing x,y,z
+ * Index/Indices: used in groups of 3 to define a triangle, points to vertices by their index in an array (some render
+ * modes do not use these)
+ * Card: a group of 2 triangles used to display a rectangular image
+ * U/V: common names for the [0-1] texture co-ordinates on an image
+ * Batch: a single call to the renderer, best done as little as possible so multiple cards are put into a single batch
+ * Buffer: WebGL array data
+ * Program/Shader: For every vertex we run the Vertex shader. The results are used per pixel by the Fragment shader. When
+ * combined and paired these are a shader "program"
+ * Texture: WebGL representation of image data and associated extra information
+ * Slot: A space on the GPU into which textures can be loaded for use in a batch, using "ActiveTexture" switches texture slot.
+ */
+
+(function () {
+ "use strict";
+
+ /**
+ * A StageGL instance is the root level {{#crossLink "Container"}}{{/crossLink}} for an WebGL-optimized display list,
+ * which is used in place of the usual {{#crossLink "Stage"}}{{/crossLink}}. This class should behave identically to
+ * a {{#crossLink "Stage"}}{{/crossLink}} except for WebGL-specific functionality.
+ *
+ * Each time the {{#crossLink "Stage/tick"}}{{/crossLink}} method is called, the display list is rendered to the
+ * target <canvas/> instance, ignoring non-WebGL-compatible display objects. On devices and browsers that don't
+ * support WebGL, content will automatically be rendered to canvas 2D context instead.
+ *
+ * Limitations
+ * - {{#crossLink "Shape"}}{{/crossLink}}, {{#crossLink "Shadow"}}{{/crossLink}}, and {{#crossLink "Text"}}{{/crossLink}}
+ * are not rendered when added to the display list.
+ * - To display something StageGL cannot render, {{#crossLink "displayObject/cache"}}{{/crossLink}} the object.
+ * Caches can be rendered regardless of source.
+ * - Images are wrapped as a webGL "Texture". Each graphics card has a limit to its concurrent Textures, too many
+ * Textures will noticeably slow performance.
+ * - Each cache counts as an individual Texture. As such {{#crossLink "SpriteSheet"}}{{/crossLink}} and
+ * {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} are recommended practices to help keep texture counts low.
+ * - To use any image node (DOM Image/Canvas Element) between multiple StageGL instances it must be a
+ * {{#crossLink "Bitmap/clone"}}{{/crossLink}}, otherwise the GPU texture loading and tracking will get confused.
+ * - to avoid an up/down scaled render you must call {{#crossLink "StageGL/updateViewport"}}{{/crossLink}} if you
+ * resize your canvas after making a StageGL instance, this will properly size the WebGL context stored in memory.
+ * - Best performance in demanding scenarios will come from manual management of texture memory, but it is handled
+ * automatically by default. See {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} for details.
+ *
+ * Example
+ * This example creates a StageGL instance, adds a child to it, then uses the EaselJS {{#crossLink "Ticker"}}{{/crossLink}}
+ * to update the child and redraw the stage.
+ *
+ * var stage = new createjs.StageGL("canvasElementId");
+ *
+ * var image = new createjs.Bitmap("imagePath.png");
+ * stage.addChild(image);
+ *
+ * createjs.Ticker.on("tick", handleTick);
+ *
+ * function handleTick(event) {
+ * image.x += 10;
+ * stage.update();
+ * }
+ *
+ * Notes
+ * - StageGL is not currently included in the minified version of EaselJS.
+ * - {{#crossLink "SpriteContainer"}}{{/crossLink}} (the previous approach to WebGL with EaselJS) has been deprecated.
+ * - Earlier versions of WebGL support in EaselJS (SpriteStage and SpriteContainer) had hard limitations on images
+ * per container, which have been solved.
+ *
+ * @class StageGL
+ * @extends Stage
+ * @constructor
+ * @param {HTMLCanvasElement | String | Object} canvas A canvas object that StageGL will render to, or the string id
+ * of a canvas object in the current DOM.
+ * @param {Object} options All the option parameters in a reference object, some are not supported by some browsers.
+ * @param {Boolean} [options.preserveBuffer=false] If `true`, the canvas is NOT auto-cleared by WebGL (the spec
+ * discourages setting this to `true`). This is useful if you want persistent draw effects.
+ * @param {Boolean} [options.antialias=false] Specifies whether or not the browser's WebGL implementation should try
+ * to perform anti-aliasing. This will also enable linear pixel sampling on power-of-two textures (smoother images).
+ * @param {Boolean} [options.transparent=false] If `true`, the canvas is transparent. This is very
+ * expensive, and should be used with caution.
+ * @param {Boolean} [options.premultiply=false] Alters color handling. If `true`, this assumes the shader must
+ * account for pre-multiplied alpha. This can help avoid visual halo effects with some assets, but may also cause
+ * problems with other assets.
+ * @param {Integer} [options.autoPurge=1200] How often the system should automatically dump unused textures with
+ * `purgeTextures(autoPurge)` every `autoPurge/2` draws. See {{#crossLink "StageGL/purgeTextures"}}{{/crossLink}} for more
+ * information.
+ */
+ function StageGL(canvas, options) {
+ this.Stage_constructor(canvas);
+
+ if (options !== undefined) {
+ if (typeof options !== "object"){ throw("Invalid options object"); }
+ var premultiply = options.premultiply;
+ var transparent = options.transparent;
+ var antialias = options.antialias;
+ var preserveBuffer = options.preserveBuffer;
+ var autoPurge = options.autoPurge;
+ }
+
+// public properties:
+ /**
+ * Console log potential issues and problems. This is designed to have minimal performance impact, so
+ * if extensive debugging information is required, this may be inadequate. See {{#crossLink "WebGLInspector"}}{{/crossLink}}
+ * @property vocalDebug
+ * @type {Boolean}
+ * @default false
+ */
+ this.vocalDebug = false;
+
+// private properties:
+ /**
+ * Specifies whether or not the canvas is auto-cleared by WebGL. The WebGL spec discourages `true`.
+ * If true, the canvas is NOT auto-cleared by WebGL. Used when the canvas context is created and requires
+ * context re-creation to update.
+ * @property _preserveBuffer
+ * @protected
+ * @type {Boolean}
+ * @default false
+ */
+ this._preserveBuffer = preserveBuffer||false;
+
+ /**
+ * Specifies whether or not the browser's WebGL implementation should try to perform anti-aliasing.
+ * @property _antialias
+ * @protected
+ * @type {Boolean}
+ * @default false
+ */
+ this._antialias = antialias||false;
+
+ /**
+ * Specifies whether or not the browser's WebGL implementation should be transparent.
+ * @property _transparent
+ * @protected
+ * @type {Boolean}
+ * @default false
+ */
+ this._transparent = transparent||false;
+
+ /**
+ * Specifies whether or not StageGL is handling colours as premultiplied alpha.
+ * @property _premultiply
+ * @protected
+ * @type {Boolean}
+ * @default false
+ */
+ this._premultiply = premultiply||false;
+
+ /**
+ * Internal value of {{#crossLink "StageGL/autoPurge"}}{{/crossLink}}
+ * @property _autoPurge
+ * @protected
+ * @type {Integer}
+ * @default null
+ */
+ this._autoPurge = undefined;
+ this.autoPurge = autoPurge; //getter/setter handles setting the real value and validating
+
+ /**
+ * The width in px of the drawing surface saved in memory.
+ * @property _viewportWidth
+ * @protected
+ * @type {Number}
+ * @default 0
+ */
+ this._viewportWidth = 0;
+
+ /**
+ * The height in px of the drawing surface saved in memory.
+ * @property _viewportHeight
+ * @protected
+ * @type {Number}
+ * @default 0
+ */
+ this._viewportHeight = 0;
+
+ /**
+ * A 2D projection matrix used to convert WebGL's viewspace into canvas co-ordinates. Regular canvas display
+ * uses Top-Left values of [0,0] where WebGL uses a Center [0,0] Top-Right [1,1] (euclidean) system.
+ * @property _projectionMatrix
+ * @protected
+ * @type {Float32Array}
+ * @default null
+ */
+ this._projectionMatrix = null;
+
+ /**
+ * The current WebGL canvas context. Often shorthanded to just "gl" in many parts of the code.
+ * @property _webGLContext
+ * @protected
+ * @type {WebGLRenderingContext}
+ * @default null
+ */
+ this._webGLContext = null;
+
+ /**
+ * The color to use when the WebGL canvas has been cleared. May appear as a background color. Defaults to grey.
+ * @property _clearColor
+ * @protected
+ * @type {Object}
+ * @default {r: 0.50, g: 0.50, b: 0.50, a: 0.00}
+ */
+ this._clearColor = {r: 0.50, g: 0.50, b: 0.50, a: 0.00};
+
+ /**
+ * The maximum number of cards (aka a single sprite) that can be drawn in one draw call. Use getter/setters to
+ * modify otherwise internal buffers may be incorrect sizes.
+ * @property _maxCardsPerBatch
+ * @protected
+ * @type {Number}
+ * @default StageGL.DEFAULT_MAX_BATCH_SIZE (10000)
+ */
+ this._maxCardsPerBatch = StageGL.DEFAULT_MAX_BATCH_SIZE; //TODO: write getter/setters for this
+
+ /**
+ * The shader program used to draw the current batch.
+ * @property _activeShader
+ * @protected
+ * @type {WebGLProgram}
+ * @default null
+ */
+ this._activeShader = null;
+
+ /**
+ * The vertex position data for the current draw call.
+ * @property _vertices
+ * @protected
+ * @type {Float32Array}
+ * @default null
+ */
+ this._vertices = null;
+
+ /**
+ * The WebGL buffer attached to {{#crossLink "StageGL/_vertices:property"}}{{/crossLink}}.
+ * @property _vertexPositionBuffer
+ * @protected
+ * @type {WebGLBuffer}
+ * @default null
+ */
+ this._vertexPositionBuffer = null;
+
+ /**
+ * The vertex U/V data for the current draw call.
+ * @property _uvs
+ * @protected
+ * @type {Float32Array}
+ * @default null
+ */
+ this._uvs = null;
+
+ /**
+ * The WebGL buffer attached to {{#crossLink "StageGL/_uvs:property"}}{{/crossLink}}.
+ * @property _uvPositionBuffer
+ * @protected
+ * @type {WebGLBuffer}
+ * @default null
+ */
+ this._uvPositionBuffer = null;
+
+ /**
+ * The vertex indices data for the current draw call.
+ * @property _indices
+ * @protected
+ * @type {Float32Array}
+ * @default null
+ */
+ this._indices = null;
+
+ /**
+ * The WebGL buffer attached to {{#crossLink "StageGL/_indices:property"}}{{/crossLink}}.
+ * @property _textureIndexBuffer
+ * @protected
+ * @type {WebGLBuffer}
+ * @default null
+ */
+ this._textureIndexBuffer = null;
+
+ /**
+ * The vertices data for the current draw call.
+ * @property _alphas
+ * @protected
+ * @type {Float32Array}
+ * @default null
+ */
+ this._alphas = null;
+
+ /**
+ * The WebGL buffer attached to {{#crossLink "StageGL/_alphas:property"}}{{/crossLink}}.
+ * @property _alphaBuffer
+ * @protected
+ * @type {WebGLBuffer}
+ * @default null
+ */
+ this._alphaBuffer = null;
+
+ /**
+ * An index based lookup of every WebGL Texture currently in use.
+ * @property _drawTexture
+ * @protected
+ * @type {Array}
+ */
+ this._textureDictionary = [];
+
+ /**
+ * A string based lookup hash of which index a texture is stored at in the dictionary. The lookup string is
+ * often the src url.
+ * @property _textureIDs
+ * @protected
+ * @type {Object}
+ */
+ this._textureIDs = {};
+
+ /**
+ * An array of all the textures currently loaded into the GPU. The index in the array matches the GPU index.
+ * @property _batchTextures
+ * @protected
+ * @type {Array}
+ */
+ this._batchTextures = [];
+
+ /**
+ * An array of all the simple filler textures used to prevent issues with missing textures in a batch.
+ * @property _baseTextures
+ * @protected
+ * @type {Array}
+ */
+ this._baseTextures = [];
+
+ /**
+ * The number of concurrent textures the GPU can handle. This value is dynamically set from WebGL during initialization
+ * via `gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)`. The WebGL spec states that the lowest guaranteed value is 8,
+ * but it could be higher. Do not set this value higher than the value returned by the GPU. Setting it lower will
+ * probably reduce performance, but may be advisable to reserve slots for custom filter work.
+ * NOTE: Can also act as a length for {{#crossLink "StageGL/_batchTextures:property"}}.
+ * @property _batchTextureCount
+ * @protected
+ * @type {Number}
+ * @default 8
+ */
+ this._batchTextureCount = 8;
+
+ /**
+ * The location at which the last texture was inserted into a GPU slot in {{#crossLink "StageGL/_batchTextures:property"}}{{/crossLink}}.
+ * Manual control of this variable can yield improvements in performance by intelligently replacing textures
+ * inside a batch to reduce texture re-load. It is impossible to write automated general use code, as it requires
+ * display list look ahead inspection and/or render foreknowledge.
+ * @property _lastTextureInsert
+ * @protected
+ * @type {Number}
+ * @default -1
+ */
+ this._lastTextureInsert = -1;
+
+ /**
+ * The current batch being drawn, A batch consists of a call to `drawElements` on the GPU. Many of these calls
+ * can occur per draw.
+ * @property _batchId
+ * @protected
+ * @type {Number}
+ * @default 0
+ */
+ this._batchID = 0;
+
+ /**
+ * The current draw being performed, may contain multiple batches. Comparing to {{#crossLink "StageGL/_batchID:property"}}{{/crossLink}}
+ * can reveal batching efficiency.
+ * @property _drawID
+ * @protected
+ * @type {Number}
+ * @default 0
+ */
+ this._drawID = 0;
+
+ /**
+ * Used to prevent textures in certain GPU slots from being replaced by an insert.
+ * @property _slotBlackList
+ * @protected
+ * @type {Array}
+ */
+ this._slotBlacklist = [];
+
+ /**
+ * Used to prevent nested draw calls from accidentally overwriting drawing information by tracking depth.
+ * @property _isDrawing
+ * @protected
+ * @type {Number}
+ * @default 0
+ */
+ this._isDrawing = 0;
+
+ /**
+ * Used to ensure every canvas used as a texture source has a unique ID.
+ * @property _lastTrackedCanvas
+ * @protected
+ * @type {Number}
+ * @default 0
+ */
+ this._lastTrackedCanvas = 0;
+
+ /**
+ * Controls whether final rendering output of a {{#crossLink "cacheDraw"}}{{/crossLink}} is the canvas or a render
+ * texture. See the {{#crossLink "cache"}}{{/crossLink}} function modifications for full implications and discussion.
+ * @property isCacheControlled
+ * @protected
+ * @type {Boolean}
+ * @default false
+ * @todo LM: is this supposed to be _isCacheControlled since its private?
+ */
+ this.isCacheControlled = false;
+
+ /**
+ * Used to counter-position the object being cached so it aligns with the cache surface. Additionally ensures
+ * that all rendering starts with a top level container.
+ * @property _cacheContainer
+ * @protected
+ * @type {Container}
+ * @default An instance of an EaselJS Container.
+ */
+ this._cacheContainer = new createjs.Container();
+
+ // and begin
+ this._initializeWebGL();
+ }
+ var p = createjs.extend(StageGL, createjs.Stage);
+
+// static methods:
+ /**
+ * Calculate the U/V co-ordinate based info for sprite frames. Instead of pixel count it uses a 0-1 space. Also includes
+ * the ability to get info back for a specific frame, or only calculate that one frame.
+ *
+ * //generate UV rects for all entries
+ * StageGL.buildUVRects( spriteSheetA );
+ * //generate all, fetch the first
+ * var firstFrame = StageGL.buildUVRects( spriteSheetB, 0 );
+ * //generate the rect for just a single frame for performance's sake
+ * var newFrame = StageGL.buildUVRects( dynamicSpriteSheet, newFrameIndex, true );
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method buildUVRects
+ * @param {SpriteSheet} spritesheet The spritesheet to find the frames on
+ * @param {int} [target=-1] The index of the frame to return
+ * @param {Boolean} [onlyTarget=false] Whether "target" is the only frame that gets calculated
+ * @static
+ * @return {Object} the target frame if supplied and present or a generic frame {t, l, b, r}
+ */
+ StageGL.buildUVRects = function (spritesheet, target, onlyTarget) {
+ if (!spritesheet || !spritesheet._frames) { return null; }
+ if (target === undefined) { target = -1; }
+ if (onlyTarget === undefined) { onlyTarget = false; }
+
+ var start = (target != -1 && onlyTarget)?(target):(0);
+ var end = (target != -1 && onlyTarget)?(target+1):(spritesheet._frames.length);
+ for (var i=start; i 0.0035) {" + // 1/255 = 0.0039, so ignore any value below 1 because it's probably noise
+ "gl_FragColor = vec4(color.rgb/color.a, color.a * alphaValue);" +
+ "} else {" +
+ "gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);" +
+ "}"
+ );
+
+ //TODO: DHG: a real particle shader
+ /**
+ * @property PARTICLE_VERTEX_BODY
+ * @todo
+ * @final
+ * @static
+ * @type {String}
+ * @readonly
+ */
+ StageGL.PARTICLE_VERTEX_BODY = (
+ StageGL.REGULAR_VERTEX_BODY
+ );
+ /**
+ * @property PARTICLE_FRAGMENT_BODY
+ * @todo
+ * @final
+ * @static
+ * @type {String}
+ * @readonly
+ */
+ StageGL.PARTICLE_FRAGMENT_BODY = (
+ StageGL.REGULAR_FRAGMENT_BODY
+ );
+
+ /**
+ * Portion of the shader that contains the "varying" properties required in both vertex and fragment shaders. The
+ * cover shader is designed to be a simple vertex/uv only texture render that covers the render surface. Shader
+ * code may contain templates that are replaced pre-compile.
+ * @property COVER_VARYING_HEADER
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.COVER_VARYING_HEADER = (
+ "precision mediump float;" +
+
+ "varying highp vec2 vRenderCoord;" +
+ "varying highp vec2 vTextureCoord;"
+ );
+
+ /**
+ * Actual full header for the vertex shader. Includes the varying header. The cover shader is designed to be a
+ * simple vertex/uv only texture render that covers the render surface. Shader code may contain templates that are
+ * replaced pre-compile.
+ * @property COVER_VERTEX_HEADER
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.COVER_VERTEX_HEADER = (
+ StageGL.COVER_VARYING_HEADER +
+ "attribute vec2 vertexPosition;" +
+ "attribute vec2 uvPosition;" +
+ "uniform float uUpright;"
+ );
+
+ /**
+ * Actual full header for the fragment shader. Includes the varying header. The cover shader is designed to be a
+ * simple vertex/uv only texture render that covers the render surface. Shader code may contain templates that are
+ * replaced pre-compile.
+ * @property COVER_FRAGMENT_HEADER
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.COVER_FRAGMENT_HEADER = (
+ StageGL.COVER_VARYING_HEADER +
+ "uniform sampler2D uSampler;"
+ );
+
+ /**
+ * Body of the vertex shader. The cover shader is designed to be a simple vertex/uv only texture render that covers
+ * the render surface. Shader code may contain templates that are replaced pre-compile.
+ * @property COVER_VERTEX_BODY
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.COVER_VERTEX_BODY = (
+ "void main(void) {" +
+ "gl_Position = vec4(vertexPosition.x, vertexPosition.y, 0.0, 1.0);" +
+ "vRenderCoord = uvPosition;" +
+ "vTextureCoord = vec2(uvPosition.x, abs(uUpright - uvPosition.y));" +
+ "}"
+ );
+
+ /**
+ * Body of the fragment shader. The cover shader is designed to be a simple vertex/uv only texture render that
+ * covers the render surface. Shader code may contain templates that are replaced pre-compile.
+ * @property COVER_FRAGMENT_BODY
+ * @static
+ * @final
+ * @type {String}
+ * @readonly
+ */
+ StageGL.COVER_FRAGMENT_BODY = (
+ "void main(void) {" +
+ "vec4 color = texture2D(uSampler, vRenderCoord);" +
+ "gl_FragColor = color;" +
+ "}"
+ );
+
+// events:
+ /**
+ * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. You can call
+ * {{#crossLink "Event/preventDefault"}}{{/crossLink}} on the event to cancel the draw.
+ * @event drawstart
+ */
+
+ /**
+ * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
+ * @event drawend
+ */
+
+// getter / setters:
+ p._get_isWebGL = function () {
+ return !!this._webGLContext;
+ };
+
+ p._set_autoPurge = function (value) {
+ value = isNaN(value)?1200:value;
+ if (value != -1) {
+ value = value<10?10:value;
+ }
+ this._autoPurge = value;
+ };
+ p._get_autoPurge = function () {
+ return Number(this._autoPurge);
+ };
+
+ try {
+ Object.defineProperties(p, {
+ /**
+ * Indicates whether WebGL is being used for rendering. For example, this would be `false` if WebGL is not
+ * supported in the browser.
+ * @property isWebGL
+ * @type {Boolean}
+ * @readonly
+ */
+ isWebGL: { get: p._get_isWebGL },
+
+ /**
+ * Specifies whether or not StageGL is automatically purging unused textures. Higher numbers purge less
+ * often. Values below 10 are upgraded to 10, and -1 disables this feature.
+ * @property autoPurge
+ * @protected
+ * @type {Integer}
+ * @default 1000
+ */
+ autoPurge: { get: p._get_autoPurge, set: p._set_autoPurge }
+ });
+ } catch (e) {} // TODO: use Log
+
+
+// constructor methods:
+ /**
+ * Create and properly initialize the WebGL instance.
+ * @method _initializeWebGL
+ * @protected
+ * @return {WebGLRenderingContext}
+ */
+ p._initializeWebGL = function () {
+ if (this.canvas) {
+ if (!this._webGLContext || this._webGLContext.canvas !== this.canvas) {
+ // A context hasn't been defined yet,
+ // OR the defined context belongs to a different canvas, so reinitialize.
+
+ // defaults and options
+ var options = {
+ depth: false, // Disable the depth buffer as it isn't used.
+ alpha: this._transparent, // Make the canvas background transparent.
+ stencil: true,
+ antialias: this._antialias,
+ premultipliedAlpha: this._premultiply, // Assume the drawing buffer contains colors with premultiplied alpha.
+ preserveDrawingBuffer: this._preserveBuffer
+ };
+
+ var gl = this._webGLContext = this._fetchWebGLContext(this.canvas, options);
+ if (!gl) { return null; }
+
+ this.updateSimultaneousTextureCount(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
+ this._maxTextureSlots = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+ this._createBuffers(gl);
+ this._initTextures(gl);
+
+ gl.disable(gl.DEPTH_TEST);
+ gl.enable(gl.BLEND);
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._premultiply);
+ //gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
+
+ this._webGLContext.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a);
+ this.updateViewport(this._viewportWidth || this.canvas.width, this._viewportHeight || this.canvas.height);
+ }
+ } else {
+ this._webGLContext = null;
+ }
+ return this._webGLContext;
+ };
+
+// public methods:
+ /**
+ * Docced in superclass
+ */
+ p.update = function (props) {
+ if (!this.canvas) { return; }
+ if (this.tickOnUpdate) { this.tick(props); }
+ this.dispatchEvent("drawstart");
+ if (this.autoClear) { this.clear(); }
+
+ if (this._webGLContext) {
+ // Use WebGL.
+ this._batchDraw(this, this._webGLContext);
+ if (this._autoPurge != -1 && !(this._drawID%((this._autoPurge/2)|0))) {
+ this.purgeTextures(this._autoPurge);
+ }
+ } else {
+ // Use 2D.
+ var ctx = this.canvas.getContext("2d");
+ ctx.save();
+ this.updateContext(ctx);
+ this.draw(ctx, false);
+ ctx.restore();
+ }
+ this.dispatchEvent("drawend");
+ };
+
+ /**
+ * Docced in superclass
+ */
+ p.clear = function () {
+ if (!this.canvas) { return; }
+ if (StageGL.isWebGLActive(this._webGLContext)) {
+ var gl = this._webGLContext;
+ var cc = this._clearColor;
+ var adjust = this._transparent ? cc.a : 1.0;
+ // Use WebGL settings; adjust for pre multiplied alpha appropriate to scenario
+ this._webGLContext.clearColor(cc.r * adjust, cc.g * adjust, cc.b * adjust, adjust);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._webGLContext.clearColor(cc.r, cc.g, cc.b, cc.a);
+ } else {
+ // Use 2D.
+ this.Stage_clear();
+ }
+ };
+
+ /**
+ * Draws the stage into the supplied context if possible. Many WebGL properties only exist on their context. As such
+ * you cannot share contexts among many StageGLs and each context requires a unique StageGL instance. Contexts that
+ * don't match the context managed by this StageGL will be treated as a 2D context.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D | WebGLRenderingContext} context The context object to draw into.
+ * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For
+ * example, used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
+ * @return {Boolean} If the draw was handled by this function
+ */
+ p.draw = function (context, ignoreCache) {
+ if (context === this._webGLContext && StageGL.isWebGLActive(this._webGLContext)) {
+ var gl = this._webGLContext;
+ this._batchDraw(this, gl, ignoreCache);
+ return true;
+ } else {
+ return this.Stage_draw(context, ignoreCache);
+ }
+ };
+
+ /**
+ * Draws the target into the correct context, be it a canvas or Render Texture using WebGL.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method cacheDraw
+ * @param {DisplayObject} target The object we're drawing into cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
+ * @param {Array} filters The filters we're drawing into cache.
+ * @param {BitmapCache} manager The BitmapCache instance looking after the cache
+ * @return {Boolean} If the draw was handled by this function
+ */
+ p.cacheDraw = function (target, filters, manager) {
+ if (StageGL.isWebGLActive(this._webGLContext)) {
+ var gl = this._webGLContext;
+ this._cacheDraw(gl, target, filters, manager);
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Blocks, or frees a texture "slot" on the GPU. Can be useful if you are overflowing textures. When overflowing
+ * textures they are re-uploaded to the GPU every time they're encountered, this can be expensive with large textures.
+ * By blocking the slot you reduce available slots, potentially increasing draw calls, but mostly you prevent a
+ * texture being re-uploaded if it would have moved slots due to overflow.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * For example, block the slot a background image is stored in so there is less re-loading of that image.
+ * @method protectTextureSlot
+ * @param {Number} id The slot to be affected
+ * @param {Boolean} [lock=false] Whether this slot is the one being locked.
+ */
+ p.protectTextureSlot = function (id, lock) {
+ if (id > this._maxTextureSlots || id < 0) {
+ throw "Slot outside of acceptable range";
+ }
+ this._slotBlacklist[id] = !!lock;
+ };
+
+ /**
+ * Render textures can't draw into themselves so any item being used for renderTextures needs two to alternate between.
+ * This function creates, gets, and toggles the render surface between the two.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method getTargetRenderTexture
+ * @param {DisplayObject} target The object associated with the render textures, usually a cached object.
+ * @param {Number} w The width to create the texture at.
+ * @param {Number} h The height to create the texture at.
+ * @return {Objet}
+ * @todo fill in return type
+ */
+ p.getTargetRenderTexture = function (target, w, h) {
+ var result, toggle = false;
+ var gl = this._webGLContext;
+ if (target.__lastRT !== undefined && target.__lastRT === target.__rtA) { toggle = true; }
+ if (!toggle) {
+ if (target.__rtA === undefined) {
+ target.__rtA = this.getRenderBufferTexture(w, h);
+ } else {
+ if (w != target.__rtA._width || h != target.__rtA._height) {
+ this.resizeTexture(target.__rtA, w, h);
+ }
+ this.setTextureParams(gl);
+ }
+ result = target.__rtA;
+ } else {
+ if (target.__rtB === undefined) {
+ target.__rtB = this.getRenderBufferTexture(w, h);
+ } else {
+ if (w != target.__rtB._width || h != target.__rtB._height) {
+ this.resizeTexture(target.__rtB, w, h);
+ }
+ this.setTextureParams(gl);
+ }
+ result = target.__rtB;
+ }
+ if (!result) {
+ throw "Problems creating render textures, known causes include using too much VRAM by not releasing WebGL texture instances";
+ }
+ target.__lastRT = result;
+ return result;
+ };
+
+ /**
+ * For every image encountered StageGL registers and tracks it automatically. This tracking can cause memory leaks
+ * if not purged. StageGL, by default, automatically purges them. This does have a cost and may unfortunately find
+ * false positives. This function is for manual management of this memory instead of the automatic system controlled
+ * by the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property.
+ *
+ * This function will recursively remove all textures found on the object, its children, cache, etc. It will uncache
+ * objects and remove any texture it finds REGARDLESS of whether it is currently in use elsewhere. It is up to the
+ * developer to ensure that a texture in use is not removed.
+ *
+ * Textures in use, or to be used again shortly, should not be removed. This is simply for performance reasons.
+ * Removing a texture in use will cause the texture to have to be re-uploaded slowing rendering.
+ * @method releaseTexture
+ * @param {DisplayObject | Texture | Image | Canvas} item An object that used the texture to be discarded.
+ */
+ p.releaseTexture = function (item) {
+ var i, l;
+ if (!item) { return; }
+
+ // this is a container object
+ if (item.children) {
+ for (i = 0, l = item.children.length; i < l; i++) {
+ this.releaseTexture(item.children[i]);
+ }
+ }
+
+ // this has a cache canvas
+ if (item.cacheCanvas) {
+ item.uncache();
+ }
+
+ var foundImage = undefined;
+ if (item._storeID !== undefined) {
+ // this is a texture itself
+ if (item === this._textureDictionary[item._storeID]) {
+ this._killTextureObject(item);
+ item._storeID = undefined;
+ return;
+ }
+
+ // this is an image or canvas
+ foundImage = item;
+ } else if (item._webGLRenderStyle === 2) {
+ // this is a Bitmap class
+ foundImage = item.image;
+ } else if (item._webGLRenderStyle === 1) {
+ // this is a SpriteSheet, we can't tell which image we used from the list easily so remove them all!
+ for (i = 0, l = item.spriteSheet._images.length; i < l; i++) {
+ this.releaseTexture(item.spriteSheet._images[i]);
+ }
+ return;
+ }
+
+ // did we find anything
+ if (foundImage === undefined) {
+ if (this.vocalDebug) {
+ console.log("No associated texture found on release");
+ }
+ return;
+ }
+
+ // remove it
+ this._killTextureObject(this._textureDictionary[foundImage._storeID]);
+ foundImage._storeID = undefined;
+ };
+
+ /**
+ * Similar to {{#crossLink "releaseTexture"}}{{/crossLink}}, but this function differs by searching for textures to
+ * release. It works by assuming that it can purge any texture which was last used more than "count" draw calls ago.
+ * Because this process is unaware of the objects and whether they may be used on your stage, false positives can
+ * occur. It is recommended to manually manage your memory with {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}},
+ * however, there are many use cases where this is simpler and error-free. This process is also run by default under
+ * the hood to prevent leaks. To disable it see the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property.
+ * @method purgeTextures
+ * @param {Number} [count=100] How many renders ago the texture was last used
+ */
+ p.purgeTextures = function (count) {
+ if (count == undefined){ count = 100; }
+
+ var dict = this._textureDictionary;
+ var l = dict.length;
+ for (var i= 0; inot update the canvas element's width/height, but
+ * the render surface's instead. This is necessary after manually resizing the canvas element on the DOM to avoid a
+ * up/down scaled render.
+ * @method updateViewport
+ * @param {Integer} width The width of the render surface in pixels.
+ * @param {Integer} height The height of the render surface in pixels.
+ */
+ p.updateViewport = function (width, height) {
+ this._viewportWidth = width|0;
+ this._viewportHeight = height|0;
+ var gl = this._webGLContext;
+
+ if (gl) {
+ gl.viewport(0, 0, this._viewportWidth, this._viewportHeight);
+
+ // WebGL works with a -1,1 space on its screen. It also follows Y-Up
+ // we need to flip the y, scale and then translate the co-ordinates to match this
+ // additionally we offset into they Y so the polygons are inside the camera's "clipping" plane
+ this._projectionMatrix = new Float32Array([
+ 2 / this._viewportWidth, 0, 0, 0,
+ 0, -2 / this._viewportHeight, 1, 0,
+ 0, 0, 1, 0,
+ -1, 1, 0.1, 0
+ ]);
+ // create the flipped version for use with render texture flipping
+ // DHG: this would be a slice/clone but some platforms don't offer them for Float32Array
+ this._projectionMatrixFlip = new Float32Array([0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]);
+ this._projectionMatrixFlip.set(this._projectionMatrix);
+ this._projectionMatrixFlip[5] *= -1;
+ this._projectionMatrixFlip[13] *= -1;
+ }
+ };
+
+ /**
+ * Fetches the shader compiled and set up to work with the provided filter/object. The shader is compiled on first
+ * use and returned on subsequent calls.
+ * @method getFilterShader
+ * @param {Filter|Object} filter The object which will provide the information needed to construct the filter shader.
+ * @return {WebGLProgram}
+ */
+ p.getFilterShader = function (filter) {
+ if (!filter) { filter = this; }
+
+ var gl = this._webGLContext;
+ var targetShader = this._activeShader;
+
+ if (filter._builtShader) {
+ targetShader = filter._builtShader;
+ if (filter.shaderParamSetup) {
+ gl.useProgram(targetShader);
+ filter.shaderParamSetup(gl, this, targetShader);
+ }
+ } else {
+ try {
+ targetShader = this._fetchShaderProgram(
+ gl, "filter",
+ filter.VTX_SHADER_BODY, filter.FRAG_SHADER_BODY,
+ filter.shaderParamSetup && filter.shaderParamSetup.bind(filter)
+ );
+ filter._builtShader = targetShader;
+ targetShader._name = filter.toString();
+ } catch (e) {
+ console && console.log("SHADER SWITCH FAILURE", e);
+ }
+ }
+ return targetShader;
+ };
+
+ /**
+ * Returns a base texture that has no image or data loaded. Not intended for loading images. It may return `null`
+ * in some error cases, and trying to use a "null" texture can cause renders to fail.
+ * @method getBaseTexture
+ * @param {uint} [w=1] The width of the texture in pixels, defaults to 1
+ * @param {uint} [h=1] The height of the texture in pixels, defaults to 1
+ */
+ p.getBaseTexture = function (w, h) {
+ var width = Math.ceil(w > 0 ? w : 1) || 1;
+ var height = Math.ceil(h > 0 ? h : 1) || 1;
+
+ var gl = this._webGLContext;
+ var texture = gl.createTexture();
+ this.resizeTexture(texture, width, height);
+ this.setTextureParams(gl, false);
+
+ return texture;
+ };
+
+ /**
+ * Resizes a supplied texture element. May generate invalid textures in some error cases such as; when the texture
+ * is too large, when an out of texture memory error occurs, or other error scenarios. Trying to use an invalid texture
+ * can cause renders to hard stop on some devices. Check the WebGL bound texture after running this function.
+ *
+ * NOTE: The supplied texture must have been made with the WebGL "texImage2D" function, all default APIs in StageGL
+ * use this, so this note only matters for library developers and plugins.
+ *
+ * @protected
+ * @method resizeTexture
+ * @param {WebGLTexture} texture The GL Texture to be modified.
+ * @param {uint} [width=1] The width of the texture in pixels, defaults to 1
+ * @param {uint} [height=1] The height of the texture in pixels, defaults to 1
+ */
+ p.resizeTexture = function (texture, width,height) {
+ var gl = this._webGLContext;
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(
+ gl.TEXTURE_2D, // target
+ 0, // level of detail
+ gl.RGBA, // internal format
+ width, height, 0, // width, height, border (only for array/null sourced textures)
+ gl.RGBA, // format (match internal format)
+ gl.UNSIGNED_BYTE, // type of texture(pixel color depth)
+ null // image data, we can do null because we're doing array data
+ );
+ texture.width = width;
+ texture.height = height;
+ };
+
+ /**
+ * Returns a base texture (see {{#crossLink "StageGL/getBaseTexture"}}{{/crossLink}}) for details. Also includes an
+ * attached and linked render buffer in `texture._frameBuffer`. RenderTextures can be thought of as an internal
+ * canvas on the GPU that can be drawn to.
+ * @method getRenderBufferTexture
+ * @param {Number} w The width of the texture in pixels.
+ * @param {Number} h The height of the texture in pixels.
+ * @return {Texture} the basic texture instance with a render buffer property.
+ */
+ p.getRenderBufferTexture = function (w, h) {
+ var gl = this._webGLContext;
+
+ // get the texture
+ var renderTexture = this.getBaseTexture(w, h);
+ if (!renderTexture) { return null; }
+
+ // get the frame buffer
+ var frameBuffer = gl.createFramebuffer();
+ if (!frameBuffer) { return null; }
+
+ // set its width and height for spoofing as an image
+ renderTexture.width = w;
+ renderTexture.height = h;
+
+ // attach frame buffer to texture and provide cross links to look up each other
+ gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTexture, 0);
+ frameBuffer._renderTexture = renderTexture;
+ renderTexture._frameBuffer = frameBuffer;
+
+ // these keep track of themselves simply to reduce complexity of some lookup code
+ renderTexture._storeID = this._textureDictionary.length;
+ this._textureDictionary[renderTexture._storeID] = renderTexture;
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ return renderTexture;
+ };
+
+ /**
+ * Common utility function used to apply the correct texture processing parameters for the bound texture.
+ * @method setTextureParams
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @param {Boolean} [isPOT=false] Marks whether the texture is "Power of Two", this may allow better quality AA.
+ */
+ p.setTextureParams = function (gl, isPOT) {
+ if (isPOT && this._antialias) {
+ //non POT linear works in some devices, but performance is NOT good, investigate
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+ } else {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ }
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ };
+
+ /**
+ * Changes the webGL clear, aka "background" color to the provided value. A transparent clear is recommended, as
+ * non-transparent colours may create undesired boxes around some visuals.
+ *
+ * The clear color will also be used for filters and other "render textures". The stage background will ignore the
+ * transparency value and display a solid color normally. For the stage to recognize and use transparency it must be
+ * created with the transparent flag set to `true` (see {{#crossLink "StageGL/constructor"}}{{/crossLink}})).
+ *
+ * Using "transparent white" to demonstrate, the valid data formats are as follows:
+ *
+ * - "#FFF"
+ * - "#FFFFFF"
+ * - "#FFFFFF00"
+ * - "rgba(255,255,255,0.0)"
+ *
+ * @method setClearColor
+ * @param {String|int} [color=0x00000000] The new color to use as the background
+ */
+ p.setClearColor = function (color) {
+ var r, g, b, a, output;
+
+ if (typeof color == "string") {
+ if (color.indexOf("#") == 0) {
+ if (color.length == 4) {
+ color = "#" + color.charAt(1)+color.charAt(1) + color.charAt(2)+color.charAt(2) + color.charAt(3)+color.charAt(3)
+ }
+ r = Number("0x"+color.slice(1, 3))/255;
+ g = Number("0x"+color.slice(3, 5))/255;
+ b = Number("0x"+color.slice(5, 7))/255;
+ a = Number("0x"+color.slice(7, 9))/255;
+ } else if (color.indexOf("rgba(") == 0) {
+ output = color.slice(5, -1).split(",");
+ r = Number(output[0])/255;
+ g = Number(output[1])/255;
+ b = Number(output[2])/255;
+ a = Number(output[3]);
+ }
+ } else { // >>> is an unsigned shift which is what we want as 0x80000000 and up are negative values
+ r = ((color & 0xFF000000) >>> 24)/255;
+ g = ((color & 0x00FF0000) >>> 16)/255;
+ b = ((color & 0x0000FF00) >>> 8)/255;
+ a = (color & 0x000000FF)/255;
+ }
+
+ this._clearColor.r = r || 0;
+ this._clearColor.g = g || 0;
+ this._clearColor.b = b || 0;
+ this._clearColor.a = a || 0;
+
+ if (!this._webGLContext) { return; }
+ this._webGLContext.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a);
+ };
+
+ /**
+ * docced in subclass
+ */
+ p.toString = function () {
+ return "[StageGL (name="+ this.name +")]";
+ };
+
+// private methods:
+ /**
+ * Sets up and returns the WebGL context for the canvas. May return undefined in error scenarios. These can include
+ * situations where the canvas element already has a context, 2D or GL.
+ * @param {Canvas} canvas The DOM canvas element to attach to
+ * @param {Object} options The options to be handed into the WebGL object, see WebGL spec
+ * @method _fetchWebGLContext
+ * @protected
+ * @return {WebGLRenderingContext} The WebGL context, may return undefined in error scenarios
+ */
+ p._fetchWebGLContext = function (canvas, options) {
+ var gl;
+
+ try {
+ gl = canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options);
+ } catch (e) {
+ // don't do anything in catch, null check will handle it
+ }
+
+ if (!gl) {
+ var msg = "Could not initialize WebGL";
+ console.error?console.error(msg):console.log(msg);
+ } else {
+ gl.viewportWidth = canvas.width;
+ gl.viewportHeight = canvas.height;
+ }
+
+ return gl;
+ };
+
+ /**
+ * Create the completed Shader Program from the vertex and fragment shaders. Allows building of custom shaders for
+ * filters. Once compiled, shaders are saved so. If the Shader code requires dynamic alterations re-run this function
+ * to generate a new shader.
+ * @method _fetchShaderProgram
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @param {String} [shaderName="regular"] Working values: "regular", "override", and "filter". Which type of shader to build.
+ * Filter and override both accept the custom params. Regular and override have all features. Filter is a special case reduced feature shader meant to be over-ridden.
+ * @param {String} [customVTX] Extra vertex shader information to replace a regular draw, see
+ * {{#crossLink "StageGL/COVER_VERTEX_BODY"}}{{/crossLink}} for default and {{#crossLink "Filter"}}{{/crossLink}} for examples.
+ * @param {String} [customFRAG] Extra fragment shader information to replace a regular draw, see
+ * {{#crossLink "StageGL/COVER_FRAGMENT_BODY"}}{{/crossLink}} for default and {{#crossLink "Filter"}}{{/crossLink}} for examples.
+ * @param {Function} [shaderParamSetup] Function to run so custom shader parameters can get applied for the render.
+ * @protected
+ * @return {WebGLProgram} The compiled and linked shader
+ */
+ p._fetchShaderProgram = function (gl, shaderName, customVTX, customFRAG, shaderParamSetup) {
+ gl.useProgram(null); // safety to avoid collisions
+
+ // build the correct shader string out of the right headers and bodies
+ var targetFrag, targetVtx;
+ switch (shaderName) {
+ case "filter":
+ targetVtx = StageGL.COVER_VERTEX_HEADER + (customVTX || StageGL.COVER_VERTEX_BODY);
+ targetFrag = StageGL.COVER_FRAGMENT_HEADER + (customFRAG || StageGL.COVER_FRAGMENT_BODY);
+ break;
+ case "particle": //TODO
+ targetVtx = StageGL.REGULAR_VERTEX_HEADER + StageGL.PARTICLE_VERTEX_BODY;
+ targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + StageGL.PARTICLE_FRAGMENT_BODY;
+ break;
+ case "override":
+ targetVtx = StageGL.REGULAR_VERTEX_HEADER + (customVTX || StageGL.REGULAR_VERTEX_BODY);
+ targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + (customFRAG || StageGL.REGULAR_FRAGMENT_BODY);
+ break;
+ case "regular":
+ default:
+ targetVtx = StageGL.REGULAR_VERTEX_HEADER + StageGL.REGULAR_VERTEX_BODY;
+ targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + StageGL.REGULAR_FRAGMENT_BODY;
+ break;
+ }
+
+ // create the separate vars
+ var vertexShader = this._createShader(gl, gl.VERTEX_SHADER, targetVtx);
+ var fragmentShader = this._createShader(gl, gl.FRAGMENT_SHADER, targetFrag);
+
+ // link them together
+ var shaderProgram = gl.createProgram();
+ gl.attachShader(shaderProgram, vertexShader);
+ gl.attachShader(shaderProgram, fragmentShader);
+ gl.linkProgram(shaderProgram);
+ shaderProgram._type = shaderName;
+
+ // check compile status
+ if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+ gl.useProgram(this._activeShader);
+ throw gl.getProgramInfoLog(shaderProgram);
+ }
+
+ // set up the parameters on the shader
+ gl.useProgram(shaderProgram);
+ switch (shaderName) {
+ case "filter":
+ // get the places in memory the shader is stored so we can feed information into them
+ // then save it off on the shader because it's so tied to the shader itself
+ shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
+ gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
+
+ shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
+ gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
+
+ shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
+ gl.uniform1i(shaderProgram.samplerUniform, 0);
+
+ shaderProgram.uprightUniform = gl.getUniformLocation(shaderProgram, "uUpright");
+ gl.uniform1f(shaderProgram.uprightUniform, 0);
+
+ // if there's some custom attributes be sure to hook them up
+ if (shaderParamSetup) {
+ shaderParamSetup(gl, this, shaderProgram);
+ }
+ break;
+ case "override":
+ case "particle":
+ case "regular":
+ default:
+ // get the places in memory the shader is stored so we can feed information into them
+ // then save it off on the shader because it's so tied to the shader itself
+ shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
+ gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
+
+ shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
+ gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
+
+ shaderProgram.textureIndexAttribute = gl.getAttribLocation(shaderProgram, "textureIndex");
+ gl.enableVertexAttribArray(shaderProgram.textureIndexAttribute);
+
+ shaderProgram.alphaAttribute = gl.getAttribLocation(shaderProgram, "objectAlpha");
+ gl.enableVertexAttribArray(shaderProgram.alphaAttribute);
+
+ var samplers = [];
+ for (var i = 0; i < this._batchTextureCount; i++) {
+ samplers[i] = i;
+ }
+
+ shaderProgram.samplerData = samplers;
+ shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
+ gl.uniform1iv(shaderProgram.samplerUniform, samplers);
+
+ shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "pMatrix");
+ break;
+ }
+
+ gl.useProgram(this._activeShader);
+ return shaderProgram;
+ };
+
+ /**
+ * Creates a shader from the specified string replacing templates. Template items are defined via `{{` `key` `}}``.
+ * @method _createShader
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @param {Number} type The type of shader to create. gl.VERTEX_SHADER | gl.FRAGMENT_SHADER
+ * @param {String} str The definition for the shader.
+ * @return {WebGLShader}
+ * @protected
+ */
+ p._createShader = function (gl, type, str) {
+ // inject the static number
+ str = str.replace(/{{count}}/g, this._batchTextureCount);
+
+ // resolve issue with no dynamic samplers by creating correct samplers in if else chain
+ // TODO: WebGL 2.0 does not need this support
+ var insert = "";
+ for (var i = 1; i gl.MAX_TEXTURE_SIZE || image.height > gl.MAX_TEXTURE_SIZE){
+ console && console.error("Oversized Texture: "+ image.width+"x"+image.height +" vs "+ gl.MAX_TEXTURE_SIZE +"max");
+ }
+ }
+ };
+
+ /**
+ * Adds the texture to a spot in the current batch, forcing a draw if no spots are free.
+ * @method _insertTextureInBatch
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @param {WebGLTexture} texture The texture to be inserted.
+ * @protected
+ */
+ p._insertTextureInBatch = function (gl, texture) {
+ // if it wasn't used last batch
+ if (this._batchTextures[texture._activeIndex] !== texture) {
+ // we've got to find it a a spot.
+ var found = -1;
+ var start = (this._lastTextureInsert+1) % this._batchTextureCount;
+ var look = start;
+ do {
+ if (this._batchTextures[look]._batchID != this._batchID && !this._slotBlacklist[look]) {
+ found = look;
+ break;
+ }
+ look = (look+1) % this._batchTextureCount;
+ } while (look !== start);
+
+ // we couldn't find anywhere for it go, meaning we're maxed out
+ if (found === -1) {
+ this.batchReason = "textureOverflow";
+ this._drawBuffers(gl); // <------------------------------------------------------------------------
+ this.batchCardCount = 0;
+ found = start;
+ }
+
+ // lets put it into that spot
+ this._batchTextures[found] = texture;
+ texture._activeIndex = found;
+ var image = texture._imageData;
+ if (image && image._invalid && texture._drawID !== undefined) {
+ this._updateTextureImageData(gl, image);
+ } else {
+ gl.activeTexture(gl.TEXTURE0 + found);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ this.setTextureParams(gl);
+ }
+ this._lastTextureInsert = found;
+ } else {
+ var image = texture._imageData;
+ if (texture._storeID != undefined && image && image._invalid) {
+ this._updateTextureImageData(gl, image);
+ }
+ }
+
+ texture._drawID = this._drawID;
+ texture._batchID = this._batchID;
+ };
+
+ /**
+ * Remove and clean the texture, expects a texture and is inflexible. Mostly for internal use, recommended to call
+ * {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} instead as it will call this with the correct texture object(s).
+ * Note: Testing shows this may not happen immediately, have to wait frames for WebGL to have actually adjust memory.
+ * @method _killTextureObject
+ * @param {Texture} tex The texture to be cleaned out
+ * @protected
+ */
+ p._killTextureObject = function (tex) {
+ if (!tex) { return; }
+ var gl = this._webGLContext;
+
+ // remove linkage
+ if (tex._storeID !== undefined && tex._storeID >= 0) {
+ this._textureDictionary[tex._storeID] = undefined;
+ for (var n in this._textureIDs) {
+ if (this._textureIDs[n] == tex._storeID) { delete this._textureIDs[n]; }
+ }
+ if(tex._imageData) { tex._imageData._storeID = undefined; }
+ tex._imageData = tex._storeID = undefined;
+ }
+
+ // make sure to drop it out of an active slot
+ if (tex._activeIndex !== undefined && this._batchTextures[tex._activeIndex] === tex) {
+ this._batchTextures[tex._activeIndex] = this._baseTextures[tex._activeIndex];
+ }
+
+ // remove buffers if present
+ try {
+ if (tex._frameBuffer) { gl.deleteFramebuffer(tex._frameBuffer); }
+ tex._frameBuffer = undefined;
+ } catch(e) {
+ /* suppress delete errors because it's already gone or didn't need deleting probably */
+ if (this.vocalDebug) { console.log(e); }
+ }
+
+ // remove entry
+ try {
+ gl.deleteTexture(tex);
+ } catch(e) {
+ /* suppress delete errors because it's already gone or didn't need deleting probably */
+ if (this.vocalDebug) { console.log(e); }
+ }
+ };
+
+ /**
+ * Store or restore current batch textures into a backup array
+ * @method _backupBatchTextures
+ * @param {Boolean} restore Perform a restore instead of a store.
+ * @param {Array} [target=this._backupTextures] Where to perform the backup, defaults to internal backup.
+ * @protected
+ */
+ p._backupBatchTextures = function (restore, target) {
+ var gl = this._webGLContext;
+
+ if (!this._backupTextures) { this._backupTextures = []; }
+ if (target === undefined) { target = this._backupTextures; }
+
+ for (var i=0; i 0) {
+ this._drawBuffers(gl);
+ }
+ this._isDrawing++;
+ this._drawID++;
+
+ this.batchCardCount = 0;
+ this.depth = 0;
+
+ this._appendToBatchGroup(sceneGraph, gl, new createjs.Matrix2D(), this.alpha, ignoreCache);
+
+ this.batchReason = "drawFinish";
+ this._drawBuffers(gl); // <--------------------------------------------------------
+ this._isDrawing--;
+ };
+
+ /**
+ * Perform the drawing process to fill a specific cache texture, including applying filters.
+ * @method _cacheDraw
+ * @param {DisplayObject} target The object we're drawing into the cache. For example, used for drawing the cache
+ * (to prevent it from simply drawing an existing cache back into itself).
+ * @param {Array} filters The filters we're drawing into cache.
+ * @param {BitmapCache} manager The BitmapCache instance looking after the cache
+ * @protected
+ */
+ p._cacheDraw = function (gl, target, filters, manager) {
+ /*
+ Implicitly there are 4 modes to this function: filtered-sameContext, filtered-uniqueContext, sameContext, uniqueContext.
+ Each situation must be handled slightly differently as 'sameContext' or 'uniqueContext' define how the output works,
+ one drawing directly into the main context and the other drawing into a stored renderTexture respectively.
+ When the draw is a 'filtered' draw, the filters are applied sequentially and will draw into saved textures repeatedly.
+ Once the final filter is done the final output is treated depending upon whether it is a same or unique context.
+ The internal complexity comes from reducing over-draw, shared code, and issues like textures needing to be flipped
+ sometimes when written to render textures.
+ */
+ var renderTexture;
+ var shaderBackup = this._activeShader;
+ var blackListBackup = this._slotBlacklist;
+ var lastTextureSlot = this._maxTextureSlots-1;
+ var wBackup = this._viewportWidth, hBackup = this._viewportHeight;
+
+ // protect the last slot so that we have somewhere to bind the renderTextures so it doesn't get upset
+ this.protectTextureSlot(lastTextureSlot, true);
+
+ // create offset container for drawing item
+ var mtx = target.getMatrix();
+ mtx = mtx.clone();
+ mtx.scale(1/manager.scale, 1/manager.scale);
+ mtx = mtx.invert();
+ mtx.translate(-manager.offX/manager.scale*target.scaleX, -manager.offY/manager.scale*target.scaleY);
+ var container = this._cacheContainer;
+ container.children = [target];
+ container.transformMatrix = mtx;
+
+ this._backupBatchTextures(false);
+
+ if (filters && filters.length) {
+ this._drawFilters(target, filters, manager);
+ } else {
+ // is this for another stage or mine?
+ if (this.isCacheControlled) {
+ // draw item to canvas I -> C
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._batchDraw(container, gl, true);
+ } else {
+ gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
+ target.cacheCanvas = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
+ renderTexture = target.cacheCanvas;
+
+ // draw item to render texture I -> T
+ gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
+ this.updateViewport(manager._drawWidth, manager._drawHeight);
+ this._projectionMatrix = this._projectionMatrixFlip;
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._batchDraw(container, gl, true);
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this.updateViewport(wBackup, hBackup);
+ }
+ }
+
+ this._backupBatchTextures(true);
+
+ this.protectTextureSlot(lastTextureSlot, false);
+ this._activeShader = shaderBackup;
+ this._slotBlacklist = blackListBackup;
+ };
+
+ /**
+ * Sub portion of _cacheDraw, split off for readability. Do not call independently.
+ * @method _drawFilters
+ * @param {DisplayObject} target The object we're drawing with a filter.
+ * @param {Array} filters The filters we're drawing into cache.
+ * @param {BitmapCache} manager The BitmapCache instance looking after the cache
+ */
+ p._drawFilters = function (target, filters, manager) {
+ var gl = this._webGLContext;
+ var renderTexture;
+ var lastTextureSlot = this._maxTextureSlots-1;
+ var wBackup = this._viewportWidth, hBackup = this._viewportHeight;
+
+ var container = this._cacheContainer;
+ var filterCount = filters.length;
+
+ // we don't know which texture slot we're dealing with previously and we need one out of the way
+ // once we're using that slot activate it so when we make and bind our RenderTexture it's safe there
+ gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
+ renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
+
+ // draw item to render texture I -> T
+ gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
+ this.updateViewport(manager._drawWidth, manager._drawHeight);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._batchDraw(container, gl, true);
+
+ // bind the result texture to slot 0 as all filters and cover draws assume original content is in slot 0
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, renderTexture);
+ this.setTextureParams(gl);
+
+ var flipY = false;
+ var i = 0, filter = filters[i];
+ do { // this is safe because we wouldn't be in apply filters without a filter count of at least 1
+
+ // swap to correct shader
+ this._activeShader = this.getFilterShader(filter);
+ if (!this._activeShader) { continue; }
+
+ // now the old result is stored in slot 0, make a new render texture
+ gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
+ renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
+
+ // draw result to render texture R -> T
+ gl.viewport(0, 0, manager._drawWidth, manager._drawHeight);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._drawCover(gl, flipY);
+
+ // bind the result texture to slot 0 as all filters and cover draws assume original content is in slot 0
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, renderTexture);
+ this.setTextureParams(gl);
+
+ // use flipping to keep things upright, things already cancel out on a single filter
+ // this needs to be here as multiPass is not accurate to _this_ frame until after shader acquisition
+ if (filterCount > 1 || filters[0]._multiPass) {
+ flipY = !flipY;
+ }
+
+ // work through the multipass if it's there, otherwise move on
+ filter = filter._multiPass !== null ? filter._multiPass : filters[++i];
+ } while (filter);
+
+ // is this for another stage or mine
+ if (this.isCacheControlled) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this.updateViewport(wBackup, hBackup);
+
+ // draw result to canvas R -> C
+ this._activeShader = this.getFilterShader(this);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._drawCover(gl, flipY);
+ } else {
+ //TODO: DHG: this is less than ideal. A flipped initial render for this circumstance might help. Adjust the perspective matrix?
+ if (flipY) {
+ gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
+ renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
+
+ this._activeShader = this.getFilterShader(this);
+ gl.viewport(0, 0, manager._drawWidth, manager._drawHeight);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ this._drawCover(gl, !flipY);
+ }
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ this.updateViewport(wBackup, hBackup);
+
+ // make sure the last texture is the active thing to draw
+ target.cacheCanvas = renderTexture;
+ }
+ };
+
+ /**
+ * Add all the contents of a container to the pending buffers, called recursively on each container. This may
+ * trigger a draw if a buffer runs out of space. This is the main workforce of the render loop.
+ * @method _appendToBatchGroup
+ * @param {Container} container The {{#crossLink "Container"}}{{/crossLink}} that contains everything to be drawn.
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @param {Matrix2D} concatMtx The effective (concatenated) transformation matrix when beginning this container
+ * @param {Number} concatAlpha The effective (concatenated) alpha when beginning this container
+ * @param {Boolean} ignoreCache Don't use an element's cache during this draw
+ * @protected
+ */
+ p._appendToBatchGroup = function (container, gl, concatMtx, concatAlpha, ignoreCache) {
+ // sort out shared properties
+ if (!container._glMtx) { container._glMtx = new createjs.Matrix2D(); }
+ var cMtx = container._glMtx;
+ cMtx.copy(concatMtx);
+ if (container.transformMatrix) {
+ cMtx.appendMatrix(container.transformMatrix);
+ } else {
+ cMtx.appendTransform(
+ container.x, container.y,
+ container.scaleX, container.scaleY,
+ container.rotation, container.skewX, container.skewY,
+ container.regX, container.regY
+ );
+ }
+
+ // sub components of figuring out the position an object holds
+ var subL, subT, subR, subB;
+
+ // actually apply its data to the buffers
+ var l = container.children.length;
+ for (var i = 0; i < l; i++) {
+ var item = container.children[i];
+
+ if (!(item.visible && concatAlpha)) { continue; }
+ if (!item.cacheCanvas || ignoreCache) {
+ if (item._updateState){
+ item._updateState();
+ }
+ if (item.children) {
+ this._appendToBatchGroup(item, gl, cMtx, item.alpha * concatAlpha);
+ continue;
+ }
+ }
+
+ // check for overflowing batch, if yes then force a render
+ // TODO: DHG: consider making this polygon count dependant for things like vector draws
+ if (this.batchCardCount+1 > this._maxCardsPerBatch) {
+ this.batchReason = "vertexOverflow";
+ this._drawBuffers(gl); // <------------------------------------------------------------
+ this.batchCardCount = 0;
+ }
+
+ // keep track of concatenated position
+ if (!item._glMtx) { item._glMtx = new createjs.Matrix2D(); }
+ var iMtx = item._glMtx;
+ iMtx.copy(cMtx);
+ if (item.transformMatrix) {
+ iMtx.appendMatrix(item.transformMatrix);
+ } else {
+ iMtx.appendTransform(
+ item.x, item.y,
+ item.scaleX, item.scaleY,
+ item.rotation, item.skewX, item.skewY,
+ item.regX, item.regY
+ );
+ }
+
+ var uvRect, texIndex, image, frame, texture, src;
+ var useCache = item.cacheCanvas && !ignoreCache;
+
+ if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas
+ image = (ignoreCache?false:item.cacheCanvas) || item.image;
+ } else if (item._webGLRenderStyle === 1) { // SPRITE
+ frame = item.spriteSheet.getFrame(item.currentFrame); //TODO: Faster way?
+ if (frame === null) { continue; }
+ image = frame.image;
+ } else { // MISC (DOM objects render themselves later)
+ continue;
+ }
+
+ var uvs = this._uvs;
+ var vertices = this._vertices;
+ var texI = this._indices;
+ var alphas = this._alphas;
+
+ // calculate texture
+ if (!image) { continue; }
+ if (image._storeID === undefined) {
+ // this texture is new to us so load it and add it to the batch
+ texture = this._loadTextureImage(gl, image);
+ this._insertTextureInBatch(gl, texture);
+ } else {
+ // fetch the texture (render textures know how to look themselves up to simplify this logic)
+ texture = this._textureDictionary[image._storeID];
+ if (!texture){
+ if (this.vocalDebug){ console.log("Texture should not be looked up while not being stored."); }
+ continue;
+ }
+
+ // put it in the batch if needed
+ if (texture._batchID !== this._batchID) {
+ this._insertTextureInBatch(gl, texture);
+ }
+ }
+ texIndex = texture._activeIndex;
+
+ if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas
+ if (!useCache && item.sourceRect) {
+ // calculate uvs
+ if (!item._uvRect) { item._uvRect = {}; }
+ src = item.sourceRect;
+ uvRect = item._uvRect;
+ uvRect.t = (src.y)/image.height;
+ uvRect.l = (src.x)/image.width;
+ uvRect.b = (src.y + src.height)/image.height;
+ uvRect.r = (src.x + src.width)/image.width;
+
+ // calculate vertices
+ subL = 0; subT = 0;
+ subR = src.width+subL; subB = src.height+subT;
+ } else {
+ // calculate uvs
+ uvRect = StageGL.UV_RECT;
+ // calculate vertices
+ if (useCache) {
+ src = item.bitmapCache;
+ subL = src.x+(src._filterOffX/src.scale); subT = src.y+(src._filterOffY/src.scale);
+ subR = (src._drawWidth/src.scale)+subL; subB = (src._drawHeight/src.scale)+subT;
+ } else {
+ subL = 0; subT = 0;
+ subR = image.width+subL; subB = image.height+subT;
+ }
+ }
+ } else if (item._webGLRenderStyle === 1) { // SPRITE
+ var rect = frame.rect;
+
+ // calculate uvs
+ uvRect = frame.uvRect;
+ if (!uvRect) {
+ uvRect = StageGL.buildUVRects(item.spriteSheet, item.currentFrame, false);
+ }
+
+ // calculate vertices
+ subL = -frame.regX; subT = -frame.regY;
+ subR = rect.width-frame.regX; subB = rect.height-frame.regY;
+ }
+
+ // These must be calculated here else a forced draw might happen after they're set
+ var offV1 = this.batchCardCount*StageGL.INDICIES_PER_CARD; // offset for 1 component vectors
+ var offV2 = offV1*2; // offset for 2 component vectors
+
+ //DHG: See Matrix2D.transformPoint for why this math specifically
+ // apply vertices
+ vertices[offV2] = subL *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+1] = subL *iMtx.b + subT *iMtx.d +iMtx.ty;
+ vertices[offV2+2] = subL *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+3] = subL *iMtx.b + subB *iMtx.d +iMtx.ty;
+ vertices[offV2+4] = subR *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+5] = subR *iMtx.b + subT *iMtx.d +iMtx.ty;
+ vertices[offV2+6] = vertices[offV2+2]; vertices[offV2+7] = vertices[offV2+3];
+ vertices[offV2+8] = vertices[offV2+4]; vertices[offV2+9] = vertices[offV2+5];
+ vertices[offV2+10] = subR *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+11] = subR *iMtx.b + subB *iMtx.d +iMtx.ty;
+
+ // apply uvs
+ uvs[offV2] = uvRect.l; uvs[offV2+1] = uvRect.t;
+ uvs[offV2+2] = uvRect.l; uvs[offV2+3] = uvRect.b;
+ uvs[offV2+4] = uvRect.r; uvs[offV2+5] = uvRect.t;
+ uvs[offV2+6] = uvRect.l; uvs[offV2+7] = uvRect.b;
+ uvs[offV2+8] = uvRect.r; uvs[offV2+9] = uvRect.t;
+ uvs[offV2+10] = uvRect.r; uvs[offV2+11] = uvRect.b;
+
+ // apply texture
+ texI[offV1] = texI[offV1+1] = texI[offV1+2] = texI[offV1+3] = texI[offV1+4] = texI[offV1+5] = texIndex;
+
+ // apply alpha
+ alphas[offV1] = alphas[offV1+1] = alphas[offV1+2] = alphas[offV1+3] = alphas[offV1+4] = alphas[offV1+5] = item.alpha * concatAlpha;
+
+ this.batchCardCount++;
+ }
+ };
+
+ /**
+ * Draws all the currently defined cards in the buffer to the render surface.
+ * @method _drawBuffers
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @protected
+ */
+ p._drawBuffers = function (gl) {
+ if (this.batchCardCount <= 0) { return; } // prevents error logs on stages filled with un-renederable content.
+
+ if (this.vocalDebug) {
+ console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason);
+ }
+ var shaderProgram = this._activeShader;
+ var vertexPositionBuffer = this._vertexPositionBuffer;
+ var textureIndexBuffer = this._textureIndexBuffer;
+ var uvPositionBuffer = this._uvPositionBuffer;
+ var alphaBuffer = this._alphaBuffer;
+
+ gl.useProgram(shaderProgram);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
+ gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._vertices);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, textureIndexBuffer);
+ gl.vertexAttribPointer(shaderProgram.textureIndexAttribute, textureIndexBuffer.itemSize, gl.FLOAT, false, 0, 0);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._indices);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer);
+ gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._uvs);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, alphaBuffer);
+ gl.vertexAttribPointer(shaderProgram.alphaAttribute, alphaBuffer.itemSize, gl.FLOAT, false, 0, 0);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._alphas);
+
+ gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, gl.FALSE, this._projectionMatrix);
+
+ for (var i = 0; i < this._batchTextureCount; i++) {
+ var texture = this._batchTextures[i];
+ gl.activeTexture(gl.TEXTURE0 + i);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ this.setTextureParams(gl, texture.isPOT);
+ }
+
+ gl.drawArrays(gl.TRIANGLES, 0, this.batchCardCount*StageGL.INDICIES_PER_CARD);
+ this._batchID++;
+ };
+
+ /**
+ * Draws a card that covers the entire render surface. Mainly used for filters.
+ * @method _drawBuffers
+ * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
+ * @param {Boolean} flipY Covers are used for things like RenderTextures and because of 3D vs Canvas space this can
+ * end up meaning the `y` space sometimes requires flipping in the render.
+ * @protected
+ */
+ p._drawCover = function (gl, flipY) {
+ if (this._isDrawing > 0) {
+ this._drawBuffers(gl);
+ }
+
+ if (this.vocalDebug) {
+ console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ "Cover");
+ }
+ var shaderProgram = this._activeShader;
+ var vertexPositionBuffer = this._vertexPositionBuffer;
+ var uvPositionBuffer = this._uvPositionBuffer;
+
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.useProgram(shaderProgram);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
+ gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, StageGL.COVER_VERT);
+ gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer);
+ gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, flipY?StageGL.COVER_UV_FLIP:StageGL.COVER_UV);
+
+ gl.uniform1i(shaderProgram.samplerUniform, 0);
+ gl.uniform1f(shaderProgram.uprightUniform, flipY?0:1);
+
+ gl.drawArrays(gl.TRIANGLES, 0, StageGL.INDICIES_PER_CARD);
+ };
+
+ createjs.StageGL = createjs.promote(StageGL, "Stage");
+}());
+
+//##############################################################################
+// Bitmap.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+
+ /**
+ * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
+ * HTML element, or a string.
+ *
+ * Example
+ *
+ * var bitmap = new createjs.Bitmap("imagePath.jpg");
+ *
+ * Notes:
+ *
+ * - When using a video source that may loop or seek, use a {{#crossLink "VideoBuffer"}}{{/crossLink}} object to
+ * prevent blinking / flashing.
+ *
- When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
+ * will be displayed.
+ * - Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
+ * the Bitmap can be cached.
+ * - Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
+ * happens in all browsers except recent Firefox builds.
+ * - Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using
+ * methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting
+ * `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`
+ *
+ *
+ * @class Bitmap
+ * @extends DisplayObject
+ * @constructor
+ * @param {CanvasImageSource | String | Object} imageOrUri The source image to display. This can be a CanvasImageSource
+ * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image.
+ * If the latter, a new Image instance with the URL as its src will be used.
+ **/
+ function Bitmap(imageOrUri) {
+ this.DisplayObject_constructor();
+
+
+ // public properties:
+ /**
+ * The source image to display. This can be a CanvasImageSource
+ * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image.
+ * If the latter, a new Image instance with the URL as its src will be used.
+ * @property image
+ * @type CanvasImageSource | Object
+ **/
+ if (typeof imageOrUri == "string") {
+ this.image = document.createElement("img");
+ this.image.src = imageOrUri;
+ } else {
+ this.image = imageOrUri;
+ }
+
+ /**
+ * Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
+ * Notes:
+ *
+ * - that video sources must have a width / height set to work correctly with `sourceRect`
+ * - Cached objects will ignore the `sourceRect` property
+ *
+ * @property sourceRect
+ * @type Rectangle
+ * @default null
+ */
+ this.sourceRect = null;
+
+ // private properties:
+ /**
+ * Docced in superclass.
+ */
+ this._webGLRenderStyle = createjs.DisplayObject._StageGL_BITMAP;
+ }
+ var p = createjs.extend(Bitmap, createjs.DisplayObject);
+
+
+// public methods:
+ /**
+ * Constructor alias for backwards compatibility. This method will be removed in future versions.
+ * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
+ * @method initialize
+ * @deprecated in favour of `createjs.promote()`
+ **/
+ p.initialize = Bitmap; // TODO: deprecated.
+
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ var image = this.image;
+ var hasContent = this.cacheCanvas || (image && (image.naturalWidth || image.getContext || image.readyState >= 2));
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
+ };
+
+ /**
+ * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
+ * @return {Boolean}
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
+ var img = this.image, rect = this.sourceRect;
+ if (img.getImage) { img = img.getImage(); }
+ if (!img) { return true; }
+ if (rect) {
+ // some browsers choke on out of bound values, so we'll fix them:
+ var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, x = 0, y = 0, w = img.width, h = img.height;
+ if (x1 < 0) { x -= x1; x1 = 0; }
+ if (x2 > w) { x2 = w; }
+ if (y1 < 0) { y -= y1; y1 = 0; }
+ if (y2 > h) { y2 = h; }
+ ctx.drawImage(img, x1, y1, x2-x1, y2-y1, x, y, x2-x1, y2-y1);
+ } else {
+ ctx.drawImage(img, 0, 0);
+ }
+ return true;
+ };
+
+ //Note, the doc sections below document using the specified APIs (from DisplayObject) from
+ //Bitmap. This is why they have no method implementations.
+
+ /**
+ * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
+ * You should not cache Bitmap instances as it can degrade performance.
+ *
+ * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
+ * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
+ * method.
+ * @method cache
+ **/
+
+ /**
+ * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
+ * You should not cache Bitmap instances as it can degrade performance.
+ *
+ * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
+ * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
+ * method.
+ * @method updateCache
+ **/
+
+ /**
+ * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
+ * You should not cache Bitmap instances as it can degrade performance.
+ *
+ * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
+ * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
+ * method.
+ * @method uncache
+ **/
+
+ /**
+ * Docced in superclass.
+ */
+ p.getBounds = function() {
+ var rect = this.DisplayObject_getBounds();
+ if (rect) { return rect; }
+ var image = this.image, o = this.sourceRect || image;
+ var hasContent = (image && (image.naturalWidth || image.getContext || image.readyState >= 2));
+ return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null;
+ };
+
+ /**
+ * Returns a clone of the Bitmap instance.
+ * @method clone
+ * @param {Boolean} node Whether the underlying dom element should be cloned as well.
+ * @return {Bitmap} a clone of the Bitmap instance.
+ **/
+ p.clone = function(node) {
+ var image = this.image;
+ if(image && node){ image = image.cloneNode(); }
+ var o = new Bitmap(image);
+ if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
+ this._cloneProps(o);
+ return o;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Bitmap (name="+ this.name +")]";
+ };
+
+
+ createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject");
+}());
+
+//##############################################################################
+// Sprite.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of
+ * images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100
+ * images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames,
+ * play frames as an animation, and even sequence animations together.
+ *
+ * See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations.
+ *
+ * Example
+ *
+ * var instance = new createjs.Sprite(spriteSheet);
+ * instance.gotoAndStop("frameName");
+ *
+ * Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called,
+ * only the first defined frame defined in the sprite sheet will be displayed.
+ *
+ * @class Sprite
+ * @extends DisplayObject
+ * @constructor
+ * @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame
+ * dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
+ * @param {String|Number} [frameOrAnimation] The frame number or animation to play initially.
+ **/
+ function Sprite(spriteSheet, frameOrAnimation) {
+ this.DisplayObject_constructor();
+
+
+ // public properties:
+ /**
+ * The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}}
+ * definitions, this will advance non-sequentially. This will always be an integer value.
+ * @property currentFrame
+ * @type {Number}
+ * @default 0
+ * @readonly
+ **/
+ this.currentFrame = 0;
+
+ /**
+ * Returns the name of the currently playing animation.
+ * @property currentAnimation
+ * @type {String}
+ * @final
+ * @readonly
+ **/
+ this.currentAnimation = null;
+
+ /**
+ * Prevents the animation from advancing each tick automatically. For example, you could create a sprite
+ * sheet of icons, set paused to true, and display the appropriate icon by setting currentFrame
.
+ * @property paused
+ * @type {Boolean}
+ * @default false
+ **/
+ this.paused = true;
+
+ /**
+ * The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame
+ * data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
+ * @property spriteSheet
+ * @type {SpriteSheet}
+ * @readonly
+ **/
+ this.spriteSheet = spriteSheet;
+
+ /**
+ * Specifies the current frame index within the currently playing animation. When playing normally, this will increase
+ * from 0 to n-1, where n is the number of frames in the current animation.
+ *
+ * This could be a non-integer value if
+ * using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is
+ * not an integer.
+ * @property currentAnimationFrame
+ * @type {Number}
+ * @default 0
+ **/
+ this.currentAnimationFrame = 0;
+
+ /**
+ * By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related
+ * SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
+ * framerate.
+ *
+ * For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will
+ * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
+ * vary slightly between frames.
+ *
+ * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
+ * passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
+ * @property framerate
+ * @type {Number}
+ * @default 0
+ **/
+ this.framerate = 0;
+
+
+ // private properties:
+ /**
+ * Current animation object.
+ * @property _animation
+ * @protected
+ * @type {Object}
+ * @default null
+ **/
+ this._animation = null;
+
+ /**
+ * Current frame index.
+ * @property _currentFrame
+ * @protected
+ * @type {Number}
+ * @default null
+ **/
+ this._currentFrame = null;
+
+ /**
+ * Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame
+ * @property _skipAdvance
+ * @protected
+ * @type {Boolean}
+ * @default false
+ **/
+ this._skipAdvance = false;
+
+ /**
+ * Docced in superclass.
+ */
+ this._webGLRenderStyle = createjs.DisplayObject._StageGL_SPRITE;
+
+ if (frameOrAnimation != null) { this.gotoAndPlay(frameOrAnimation); }
+ }
+ var p = createjs.extend(Sprite, createjs.DisplayObject);
+
+ /**
+ * Constructor alias for backwards compatibility. This method will be removed in future versions.
+ * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
+ * @method initialize
+ * @deprecated in favour of `createjs.promote()`
+ **/
+ p.initialize = Sprite; // TODO: Deprecated. This is for backwards support of Flash/Animate spritesheet export.
+
+
+// events:
+ /**
+ * Dispatched when an animation reaches its ends.
+ * @event animationend
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @param {String} name The name of the animation that just ended.
+ * @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping.
+ * @since 0.6.0
+ */
+
+ /**
+ * Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick,
+ * or calling gotoAndPlay() or gotoAndStop().
+ * @event change
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ */
+
+
+// public methods:
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ var hasContent = this.cacheCanvas || this.spriteSheet.complete;
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
+ };
+
+ /**
+ * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
+ this._normalizeFrame();
+ var o = this.spriteSheet.getFrame(this._currentFrame|0);
+ if (!o) { return false; }
+ var rect = o.rect;
+ if (rect.width && rect.height) { ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height); }
+ return true;
+ };
+
+ //Note, the doc sections below document using the specified APIs (from DisplayObject) from
+ //Bitmap. This is why they have no method implementations.
+
+ /**
+ * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
+ * You should not cache Sprite instances as it can degrade performance.
+ * @method cache
+ **/
+
+ /**
+ * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
+ * You should not cache Sprite instances as it can degrade performance.
+ * @method updateCache
+ **/
+
+ /**
+ * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
+ * You should not cache Sprite instances as it can degrade performance.
+ * @method uncache
+ **/
+
+ /**
+ * Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}}
+ * or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain
+ * unchanged.
+ * @method play
+ **/
+ p.play = function() {
+ this.paused = false;
+ };
+
+ /**
+ * Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}
+ * is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}}
+ * will resume playback.
+ * @method stop
+ **/
+ p.stop = function() {
+ this.paused = true;
+ };
+
+ /**
+ * Sets paused to false and plays the specified animation name, named frame, or frame number.
+ * @method gotoAndPlay
+ * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
+ * and begin playing.
+ **/
+ p.gotoAndPlay = function(frameOrAnimation) {
+ this.paused = false;
+ this._skipAdvance = true;
+ this._goto(frameOrAnimation);
+ };
+
+ /**
+ * Sets paused to true and seeks to the specified animation name, named frame, or frame number.
+ * @method gotoAndStop
+ * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
+ * and stop.
+ **/
+ p.gotoAndStop = function(frameOrAnimation) {
+ this.paused = true;
+ this._goto(frameOrAnimation);
+ };
+
+ /**
+ * Advances the playhead. This occurs automatically each tick by default.
+ * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite
+ * or its SpriteSheet.
+ * @method advance
+ */
+ p.advance = function(time) {
+ var fps = this.framerate || this.spriteSheet.framerate;
+ var t = (fps && time != null) ? time/(1000/fps) : 1;
+ this._normalizeFrame(t);
+ };
+
+ /**
+ * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to
+ * the origin. For example, a 90 x 70 frame with regX=50
and regY=40
would return a
+ * rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object.
+ *
+ * Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method.
+ * @method getBounds
+ * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully
+ * loaded.
+ **/
+ p.getBounds = function() {
+ // TODO: should this normalizeFrame?
+ return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle);
+ };
+
+ /**
+ * Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned
+ * instances.
+ * @method clone
+ * @return {Sprite} a clone of the Sprite instance.
+ **/
+ p.clone = function() {
+ return this._cloneProps(new Sprite(this.spriteSheet));
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Sprite (name="+ this.name +")]";
+ };
+
+// private methods:
+ /**
+ * @method _cloneProps
+ * @param {Sprite} o
+ * @return {Sprite} o
+ * @protected
+ **/
+ p._cloneProps = function(o) {
+ this.DisplayObject__cloneProps(o);
+ o.currentFrame = this.currentFrame;
+ o.currentAnimation = this.currentAnimation;
+ o.paused = this.paused;
+ o.currentAnimationFrame = this.currentAnimationFrame;
+ o.framerate = this.framerate;
+
+ o._animation = this._animation;
+ o._currentFrame = this._currentFrame;
+ o._skipAdvance = this._skipAdvance;
+ return o;
+ };
+
+ /**
+ * Advances the currentFrame
if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}}
+ * ticks.
+ * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
+ * @protected
+ * @method _tick
+ **/
+ p._tick = function(evtObj) {
+ if (!this.paused) {
+ if (!this._skipAdvance) { this.advance(evtObj&&evtObj.delta); }
+ this._skipAdvance = false;
+ }
+ this.DisplayObject__tick(evtObj);
+ };
+
+
+ /**
+ * Normalizes the current frame, advancing animations and dispatching callbacks as appropriate.
+ * @protected
+ * @method _normalizeFrame
+ **/
+ p._normalizeFrame = function(frameDelta) {
+ frameDelta = frameDelta || 0;
+ var animation = this._animation;
+ var paused = this.paused;
+ var frame = this._currentFrame;
+ var l;
+
+ if (animation) {
+ var speed = animation.speed || 1;
+ var animFrame = this.currentAnimationFrame;
+ l = animation.frames.length;
+ if (animFrame + frameDelta * speed >= l) {
+ var next = animation.next;
+ if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) {
+ // something changed in the event stack, so we shouldn't make any more changes here.
+ return;
+ } else if (next) {
+ // sequence. Automatically calls _normalizeFrame again with the remaining frames.
+ return this._goto(next, frameDelta - (l - animFrame) / speed);
+ } else {
+ // end.
+ this.paused = true;
+ animFrame = animation.frames.length - 1;
+ }
+ } else {
+ animFrame += frameDelta * speed;
+ }
+ this.currentAnimationFrame = animFrame;
+ this._currentFrame = animation.frames[animFrame | 0]
+ } else {
+ frame = (this._currentFrame += frameDelta);
+ l = this.spriteSheet.getNumFrames();
+ if (frame >= l && l > 0) {
+ if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) {
+ // looped.
+ if ((this._currentFrame -= l) >= l) { return this._normalizeFrame(); }
+ }
+ }
+ }
+ frame = this._currentFrame | 0;
+ if (this.currentFrame != frame) {
+ this.currentFrame = frame;
+ this.dispatchEvent("change");
+ }
+ };
+
+ /**
+ * Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}},
+ * {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.)
+ * @property _dispatchAnimationEnd
+ * @private
+ * @type {Function}
+ **/
+ p._dispatchAnimationEnd = function(animation, frame, paused, next, end) {
+ var name = animation ? animation.name : null;
+ if (this.hasEventListener("animationend")) {
+ var evt = new createjs.Event("animationend");
+ evt.name = name;
+ evt.next = next;
+ this.dispatchEvent(evt);
+ }
+ // did the animation get changed in the event stack?:
+ var changed = (this._animation != animation || this._currentFrame != frame);
+ // if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame:
+ if (!changed && !paused && this.paused) { this.currentAnimationFrame = end; changed = true; }
+ return changed;
+ };
+
+ /**
+ * Moves the playhead to the specified frame number or animation.
+ * @method _goto
+ * @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to.
+ * @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0.
+ * @protected
+ **/
+ p._goto = function(frameOrAnimation, frame) {
+ this.currentAnimationFrame = 0;
+ if (isNaN(frameOrAnimation)) {
+ var data = this.spriteSheet.getAnimation(frameOrAnimation);
+ if (data) {
+ this._animation = data;
+ this.currentAnimation = frameOrAnimation;
+ this._normalizeFrame(frame);
+ }
+ } else {
+ this.currentAnimation = this._animation = null;
+ this._currentFrame = frameOrAnimation;
+ this._normalizeFrame();
+ }
+ };
+
+
+ createjs.Sprite = createjs.promote(Sprite, "DisplayObject");
+}());
+
+//##############################################################################
+// Shape.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}}
+ * instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape
+ * instances to display the same vector graphics with different positions or transforms.
+ *
+ * If the vector art will not
+ * change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the
+ * rendering cost.
+ *
+ * Example
+ *
+ * var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100);
+ * var shape = new createjs.Shape(graphics);
+ *
+ * //Alternatively use can also use the graphics property of the Shape class to renderer the same as above.
+ * var shape = new createjs.Shape();
+ * shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
+ *
+ * @class Shape
+ * @extends DisplayObject
+ * @constructor
+ * @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created.
+ **/
+ function Shape(graphics) {
+ this.DisplayObject_constructor();
+
+
+ // public properties:
+ /**
+ * The graphics instance to display.
+ * @property graphics
+ * @type Graphics
+ **/
+ this.graphics = graphics ? graphics : new createjs.Graphics();
+ }
+ var p = createjs.extend(Shape, createjs.DisplayObject);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// public methods:
+ /**
+ * Returns true or false indicating whether the Shape would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ var hasContent = this.cacheCanvas || (this.graphics && !this.graphics.isEmpty());
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
+ };
+
+ /**
+ * Draws the Shape into the specified context ignoring its visible, alpha, shadow, and transform. Returns true if
+ * the draw was handled (useful for overriding functionality).
+ *
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
+ * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
+ * @return {Boolean}
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
+ this.graphics.draw(ctx, this);
+ return true;
+ };
+
+ /**
+ * Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to
+ * their defaults (for example .parent).
+ * @method clone
+ * @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be
+ * cloned. If false, the Graphics instance will be shared with the new Shape.
+ **/
+ p.clone = function(recursive) {
+ var g = (recursive && this.graphics) ? this.graphics.clone() : this.graphics;
+ return this._cloneProps(new Shape(g));
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Shape (name="+ this.name +")]";
+ };
+
+
+ createjs.Shape = createjs.promote(Shape, "DisplayObject");
+}());
+
+//##############################################################################
+// Text.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the
+ * lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML
+ * text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
+ * method, or using {{#crossLink "DOMElement"}}{{/crossLink}}.
+ *
+ * Please note that Text does not support HTML text, and can only display one font style at a time. To use
+ * multiple font styles, you will need to create multiple text instances, and position them manually.
+ *
+ * Example
+ *
+ * var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
+ * text.x = 100;
+ * text.textBaseline = "alphabetic";
+ *
+ * CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser
+ * before it can be displayed.
+ *
+ * Note: Text can be expensive to generate, so cache instances where possible. Be aware that not all
+ * browsers will render Text exactly the same.
+ * @class Text
+ * @extends DisplayObject
+ * @constructor
+ * @param {String} [text] The text to display.
+ * @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold
+ * 36px Arial").
+ * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
+ * "#F00", "red", or "#FF0000").
+ **/
+ function Text(text, font, color) {
+ this.DisplayObject_constructor();
+
+
+ // public properties:
+ /**
+ * The text to display.
+ * @property text
+ * @type String
+ **/
+ this.text = text;
+
+ /**
+ * The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial").
+ * @property font
+ * @type String
+ **/
+ this.font = font;
+
+ /**
+ * The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000".
+ * It will also accept valid canvas fillStyle values.
+ * @property color
+ * @type String
+ **/
+ this.color = color;
+
+ /**
+ * The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed
+ * information view the
+ *
+ * whatwg spec. Default is "left".
+ * @property textAlign
+ * @type String
+ **/
+ this.textAlign = "left";
+
+ /**
+ * The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or
+ * "bottom". For detailed information view the
+ * whatwg spec. Default is "top".
+ * @property textBaseline
+ * @type String
+ */
+ this.textBaseline = "top";
+
+ /**
+ * The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or
+ * shrunk to make it fit in this width. For detailed information view the
+ *
+ * whatwg spec.
+ * @property maxWidth
+ * @type Number
+ */
+ this.maxWidth = null;
+
+ /**
+ * If greater than 0, the text will be drawn as a stroke (outline) of the specified width.
+ * @property outline
+ * @type Number
+ **/
+ this.outline = 0;
+
+ /**
+ * Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0,
+ * the value of getMeasuredLineHeight is used.
+ * @property lineHeight
+ * @type Number
+ **/
+ this.lineHeight = 0;
+
+ /**
+ * Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null,
+ * the text will not be wrapped.
+ * @property lineWidth
+ * @type Number
+ **/
+ this.lineWidth = null;
+ }
+ var p = createjs.extend(Text, createjs.DisplayObject);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// static properties:
+ /**
+ * @property _workingContext
+ * @type CanvasRenderingContext2D
+ * @private
+ **/
+ var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
+ if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; }
+
+
+// constants:
+ /**
+ * Lookup table for the ratio to offset bounds x calculations based on the textAlign property.
+ * @property H_OFFSETS
+ * @type Object
+ * @protected
+ * @static
+ **/
+ Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1};
+
+ /**
+ * Lookup table for the ratio to offset bounds y calculations based on the textBaseline property.
+ * @property H_OFFSETS
+ * @type Object
+ * @protected
+ * @static
+ **/
+ Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1};
+
+
+// public methods:
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ var hasContent = this.cacheCanvas || (this.text != null && this.text !== "");
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
+ };
+
+ /**
+ * Draws the Text into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
+
+ var col = this.color || "#000";
+ if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; }
+ else { ctx.fillStyle = col; }
+
+ this._drawText(this._prepContext(ctx));
+ return true;
+ };
+
+ /**
+ * Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value.
+ * @method getMeasuredWidth
+ * @return {Number} The measured, untransformed width of the text.
+ **/
+ p.getMeasuredWidth = function() {
+ return this._getMeasuredWidth(this.text);
+ };
+
+ /**
+ * Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured
+ * width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts.
+ * @method getMeasuredLineHeight
+ * @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is
+ * based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts.
+ **/
+ p.getMeasuredLineHeight = function() {
+ return this._getMeasuredWidth("M")*1.2;
+ };
+
+ /**
+ * Returns the approximate height of multi-line text by multiplying the number of lines against either the
+ * lineHeight
(if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
+ * this operation requires the text flowing logic to run, which has an associated CPU cost.
+ * @method getMeasuredHeight
+ * @return {Number} The approximate height of the untransformed multi-line text.
+ **/
+ p.getMeasuredHeight = function() {
+ return this._drawText(null,{}).height;
+ };
+
+ /**
+ * Docced in superclass.
+ */
+ p.getBounds = function() {
+ var rect = this.DisplayObject_getBounds();
+ if (rect) { return rect; }
+ if (this.text == null || this.text === "") { return null; }
+ var o = this._drawText(null, {});
+ var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width;
+ var x = w * Text.H_OFFSETS[this.textAlign||"left"];
+ var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
+ var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
+ return this._rectangle.setValues(x, y, w, o.height);
+ };
+
+ /**
+ * Returns an object with width, height, and lines properties. The width and height are the visual width and height
+ * of the drawn text. The lines property contains an array of strings, one for
+ * each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing
+ * whitespace removed.
+ * @method getMetrics
+ * @return {Object} An object with width, height, and lines properties.
+ **/
+ p.getMetrics = function() {
+ var o = {lines:[]};
+ o.lineHeight = this.lineHeight || this.getMeasuredLineHeight();
+ o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
+ return this._drawText(null, o, o.lines);
+ };
+
+ /**
+ * Returns a clone of the Text instance.
+ * @method clone
+ * @return {Text} a clone of the Text instance.
+ **/
+ p.clone = function() {
+ return this._cloneProps(new Text(this.text, this.font, this.color));
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Text (text="+ (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
+ };
+
+
+// private methods:
+ /**
+ * @method _cloneProps
+ * @param {Text} o
+ * @protected
+ * @return {Text} o
+ **/
+ p._cloneProps = function(o) {
+ this.DisplayObject__cloneProps(o);
+ o.textAlign = this.textAlign;
+ o.textBaseline = this.textBaseline;
+ o.maxWidth = this.maxWidth;
+ o.outline = this.outline;
+ o.lineHeight = this.lineHeight;
+ o.lineWidth = this.lineWidth;
+ return o;
+ };
+
+ /**
+ * @method _getWorkingContext
+ * @param {CanvasRenderingContext2D} ctx
+ * @return {CanvasRenderingContext2D}
+ * @protected
+ **/
+ p._prepContext = function(ctx) {
+ ctx.font = this.font||"10px sans-serif";
+ ctx.textAlign = this.textAlign||"left";
+ ctx.textBaseline = this.textBaseline||"top";
+ ctx.lineJoin = "miter";
+ ctx.miterLimit = 2.5;
+ return ctx;
+ };
+
+ /**
+ * Draws multiline text.
+ * @method _drawText
+ * @param {CanvasRenderingContext2D} ctx
+ * @param {Object} o
+ * @param {Array} lines
+ * @return {Object}
+ * @protected
+ **/
+ p._drawText = function(ctx, o, lines) {
+ var paint = !!ctx;
+ if (!paint) {
+ ctx = Text._workingContext;
+ ctx.save();
+ this._prepContext(ctx);
+ }
+ var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
+
+ var maxW = 0, count = 0;
+ var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);
+ for (var i=0, l=hardLines.length; i this.lineWidth) {
+ // text wrapping:
+ var words = str.split(/(\s)/);
+ str = words[0];
+ w = ctx.measureText(str).width;
+
+ for (var j=1, jl=words.length; j this.lineWidth) {
+ if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
+ if (lines) { lines.push(str); }
+ if (w > maxW) { maxW = w; }
+ str = words[j+1];
+ w = ctx.measureText(str).width;
+ count++;
+ } else {
+ str += words[j] + words[j+1];
+ w += wordW;
+ }
+ }
+ }
+
+ if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
+ if (lines) { lines.push(str); }
+ if (o && w == null) { w = ctx.measureText(str).width; }
+ if (w > maxW) { maxW = w; }
+ count++;
+ }
+
+ if (o) {
+ o.width = maxW;
+ o.height = count*lineHeight;
+ }
+ if (!paint) { ctx.restore(); }
+ return o;
+ };
+
+ /**
+ * @method _drawTextLine
+ * @param {CanvasRenderingContext2D} ctx
+ * @param {String} text
+ * @param {Number} y
+ * @protected
+ **/
+ p._drawTextLine = function(ctx, text, y) {
+ // Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
+ if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); }
+ else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); }
+ };
+
+
+ /**
+ * @method _getMeasuredWidth
+ * @param {String} text
+ * @protected
+ **/
+ p._getMeasuredWidth = function(text) {
+ var ctx = Text._workingContext;
+ ctx.save();
+ var w = this._prepContext(ctx).measureText(text).width;
+ ctx.restore();
+ return w;
+ };
+
+
+ createjs.Text = createjs.promote(Text, "DisplayObject");
+}());
+
+//##############################################################################
+// BitmapText.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+
+// constructor:
+ /**
+ * Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported using new line characters,
+ * but automatic wrapping is not supported. See the {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}}
+ * property for more information on defining glyphs.
+ *
+ * Important: While BitmapText extends Container, it is not designed to be used as one.
+ * As such, methods like addChild and removeChild are disabled.
+ *
+ *
+ * @class BitmapText
+ * @extends DisplayObject
+ * @param {String} [text=""] The text to display.
+ * @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs.
+ * @constructor
+ **/
+ function BitmapText(text, spriteSheet) {
+ this.Container_constructor();
+
+
+ // public properties:
+ /**
+ * The text to display.
+ * @property text
+ * @type String
+ * @default ""
+ **/
+ this.text = text||"";
+
+ /**
+ * A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character
+ * should have a single frame animation defined in the sprite sheet named the same as
+ * corresponding character. For example, the following animation definition:
+ *
+ * "A": {frames: [0]}
+ *
+ * would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form
+ * is also acceptable:
+ *
+ * "A": 0
+ *
+ * Note that if a character in the text is not found in the sprite sheet, it will also
+ * try to use the alternate case (upper or lower).
+ *
+ * See SpriteSheet for more information on defining sprite sheet data.
+ * @property spriteSheet
+ * @type SpriteSheet
+ * @default null
+ **/
+ this.spriteSheet = spriteSheet;
+
+ /**
+ * The height of each line of text. If 0, then it will use a line height calculated
+ * by checking for the height of the "1", "T", or "L" character (in that order). If
+ * those characters are not defined, it will use the height of the first frame of the
+ * sprite sheet.
+ * @property lineHeight
+ * @type Number
+ * @default 0
+ **/
+ this.lineHeight = 0;
+
+ /**
+ * This spacing (in pixels) will be added after each character in the output.
+ * @property letterSpacing
+ * @type Number
+ * @default 0
+ **/
+ this.letterSpacing = 0;
+
+ /**
+ * If a space character is not defined in the sprite sheet, then empty pixels equal to
+ * spaceWidth will be inserted instead. If 0, then it will use a value calculated
+ * by checking for the width of the "1", "l", "E", or "A" character (in that order). If
+ * those characters are not defined, it will use the width of the first frame of the
+ * sprite sheet.
+ * @property spaceWidth
+ * @type Number
+ * @default 0
+ **/
+ this.spaceWidth = 0;
+
+
+ // private properties:
+ /**
+ * @property _oldProps
+ * @type Object
+ * @protected
+ **/
+ this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0};
+
+ /**
+ * Used to track the object which this class attached listeners to, helps optimize listener attachment.
+ * @property _oldStage
+ * @type Stage
+ * @protected
+ */
+ this._oldStage = null;
+ /**
+ * The event listener proxy triggered drawing draw for special circumstances.
+ * @property _drawAction
+ * @type function
+ * @protected
+ */
+ this._drawAction = null;
+ }
+ var p = createjs.extend(BitmapText, createjs.Container);
+
+// static properties:
+ /**
+ * BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains
+ * an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be
+ * retained, slightly increasing memory use, but reducing instantiation.
+ * @property maxPoolSize
+ * @type Number
+ * @static
+ * @default 100
+ **/
+ BitmapText.maxPoolSize = 100;
+
+ /**
+ * Sprite object pool.
+ * @type {Array}
+ * @static
+ * @private
+ */
+ BitmapText._spritePool = [];
+
+
+// public methods:
+ /**
+ * Docced in superclass.
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return; }
+ this._updateState();
+ this.Container_draw(ctx, ignoreCache);
+ };
+
+ /**
+ * Docced in superclass.
+ **/
+ p.getBounds = function() {
+ this._updateText();
+ return this.Container_getBounds();
+ };
+
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ var hasContent = this.cacheCanvas || (this.spriteSheet && this.spriteSheet.complete && this.text);
+ return !!(this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0 && hasContent);
+ };
+
+ p.clone = function() {
+ return this._cloneProps(new BitmapText(this.text, this.spriteSheet));
+ };
+
+ /**
+ * Disabled in BitmapText.
+ * @method addChild
+ **/
+ /**
+ * Disabled in BitmapText.
+ * @method addChildAt
+ **/
+ /**
+ * Disabled in BitmapText.
+ * @method removeChild
+ **/
+ /**
+ * Disabled in BitmapText.
+ * @method removeChildAt
+ **/
+ /**
+ * Disabled in BitmapText.
+ * @method removeAllChildren
+ **/
+ p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function() {};
+
+
+// private methods:
+ /**
+ * Docced in superclass.
+ **/
+ p._updateState = function() {
+ this._updateText();
+ };
+
+ /**
+ * @method _cloneProps
+ * @param {BitmapText} o
+ * @return {BitmapText} o
+ * @protected
+ **/
+ p._cloneProps = function(o) {
+ this.Container__cloneProps(o);
+ o.lineHeight = this.lineHeight;
+ o.letterSpacing = this.letterSpacing;
+ o.spaceWidth = this.spaceWidth;
+ return o;
+ };
+
+ /**
+ * @method _getFrameIndex
+ * @param {String} character
+ * @param {SpriteSheet} spriteSheet
+ * @return {Number}
+ * @protected
+ **/
+ p._getFrameIndex = function(character, spriteSheet) {
+ var c, o = spriteSheet.getAnimation(character);
+ if (!o) {
+ (character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null);
+ if (c) { o = spriteSheet.getAnimation(c); }
+ }
+ return o && o.frames[0];
+ };
+
+ /**
+ * @method _getFrame
+ * @param {String} character
+ * @param {SpriteSheet} spriteSheet
+ * @return {Object}
+ * @protected
+ **/
+ p._getFrame = function(character, spriteSheet) {
+ var index = this._getFrameIndex(character, spriteSheet);
+ return index == null ? index : spriteSheet.getFrame(index);
+ };
+
+ /**
+ * @method _getLineHeight
+ * @param {SpriteSheet} ss
+ * @return {Number}
+ * @protected
+ **/
+ p._getLineHeight = function(ss) {
+ var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0);
+ return frame ? frame.rect.height : 1;
+ };
+
+ /**
+ * @method _getSpaceWidth
+ * @param {SpriteSheet} ss
+ * @return {Number}
+ * @protected
+ **/
+ p._getSpaceWidth = function(ss) {
+ var frame = this._getFrame("1",ss) || this._getFrame("l",ss) || this._getFrame("e",ss) || this._getFrame("a",ss) || ss.getFrame(0);
+ return frame ? frame.rect.width : 1;
+ };
+
+ /**
+ * @method _updateText
+ * @protected
+ **/
+ p._updateText = function() {
+ var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet;
+ var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite;
+
+ for (var n in o) {
+ if (o[n] != this[n]) {
+ o[n] = this[n];
+ change = true;
+ }
+ }
+ if (!change) { return; }
+
+ var hasSpace = !!this._getFrame(" ", ss);
+ if (!hasSpace && !spaceW) { spaceW = this._getSpaceWidth(ss); }
+ if (!lineH) { lineH = this._getLineHeight(ss); }
+
+ for(var i=0, l=this.text.length; i childIndex) {
+ // faster than removeChild.
+ pool.push(sprite = kids.pop());
+ sprite.parent = null;
+ numKids--;
+ }
+ if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; }
+ };
+
+
+ createjs.BitmapText = createjs.promote(BitmapText, "Container");
+}());
+
+//##############################################################################
+// MovieClip.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * The MovieClip class associates a TweenJS Timeline with an EaselJS {{#crossLink "Container"}}{{/crossLink}}. It allows
+ * you to create objects which encapsulate timeline animations, state changes, and synched actions. The MovieClip
+ * class has been included in the EaselJS minified file since 0.7.0.
+ *
+ * Currently MovieClip only works properly if it is tick based (as opposed to time based) though some concessions have
+ * been made to support time-based timelines in the future.
+ *
+ * Example
+ * This example animates two shapes back and forth. The grey shape starts on the left, but we jump to a mid-point in
+ * the animation using {{#crossLink "MovieClip/gotoAndPlay"}}{{/crossLink}}.
+ *
+ * var stage = new createjs.Stage("canvas");
+ * createjs.Ticker.addEventListener("tick", stage);
+ *
+ * var mc = new createjs.MovieClip({loop:-1, labels:{myLabel:20}});
+ * stage.addChild(mc);
+ *
+ * var child1 = new createjs.Shape(
+ * new createjs.Graphics().beginFill("#999999")
+ * .drawCircle(30,30,30));
+ * var child2 = new createjs.Shape(
+ * new createjs.Graphics().beginFill("#5a9cfb")
+ * .drawCircle(30,30,30));
+ *
+ * mc.timeline.addTween(
+ * createjs.Tween.get(child1)
+ * .to({x:0}).to({x:60}, 50).to({x:0}, 50));
+ * mc.timeline.addTween(
+ * createjs.Tween.get(child2)
+ * .to({x:60}).to({x:0}, 50).to({x:60}, 50));
+ *
+ * mc.gotoAndPlay("start");
+ *
+ * It is recommended to use tween.to()
to animate and set properties (use no duration to have it set
+ * immediately), and the tween.wait()
method to create delays between animations. Note that using the
+ * tween.set()
method to affect properties will likely not provide the desired result.
+ *
+ * @class MovieClip
+ * @main MovieClip
+ * @param {Object} [props] The configuration properties to apply to this instance (ex. `{mode:MovieClip.SYNCHED}`).
+ * Supported props for the MovieClip are listed below. These props are set on the corresponding instance properties except where
+ * specified.
+ * - `mode`
+ * - `startPosition`
+ * - `frameBounds`
+ *
+ *
+ * This object will also be passed into the Timeline instance associated with this MovieClip. See the documentation
+ * for Timeline for a list of supported props (ex. `paused`, `labels`, `loop`, `reversed`, etc.)
+ * @extends Container
+ * @constructor
+ **/
+ function MovieClip(props) {
+ this.Container_constructor();
+ !MovieClip.inited&&MovieClip.init(); // static init
+
+ var mode, startPosition, loop, labels;
+
+ // handle old params (mode, startPosition, loop, labels):
+ // TODO: deprecated param handling:
+ if (props instanceof String || arguments.length > 1) {
+ mode = props;
+ startPosition = arguments[1];
+ loop = arguments[2];
+ labels = arguments[3];
+ if (loop == null) { loop = -1; }
+ props = null;
+ } else if (props) {
+ mode = props.mode;
+ startPosition = props.startPosition;
+ loop = props.loop;
+ labels = props.labels;
+ }
+ if (!props) { props = {labels:labels}; }
+
+
+ // public properties:
+ /**
+ * Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED).
+ * See each constant for a description of the behaviour.
+ * @property mode
+ * @type String
+ * @default null
+ **/
+ this.mode = mode||MovieClip.INDEPENDENT;
+
+ /**
+ * Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME.
+ * @property startPosition
+ * @type Number
+ * @default 0
+ */
+ this.startPosition = startPosition||0;
+
+ /**
+ * Specifies how many times this MovieClip should loop. A value of -1 indicates it should loop indefinitely. A value of
+ * 1 would cause it to loop once (ie. play a total of twice).
+ * @property loop
+ * @type Number
+ * @default -1
+ */
+ this.loop = loop === true ? -1 : (loop || 0);
+
+ /**
+ * The current frame of the movieclip.
+ * @property currentFrame
+ * @type Number
+ * @default 0
+ * @readonly
+ */
+ this.currentFrame = 0;
+
+ /**
+ * If true, the MovieClip's position will not advance when ticked.
+ * @property paused
+ * @type Boolean
+ * @default false
+ */
+ this.paused = props.paused||false;
+
+ /**
+ * If true, actions in this MovieClip's tweens will be run when the playhead advances.
+ * @property actionsEnabled
+ * @type Boolean
+ * @default true
+ */
+ this.actionsEnabled = true;
+
+ /**
+ * If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds
+ * it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT.
+ *
+ * For example, if you had a character animation with a "body" child MovieClip instance
+ * with different costumes on each frame, you could set body.autoReset = false, so that
+ * you can manually change the frame it is on, without worrying that it will be reset
+ * automatically.
+ * @property autoReset
+ * @type Boolean
+ * @default true
+ */
+ this.autoReset = true;
+
+ /**
+ * An array of bounds for each frame in the MovieClip. This is mainly intended for tool output.
+ * @property frameBounds
+ * @type Array
+ * @default null
+ */
+ this.frameBounds = this.frameBounds||props.frameBounds; // frameBounds are set on the prototype in Animate.
+
+ /**
+ * By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip
+ * will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
+ * framerate.
+ *
+ * For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will
+ * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
+ * vary slightly between frames.
+ *
+ * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
+ * passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
+ * @property framerate
+ * @type {Number}
+ * @default null
+ **/
+ this.framerate = null;
+
+ // set up the needed props for Timeline:
+ props.useTicks = props.paused = true;
+
+ /**
+ * The TweenJS Timeline that is associated with this MovieClip. This is created automatically when the MovieClip
+ * instance is initialized. Animations are created by adding TweenJS Tween
+ * instances to the timeline.
+ *
+ * Example
+ *
+ * var tween = createjs.Tween.get(target).to({x:0}).to({x:100}, 30);
+ * var mc = new createjs.MovieClip();
+ * mc.timeline.addTween(tween);
+ *
+ * Elements can be added and removed from the timeline by toggling an "_off" property
+ * using the tweenInstance.to()
method. Note that using Tween.set
is not recommended to
+ * create MovieClip animations. The following example will toggle the target off on frame 0, and then back on for
+ * frame 1. You can use the "visible" property to achieve the same effect.
+ *
+ * var tween = createjs.Tween.get(target).to({_off:false})
+ * .wait(1).to({_off:true})
+ * .wait(1).to({_off:false});
+ *
+ * @property timeline
+ * @type Timeline
+ * @default null
+ */
+ this.timeline = new createjs.Timeline(props);
+
+
+ // private properties:
+ /**
+ * @property _synchOffset
+ * @type Number
+ * @default 0
+ * @private
+ */
+ this._synchOffset = 0;
+
+ /**
+ * @property _rawPosition
+ * @type Number
+ * @default -1
+ * @private
+ */
+ this._rawPosition = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1.
+
+ /**
+ * @property _bound_resolveState
+ * @type Function
+ * @private
+ */
+ this._bound_resolveState = this._resolveState.bind(this);
+
+
+ /**
+ * The time remaining from the previous tick, only applicable when .framerate is set.
+ * @property _t
+ * @type Number
+ * @private
+ */
+ this._t = 0;
+
+ /**
+ * List of display objects that are actively being managed by the MovieClip.
+ * @property _managed
+ * @type Object
+ * @private
+ */
+ this._managed = {};
+ }
+ var p = createjs.extend(MovieClip, createjs.Container);
+
+
+// constants:
+ /**
+ * The MovieClip will advance independently of its parent, even if its parent is paused.
+ * This is the default mode.
+ * @property INDEPENDENT
+ * @static
+ * @type String
+ * @default "independent"
+ * @readonly
+ **/
+ MovieClip.INDEPENDENT = "independent";
+
+ /**
+ * The MovieClip will only display a single frame (as determined by the startPosition property).
+ * @property SINGLE_FRAME
+ * @static
+ * @type String
+ * @default "single"
+ * @readonly
+ **/
+ MovieClip.SINGLE_FRAME = "single";
+
+ /**
+ * The MovieClip will be advanced only when its parent advances and will be synched to the position of
+ * the parent MovieClip.
+ * @property SYNCHED
+ * @static
+ * @type String
+ * @default "synched"
+ * @readonly
+ **/
+ MovieClip.SYNCHED = "synched";
+
+
+// static properties:
+ MovieClip.inited = false;
+
+
+// static methods:
+ MovieClip.init = function() {
+ if (MovieClip.inited) { return; }
+ // plugins introduce some overhead to Tween, so we only install this if an MC is instantiated.
+ MovieClipPlugin.install();
+ MovieClip.inited = true;
+ };
+
+
+// getter / setters:
+ /**
+ * Use the {{#crossLink "MovieClip/labels:property"}}{{/crossLink}} property instead.
+ * @method _getLabels
+ * @protected
+ * @return {Array}
+ **/
+ p._getLabels = function() {
+ return this.timeline.getLabels();
+ };
+ // MovieClip.getLabels is @deprecated. Remove for 1.1+
+ p.getLabels = createjs.deprecate(p._getLabels, "MovieClip.getLabels");
+
+ /**
+ * Use the {{#crossLink "MovieClip/currentLabel:property"}}{{/crossLink}} property instead.
+ * @method _getCurrentLabel
+ * @protected
+ * @return {String}
+ **/
+ p._getCurrentLabel = function() {
+ return this.timeline.currentLabel;
+ };
+ // MovieClip.getCurrentLabel is @deprecated. Remove for 1.1+
+ p.getCurrentLabel = createjs.deprecate(p._getCurrentLabel, "MovieClip.getCurrentLabel");
+
+ /**
+ * Use the {{#crossLink "MovieClip/duration:property"}}{{/crossLink}} property instead.
+ * @method _getDuration
+ * @protected
+ * @return {Number}
+ **/
+ p._getDuration = function() {
+ return this.timeline.duration;
+ };
+ // MovieClip.getDuration is @deprecated. Remove for 1.1+
+ p.getDuration = createjs.deprecate(p._getDuration, "MovieClip.getDuration");
+
+ /**
+ * Returns an array of objects with label and position (aka frame) properties, sorted by position.
+ * @property labels
+ * @type {Array}
+ * @readonly
+ **/
+
+ /**
+ * Returns the name of the label on or immediately before the current frame.
+ * @property currentLabel
+ * @type {String}
+ * @readonly
+ **/
+
+ /**
+ * Returns the duration of this MovieClip in seconds or ticks.
+ * @property totalFrames
+ * @type {Number}
+ * @readonly
+ **/
+
+ /**
+ * Returns the duration of this MovieClip in seconds or ticks.
+ * @property duration
+ * @type {Number}
+ * @readonly
+ **/
+ try {
+ Object.defineProperties(p, {
+ labels: { get: p._getLabels },
+ currentLabel: { get: p._getCurrentLabel },
+ totalFrames: { get: p._getDuration },
+ duration: { get: p._getDuration }
+ // TODO: can we just proxy .currentFrame to tl.position as well? Ditto for .loop (or just remove entirely).
+ });
+ } catch (e) {}
+
+
+// public methods:
+ /**
+ * Constructor alias for backwards compatibility. This method will be removed in future versions.
+ * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
+ * @method initialize
+ * @deprecated in favour of `createjs.promote()`
+ **/
+ p.initialize = MovieClip; // TODO: Deprecated. This is for backwards support of Adobe Flash/Animate
+
+ /**
+ * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
+ * This does not account for whether it would be visible within the boundaries of the stage.
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method isVisible
+ * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
+ **/
+ p.isVisible = function() {
+ // children are placed in draw, so we can't determine if we have content.
+ return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
+ };
+
+ /**
+ * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
+ * Returns true if the draw was handled (useful for overriding functionality).
+ * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
+ * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
+ **/
+ p.draw = function(ctx, ignoreCache) {
+ // draw to cache first:
+ if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
+ this._updateState();
+ this.Container_draw(ctx, ignoreCache);
+ return true;
+ };
+
+ /**
+ * Sets paused to false.
+ * @method play
+ **/
+ p.play = function() {
+ this.paused = false;
+ };
+
+ /**
+ * Sets paused to true.
+ * @method stop
+ **/
+ p.stop = function() {
+ this.paused = true;
+ };
+
+ /**
+ * Advances this movie clip to the specified position or label and sets paused to false.
+ * @method gotoAndPlay
+ * @param {String|Number} positionOrLabel The animation name or frame number to go to.
+ **/
+ p.gotoAndPlay = function(positionOrLabel) {
+ this.paused = false;
+ this._goto(positionOrLabel);
+ };
+
+ /**
+ * Advances this movie clip to the specified position or label and sets paused to true.
+ * @method gotoAndStop
+ * @param {String|Number} positionOrLabel The animation or frame name to go to.
+ **/
+ p.gotoAndStop = function(positionOrLabel) {
+ this.paused = true;
+ this._goto(positionOrLabel);
+ };
+
+ /**
+ * Advances the playhead. This occurs automatically each tick by default.
+ * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set.
+ * @method advance
+ */
+ p.advance = function(time) {
+ var independent = MovieClip.INDEPENDENT;
+ if (this.mode !== independent) { return; } // update happens in draw for synched clips
+
+ // if this MC doesn't have a framerate, hunt ancestors for one:
+ var o=this, fps = o.framerate;
+ while ((o = o.parent) && fps === null) { if (o.mode === independent) { fps = o._framerate; } }
+ this._framerate = fps;
+
+ if (this.paused) { return; }
+
+ // calculate how many frames to advance:
+ var t = (fps !== null && fps !== -1 && time !== null) ? time/(1000/fps) + this._t : 1;
+ var frames = t|0;
+ this._t = t-frames; // leftover time, save to add to next advance.
+
+ while (frames--) { this._updateTimeline(this._rawPosition+1, false); }
+ };
+
+ /**
+ * MovieClip instances cannot be cloned.
+ * @method clone
+ **/
+ p.clone = function() {
+ // TODO: add support for this? Need to clone the Timeline & retarget tweens - pretty complex.
+ throw("MovieClip cannot be cloned.");
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[MovieClip (name="+ this.name +")]";
+ };
- /**
- * Indicates whether to include this object when running mouse interactions. Setting this to `false` for children
- * of a {{#crossLink "Container"}}{{/crossLink}} will cause events on the Container to not fire when that child is
- * clicked. Setting this property to `false` does not prevent the {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}
- * method from returning the child.
- *
- * Note: In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please
- * check out the latest NEXT version in GitHub for an updated version with this issue resolved. The fix will be
- * provided in the next release of EaselJS.
- * @property mouseEnabled
- * @type {Boolean}
- * @default true
- **/
- this.mouseEnabled = true;
- /**
- * If false, the tick will not run on this display object (or its children). This can provide some performance benefits.
- * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
- * on some display objects (ex. Sprite & MovieClip frame advancing, and DOMElement display properties).
- * @property tickEnabled
- * @type Boolean
- * @default true
- **/
- this.tickEnabled = true;
+// private methods:
+ /**
+ * Docced in superclass.
+ **/
+ p._updateState = function() {
+ if (this._rawPosition === -1 || this.mode !== MovieClip.INDEPENDENT) { this._updateTimeline(-1); }
+ };
- /**
- * An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for
- * debugging.
- * @property name
- * @type {String}
- * @default null
- **/
- this.name = null;
+ /**
+ * @method _tick
+ * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
+ * function.
+ * @protected
+ **/
+ p._tick = function(evtObj) {
+ this.advance(evtObj&&evtObj.delta);
+ this.Container__tick(evtObj);
+ };
+
+ /**
+ * @method _goto
+ * @param {String|Number} positionOrLabel The animation name or frame number to go to.
+ * @protected
+ **/
+ p._goto = function(positionOrLabel) {
+ var pos = this.timeline.resolve(positionOrLabel);
+ if (pos == null) { return; }
+ this._t = 0;
+ this._updateTimeline(pos, true);
+ };
+
+ /**
+ * @method _reset
+ * @private
+ **/
+ p._reset = function() {
+ this._rawPosition = -1;
+ this._t = this.currentFrame = 0;
+ this.paused = false;
+ };
+
+ /**
+ * @method _updateTimeline
+ * @param {Boolean} jump Indicates whether this update is due to jumping (via gotoAndXX) to a new position.
+ * @protected
+ **/
+ p._updateTimeline = function(rawPosition, jump) {
+ var synced = this.mode !== MovieClip.INDEPENDENT, tl = this.timeline;
+ if (synced) { rawPosition = this.startPosition + (this.mode===MovieClip.SINGLE_FRAME?0:this._synchOffset); }
+ if (rawPosition < 0) { rawPosition = 0; }
+ if (this._rawPosition === rawPosition && !synced) { return; }
+ this._rawPosition = rawPosition;
+
+ // update timeline position, ignoring actions if this is a graphic.
+ tl.loop = this.loop; // TODO: should we maintain this on MovieClip, or just have it on timeline?
+ tl.setPosition(rawPosition, synced || !this.actionsEnabled, jump, this._bound_resolveState);
+ };
+
+ /**
+ * Renders position 0 without running actions or updating _rawPosition.
+ * Primarily used by Animate CC to build out the first frame in the constructor of MC symbols.
+ * NOTE: not tested when run after the MC advances past the first frame.
+ * @method _renderFirstFrame
+ * @protected
+ **/
+ p._renderFirstFrame = function() {
+ var tl = this.timeline, pos = tl.rawPosition;
+ tl.setPosition(0, true, true, this._bound_resolveState);
+ tl.rawPosition = pos;
+ };
+
+ /**
+ * Runs via a callback after timeline property updates and before actions.
+ * @method _resolveState
+ * @protected
+ **/
+ p._resolveState = function() {
+ var tl = this.timeline;
+ this.currentFrame = tl.position;
+
+ for (var n in this._managed) { this._managed[n] = 1; }
- /**
- * A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that
- * contains this display object, or null if it has not been added
- * to one.
- * @property parent
- * @final
- * @type {Container}
- * @default null
- * @readonly
- **/
- this.parent = null;
+ var tweens = tl.tweens;
+ for (var i=0, l=tweens.length; i=0; i--) {
+ var id = kids[i].id;
+ if (this._managed[id] === 1) {
+ this.removeChildAt(i);
+ delete(this._managed[id]);
+ }
+ }
+ };
+
+ /**
+ * @method _setState
+ * @param {Array} state
+ * @param {Number} offset
+ * @protected
+ **/
+ p._setState = function(state, offset) {
+ if (!state) { return; }
+ for (var i=state.length-1;i>=0;i--) {
+ var o = state[i];
+ var target = o.t;
+ var props = o.p;
+ for (var n in props) { target[n] = props[n]; }
+ this._addManagedChild(target, offset);
+ }
+ };
+
+ /**
+ * Adds a child to the timeline, and sets it up as a managed child.
+ * @method _addManagedChild
+ * @param {MovieClip} child The child MovieClip to manage
+ * @param {Number} offset
+ * @private
+ **/
+ p._addManagedChild = function(child, offset) {
+ if (child._off) { return; }
+ this.addChildAt(child,0);
+
+ if (child instanceof MovieClip) {
+ child._synchOffset = offset;
+ // TODO: this does not precisely match Adobe Flash/Animate, which loses track of the clip if it is renamed or removed from the timeline, which causes it to reset.
+ // TODO: should also reset when MovieClip loops, though that will be a bit tricky to detect.
+ if (child.mode === MovieClip.INDEPENDENT && child.autoReset && (!this._managed[child.id])) { child._reset(); }
+ }
+ this._managed[child.id] = 2;
+ };
+
+ /**
+ * @method _getBounds
+ * @param {Matrix2D} matrix
+ * @param {Boolean} ignoreTransform
+ * @return {Rectangle}
+ * @protected
+ **/
+ p._getBounds = function(matrix, ignoreTransform) {
+ var bounds = this.DisplayObject_getBounds();
+ if (!bounds) {
+ if (this.frameBounds) { bounds = this._rectangle.copy(this.frameBounds[this.currentFrame]); }
+ }
+ if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
+ return this.Container__getBounds(matrix, ignoreTransform);
+ };
+
+
+ createjs.MovieClip = createjs.promote(MovieClip, "Container");
+
+
+
+// MovieClipPlugin for TweenJS:
+ /**
+ * This plugin works with TweenJS to prevent the startPosition
+ * property from tweening.
+ * @private
+ * @class MovieClipPlugin
+ * @constructor
+ **/
+ function MovieClipPlugin() {
+ throw("MovieClipPlugin cannot be instantiated.")
+ }
+
+ /**
+ * @property priority
+ * @type {Number}
+ * @static
+ * @readonly
+ **/
+ MovieClipPlugin.priority = 100; // very high priority, should run first
+
+ /**
+ * @property ID
+ * @type {String}
+ * @static
+ * @readonly
+ **/
+ MovieClipPlugin.ID = "MovieClip";
+
+ /**
+ * @method install
+ * @static
+ **/
+ MovieClipPlugin.install = function() {
+ createjs.Tween._installPlugin(MovieClipPlugin);
+ };
+
+ /**
+ * @method init
+ * @param {Tween} tween
+ * @param {String} prop
+ * @param {*} value
+ * @static
+ **/
+ MovieClipPlugin.init = function(tween, prop, value) {
+ if (prop === "startPosition" && tween.target instanceof MovieClip) { tween._addPlugin(MovieClipPlugin); }
+ };
+
+ /**
+ * @method step
+ * @param {Tween} tween
+ * @param {TweenStep} step
+ * @param {Object} props
+ * @static
+ **/
+ MovieClipPlugin.step = function(tween, step, props) {};
+
+ /**
+ * @method change
+ * @param {Tween} tween
+ * @param {TweenStep} step
+ * @param {*} value
+ * @param {Number} ratio
+ * @param {Object} end
+ * @return {*}
+ * @static
+ */
+ MovieClipPlugin.change = function(tween, step, prop, value, ratio, end) {
+ if (prop === "startPosition") { return (ratio === 1 ? step.props[prop] : step.prev.props[prop]); }
+ };
+
+}());
+
+//##############################################################################
+// SpriteSheetUtils.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
+ /**
+ * The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s.
+ * A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For
+ * example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across
+ * by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated.
+ * @class SpriteSheetUtils
+ * @static
+ **/
+ function SpriteSheetUtils() {
+ throw "SpriteSheetUtils cannot be instantiated";
+ }
+
+
+// private static properties:
+ /**
+ * @property _workingCanvas
+ * @static
+ * @type HTMLCanvasElement | Object
+ * @protected
+ */
+ /**
+ * @property _workingContext
+ * @static
+ * @type CanvasRenderingContext2D
+ * @protected
+ */
+ var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
+ if (canvas.getContext) {
+ SpriteSheetUtils._workingCanvas = canvas;
+ SpriteSheetUtils._workingContext = canvas.getContext("2d");
+ canvas.width = canvas.height = 1;
+ }
+
+
+// public static methods:
+ /**
+ * Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is
+ * to use a spritesheet frame as the source for a bitmap fill.
+ *
+ * WARNING: In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}}
+ * with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this
+ * method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}}
+ * property of {{#crossLink "Bitmap"}}{{/crossLink}}.
+ *
+ * The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas.
+ * @method extractFrame
+ * @static
+ * @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from.
+ * @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation
+ * name is specified, only the first frame of the animation will be extracted.
+ * @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image.
+ */
+ SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) {
+ if (isNaN(frameOrAnimation)) {
+ frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0];
+ }
+ var data = spriteSheet.getFrame(frameOrAnimation);
+ if (!data) { return null; }
+ var r = data.rect;
+ var canvas = SpriteSheetUtils._workingCanvas;
+ canvas.width = r.width;
+ canvas.height = r.height;
+ SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
+ var img = document.createElement("img");
+ img.src = canvas.toDataURL("image/png");
+ return img;
+ };
- /**
- * The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate
- * around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50.
- * Cached object's registration points should be set based on pre-cache conditions, not cached size.
- * @property regX
- * @type {Number}
- * @default 0
- **/
- this.regX = 0;
+ // SpriteSheetUtils.addFlippedFrames is @deprecated. Remove for 1.1+
+ SpriteSheetUtils.addFlippedFrames = createjs.deprecate(null, "SpriteSheetUtils.addFlippedFrames");
- /**
- * The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around
- * its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50.
- * Cached object's registration points should be set based on pre-cache conditions, not cached size.
- * @property regY
- * @type {Number}
- * @default 0
- **/
- this.regY = 0;
+ // SpriteSheetUtils.addFlippedFrames is @deprecated. Remove for 1.1+
+ SpriteSheetUtils.mergeAlpha = createjs.deprecate(null, "SpriteSheetUtils.mergeAlpha");
- /**
- * The rotation in degrees for this display object.
- * @property rotation
- * @type {Number}
- * @default 0
- **/
- this.rotation = 0;
+
+// private static methods:
+ SpriteSheetUtils._flip = function(spriteSheet, count, h, v) {
+ var imgs = spriteSheet._images;
+ var canvas = SpriteSheetUtils._workingCanvas;
+ var ctx = SpriteSheetUtils._workingContext;
+ var il = imgs.length/count;
+ for (var i=0;i
- * whatwg spec on compositing. For a list of supported compositeOperation value, visit
- * the W3C draft on Compositing and Blending.
- * @property compositeOperation
- * @type {String}
- * @default null
+ * The maximum width for the images (not individual frames) in the generated SpriteSheet. It is recommended to
+ * use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max
+ * dimensions, then additional images will be created as needed.
+ * @property maxWidth
+ * @type Number
+ * @default 2048
+ */
+ this.maxWidth = 2048;
+
+ /**
+ * The maximum height for the images (not individual frames) in the generated SpriteSheet. It is recommended to
+ * use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max
+ * dimensions, then additional images will be created as needed.
+ * @property maxHeight
+ * @type Number
+ * @default 2048
**/
- this.compositeOperation = null;
-
+ this.maxHeight = 2048;
+
/**
- * Indicates whether the display object should be drawn to a whole pixel when
- * {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole
- * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true).
- * @property snapToPixel
- * @type {Boolean}
- * @default true
+ * The SpriteSheet that was generated. This will be null before a build is completed successfully.
+ * @property spriteSheet
+ * @type SpriteSheet
**/
- this.snapToPixel = true;
-
+ this.spriteSheet = null;
+
/**
- * An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}}
- * or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is
- * cached.
- * @property filters
- * @type {Array}
- * @default null
+ * The scale to apply when drawing all frames to the SpriteSheet. This is multiplied against any scale specified
+ * in the addFrame call. This can be used, for example, to generate a SpriteSheet at run time that is tailored
+ * to the a specific device resolution (ex. tablet vs mobile).
+ * @property scale
+ * @type Number
+ * @default 1
**/
- this.filters = null;
-
+ this.scale = 1;
+
/**
- * A Shape instance that defines a vector mask (clipping path) for this display object. The shape's transformation
- * will be applied relative to the display object's parent coordinates (as if it were a child of the parent).
- * @property mask
- * @type {Shape}
- * @default null
- */
- this.mask = null;
-
+ * The padding to use between frames. This is helpful to preserve antialiasing on drawn vector content.
+ * @property padding
+ * @type Number
+ * @default 1
+ **/
+ this.padding = 1;
+
/**
- * A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}.
- * The hit area will have its transformation applied relative to this display object's coordinate space (as though
- * the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested
- * using only its own `alpha` value regardless of the alpha value on the target display object, or the target's
- * ancestors (parents).
- *
- * If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events.
- * This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false.
- *
- * Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}.
- * @property hitArea
- * @type {DisplayObject}
- * @default null
+ * A number from 0.01 to 0.99 that indicates what percentage of time the builder can use. This can be
+ * thought of as the number of seconds per second the builder will use. For example, with a timeSlice value of 0.3,
+ * the builder will run 20 times per second, using approximately 15ms per build (30% of available time, or 0.3s per second).
+ * Defaults to 0.3.
+ * @property timeSlice
+ * @type Number
+ * @default 0.3
+ **/
+ this.timeSlice = 0.3;
+
+ /**
+ * A value between 0 and 1 that indicates the progress of a build, or -1 if a build has not
+ * been initiated.
+ * @property progress
+ * @type Number
+ * @default -1
+ * @readonly
*/
- this.hitArea = null;
+ this.progress = -1;
/**
- * A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display
- * object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to
- * use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants.
- * @property cursor
- * @type {String}
- * @default null
+ * A {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} value that will be passed to new {{#crossLink "SpriteSheet"}}{{/crossLink}} instances that are
+ * created. If no framerate is specified (or it is 0), then SpriteSheets will use the {{#crossLink "Ticker"}}{{/crossLink}}
+ * framerate.
+ * @property framerate
+ * @type Number
+ * @default 0
*/
- this.cursor = null;
-
-
+ this.framerate = framerate || 0;
+
+
// private properties:
/**
- * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
- * @property _cacheScale
+ * @property _frames
* @protected
- * @type {Number}
- * @default 1
- * @deprecated
+ * @type Array
**/
-
+ this._frames = [];
+
/**
- * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
- * @property _cacheDataURLID
+ * @property _animations
* @protected
- * @type {Number}
- * @default 0
- * @deprecated
- */
-
+ * @type Array
+ **/
+ this._animations = {};
+
/**
- * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
- * @property _cacheDataURL
+ * @property _data
* @protected
- * @type {String}
- * @default null
- * @deprecated
- */
-
+ * @type Array
+ **/
+ this._data = null;
+
/**
- * @property _props
+ * @property _nextFrameIndex
* @protected
- * @type {DisplayObject}
- * @default null
+ * @type Number
**/
- this._props = new createjs.DisplayProps();
-
+ this._nextFrameIndex = 0;
+
/**
- * @property _rectangle
+ * @property _index
* @protected
- * @type {Rectangle}
- * @default null
+ * @type Number
**/
- this._rectangle = new createjs.Rectangle();
-
+ this._index = 0;
+
/**
- * @property _bounds
+ * @property _timerID
* @protected
- * @type {Rectangle}
- * @default null
+ * @type Number
**/
- this._bounds = null;
-
+ this._timerID = null;
+
/**
- * Where StageGL should look for required display properties, matters only for leaf display objects. Containers
- * or cached objects won't use this property, it's for native display of terminal elements.
- * @property _webGLRenderStyle
+ * @property _scale
* @protected
- * @type {number}
- * @default 0
- */
- this._webGLRenderStyle = DisplayObject._StageGL_NONE;
+ * @type Number
+ **/
+ this._scale = 1;
}
- var p = createjs.extend(DisplayObject, createjs.EventDispatcher);
-
-// static properties:
- /**
- * Listing of mouse event names. Used in _hasMouseEventListener.
- * @property _MOUSE_EVENTS
- * @protected
- * @static
- * @type {Array}
- **/
- DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"];
-
- /**
- * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}}
- * with cross domain content.
- * @property suppressCrossDomainErrors
- * @static
- * @type {Boolean}
- * @default false
- **/
- DisplayObject.suppressCrossDomainErrors = false;
+ var p = createjs.extend(SpriteSheetBuilder, createjs.EventDispatcher);
- /**
- * @property _snapToPixelEnabled
- * @protected
- * @static
- * @type {Boolean}
- * @default false
- **/
- DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access.
+// constants:
+ SpriteSheetBuilder.ERR_DIMENSIONS = "frame dimensions exceed max spritesheet dimensions";
+ SpriteSheetBuilder.ERR_RUNNING = "a build is already running";
+// events:
/**
- * Enum like property for determining StageGL render lookup, i.e. where to expect properties.
- * @property _StageGL_NONE
- * @protected
- * @static
- * @type {number}
+ * Dispatched when a build completes.
+ * @event complete
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.6.0
*/
- DisplayObject._StageGL_NONE = 0;
/**
- * Enum like property for determining StageGL render lookup, i.e. where to expect properties.
- * @property _StageGL_SPRITE
- * @protected
- * @static
- * @type {number}
+ * Dispatched when an asynchronous build has progress.
+ * @event progress
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @param {Number} progress The current progress value (0-1).
+ * @since 0.6.0
*/
- DisplayObject._StageGL_SPRITE = 1;
- /**
- * Enum like property for determining StageGL render lookup, i.e. where to expect properties.
- * @property _StageGL_BITMAP
- * @protected
- * @static
- * @type {number}
- */
- DisplayObject._StageGL_BITMAP = 2;
+// public methods:
/**
- * @property _hitTestCanvas
- * @type {HTMLCanvasElement | Object}
- * @static
- * @protected
+ * Adds a frame to the {{#crossLink "SpriteSheet"}}{{/crossLink}}. Note that the frame will not be drawn until you
+ * call {{#crossLink "SpriteSheetBuilder/build"}}{{/crossLink}} method. The optional setup params allow you to have
+ * a function run immediately before the draw occurs. For example, this allows you to add a single source multiple
+ * times, but manipulate it or its children to change it to generate different frames.
+ *
+ * Note that the source's transformations (x, y, scale, rotate, alpha) will be ignored, except for regX/Y. To apply
+ * transforms to a source object and have them captured in the SpriteSheet, simply place it into a {{#crossLink "Container"}}{{/crossLink}}
+ * and pass in the Container as the source.
+ * @method addFrame
+ * @param {DisplayObject} source The source {{#crossLink "DisplayObject"}}{{/crossLink}} to draw as the frame.
+ * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the
+ * source to draw to the frame. If not specified, it will look for a `getBounds` method, bounds property, or
+ * `nominalBounds` property on the source to use. If one is not found, the frame will be skipped.
+ * @param {Number} [scale=1] Optional. The scale to draw this frame at. Default is 1.
+ * @param {Function} [setupFunction] A function to call immediately before drawing this frame. It will be called with two parameters: the source, and setupData.
+ * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
+ * @return {Number} The index of the frame that was just added, or null if a sourceRect could not be determined.
**/
+ p.addFrame = function(source, sourceRect, scale, setupFunction, setupData) {
+ if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
+ var rect = sourceRect||source.bounds||source.nominalBounds;
+ if (!rect&&source.getBounds) { rect = source.getBounds(); }
+ if (!rect) { return null; }
+ scale = scale||1;
+ return this._frames.push({source:source, sourceRect:rect, scale:scale, funct:setupFunction, data:setupData, index:this._frames.length, height:rect.height*scale})-1;
+ };
+
/**
- * @property _hitTestContext
- * @type {CanvasRenderingContext2D}
- * @static
- * @protected
+ * Adds an animation that will be included in the created {{#crossLink "SpriteSheet"}}{{/crossLink}}.
+ * @method addAnimation
+ * @param {String} name The name for the animation.
+ * @param {Array} frames An array of frame indexes that comprise the animation. Ex. [3,6,5] would describe an animation
+ * that played frame indexes 3, 6, and 5 in that order.
+ * @param {String} [next] Specifies the name of the animation to continue to after this animation ends. You can
+ * also pass false to have the animation stop when it ends. By default it will loop to the start of the same animation.
+ * @param {Number} [speed] Specifies a frame advance speed for this animation. For example, a value of 0.5 would
+ * cause the animation to advance every second tick. Note that earlier versions used `frequency` instead, which had
+ * the opposite effect.
**/
- var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas.
- if (canvas.getContext) {
- DisplayObject._hitTestCanvas = canvas;
- DisplayObject._hitTestContext = canvas.getContext("2d");
- canvas.width = canvas.height = 1;
- }
+ p.addAnimation = function(name, frames, next, speed) {
+ if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
+ this._animations[name] = {frames:frames, next:next, speed:speed};
+ };
-// events:
/**
- * Dispatched when the user presses their left mouse button over the display object. See the
- * {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
- * @event mousedown
- * @since 0.6.0
- */
+ * This will take a {{#crossLink "MovieClip"}}{{/crossLink}} instance, and add its frames and labels to this
+ * builder. Labels will be added as an animation running from the label index to the next label. For example, if
+ * there is a label named "foo" at frame 0 and a label named "bar" at frame 10, in a MovieClip with 15 frames, it
+ * will add an animation named "foo" that runs from frame index 0 to 9, and an animation named "bar" that runs from
+ * frame index 10 to 14.
+ *
+ * Note that this will iterate through the full MovieClip with {{#crossLink "MovieClip/actionsEnabled:property"}}{{/crossLink}}
+ * set to `false`, ending on the last frame.
+ * @method addMovieClip
+ * @param {MovieClip} source The source MovieClip instance to add to the SpriteSheet.
+ * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the source to
+ * draw to the frame. If not specified, it will look for a {{#crossLink "DisplayObject/getBounds"}}{{/crossLink}}
+ * method, `frameBounds` Array, `bounds` property, or `nominalBounds` property on the source to use. If one is not
+ * found, the MovieClip will be skipped.
+ * @param {Number} [scale=1] The scale to draw the movie clip at.
+ * @param {Function} [setupFunction] A function to call immediately before drawing each frame. It will be called
+ * with three parameters: the source, setupData, and the frame index.
+ * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
+ * @param {Function} [labelFunction] This method will be called for each MovieClip label that is added with four
+ * parameters: the label name, the source MovieClip instance, the starting frame index (in the movieclip timeline)
+ * and the end index. It must return a new name for the label/animation, or `false` to exclude the label.
+ **/
+ p.addMovieClip = function(source, sourceRect, scale, setupFunction, setupData, labelFunction) {
+ if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
+ var rects = source.frameBounds;
+ var rect = sourceRect||source.bounds||source.nominalBounds;
+ if (!rect&&source.getBounds) { rect = source.getBounds(); }
+ if (!rect && !rects) { return; }
- /**
- * Dispatched when the user presses their left mouse button and then releases it while over the display object.
- * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
- * @event click
- * @since 0.6.0
- */
+ var i, l, baseFrameIndex = this._frames.length;
+ var duration = source.timeline.duration;
+ for (i=0; i
- * - when the mouse enters shapeA (target=shapeA)
- * - when the mouse enters shapeB (target=shapeB)
- *
- * However, with a listener for "rollover" instead, only a single event is received when the mouse first enters
- * the aggregate myContainer content (target=myContainer).
- *
- * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
- * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
- * @event rollover
- * @since 0.7.0
- */
+ * SpriteSheetBuilder instances cannot be cloned.
+ * @method clone
+ **/
+ p.clone = function() {
+ throw("SpriteSheetBuilder cannot be cloned.");
+ };
/**
- * This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following
- * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
- * aggregate of their content.
- *
- * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
- * shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}}
- * on myContainer, two events would be received, each targeting a child element:
- * - when the mouse leaves shapeA (target=shapeA)
- * - when the mouse leaves shapeB (target=shapeB)
- *
- * However, with a listener for "rollout" instead, only a single event is received when the mouse leaves
- * the aggregate myContainer content (target=myContainer).
- *
- * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
- * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
- * @event rollout
- * @since 0.7.0
- */
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[SpriteSheetBuilder]";
+ };
- /**
- * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove
- * event will be generated on that object whenever the mouse moves until the mouse press is released. This can be
- * useful for dragging and similar operations.
- *
- * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed
- * (e.g. during a `pressmove` event), a `pressmove` event is still generated. However since it is no longer in the
- * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive
- * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressup:event"}}{{/crossLink}}`
- * or `pressmove` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}),
- * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`.
- * @event pressmove
- * @since 0.7.0
- */
+// private methods:
/**
- * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event
- * will be generated on that object when that mouse press is released. This can be useful for dragging and similar
- * operations.
- *
- * **Please note** that if the initial mouse target from a `mousedown` event is removed from the stage after being pressed
- * (e.g. during a `pressmove` event), a `pressup` event is still generated. However since it is no longer in the
- * display list, the event can not bubble. This means that previous ancestors (parent containers) will not receive
- * the event, and therefore can not re-dispatch it. If you intend to listen for `{{#crossLink "DisplayObject/pressmove:event"}}{{/crossLink}}`
- * or `pressup` on a dynamic object (such as a {{#crossLink "MovieClip"}}{{/crossLink}} or {{#crossLink "Container"}}{{/crossLink}}),
- * then ensure you set {{#crossLink "Container/mouseChildren:property"}}{{/crossLink}} to `false`.
- * @event pressup
- * @since 0.7.0
- */
+ * @method _startBuild
+ * @protected
+ **/
+ p._startBuild = function() {
+ var pad = this.padding||0;
+ this.progress = 0;
+ this.spriteSheet = null;
+ this._index = 0;
+ this._scale = this.scale;
+ var dataFrames = [];
+ this._data = {
+ images: [],
+ frames: dataFrames,
+ framerate: this.framerate,
+ animations: this._animations // TODO: should we "clone" _animations in case someone adds more animations after a build?
+ };
+ var frames = this._frames.slice();
+ frames.sort(function(a,b) { return (a.height<=b.height) ? -1 : 1; });
+
+ if (frames[frames.length-1].height+pad*2 > this.maxHeight) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
+ var y=0, x=0;
+ var img = 0;
+ while (frames.length) {
+ var o = this._fillRow(frames, y, img, dataFrames, pad);
+ if (o.w > x) { x = o.w; }
+ y += o.h;
+ if (!o.h || !frames.length) {
+ var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
+ canvas.width = this._getSize(x,this.maxWidth);
+ canvas.height = this._getSize(y,this.maxHeight);
+ this._data.images[img] = canvas;
+ if (!o.h) {
+ x=y=0;
+ img++;
+ }
+ }
+ }
+ };
+
/**
- * Dispatched when the display object is added to a parent container.
- * @event added
- */
+ * @method _setupMovieClipFrame
+ * @protected
+ * @return {Number} The width & height of the row.
+ **/
+ p._setupMovieClipFrame = function(source, data) {
+ var ae = source.actionsEnabled;
+ source.actionsEnabled = false;
+ source.gotoAndStop(data.i);
+ source.actionsEnabled = ae;
+ data.f&&data.f(source, data.d, data.i);
+ };
/**
- * Dispatched when the display object is removed from its parent container.
- * @event removed
- */
+ * @method _getSize
+ * @protected
+ * @return {Number} The width & height of the row.
+ **/
+ p._getSize = function(size,max) {
+ var pow = 4;
+ while (Math.pow(2,++pow) < size){}
+ return Math.min(max,Math.pow(2,pow));
+ };
/**
- * Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the
- * rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on
- * the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their
- * {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being
- * dispatched on their parent.
- * @event tick
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For
- * example if you called stage.update("hello"), then the params would be ["hello"].
- * @since 0.6.0
- */
-
+ * @method _fillRow
+ * @param {Array} frames
+ * @param {Number} y
+ * @param {HTMLImageElement} img
+ * @param {Object} dataFrames
+ * @param {Number} pad
+ * @protected
+ * @return {Number} The width & height of the row.
+ **/
+ p._fillRow = function(frames, y, img, dataFrames, pad) {
+ var w = this.maxWidth;
+ var maxH = this.maxHeight;
+ y += pad;
+ var h = maxH-y;
+ var x = pad;
+ var height = 0;
+ for (var i=frames.length-1; i>=0; i--) {
+ var frame = frames[i];
+ var sc = this._scale*frame.scale;
+ var rect = frame.sourceRect;
+ var source = frame.source;
+ var rx = Math.floor(sc*rect.x-pad);
+ var ry = Math.floor(sc*rect.y-pad);
+ var rh = Math.ceil(sc*rect.height+pad*2);
+ var rw = Math.ceil(sc*rect.width+pad*2);
+ if (rw > w) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
+ if (rh > h || x+rw > w) { continue; }
+ frame.img = img;
+ frame.rect = new createjs.Rectangle(x,y,rw,rh);
+ height = height || rh;
+ frames.splice(i,1);
+ dataFrames[frame.index] = [x,y,rw,rh,img,Math.round(-rx+sc*source.regX-pad),Math.round(-ry+sc*source.regY-pad)];
+ x += rw;
+ }
+ return {w:x, h:height};
+ };
-// getter / setters:
/**
- * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead.
- * @method _getStage
+ * @method _endBuild
* @protected
- * @return {Stage}
**/
- p._getStage = function() {
- // uses dynamic access to avoid circular dependencies;
- var o = this, _Stage = createjs["Stage"];
- while (o.parent) { o = o.parent; }
- if (o instanceof _Stage) { return o; }
- return null;
+ p._endBuild = function() {
+ this.spriteSheet = new createjs.SpriteSheet(this._data);
+ this._data = null;
+ this.progress = 1;
+ this.dispatchEvent("complete");
};
- // DisplayObject.getStage is @deprecated. Remove for 1.1+
- p.getStage = createjs.deprecate(p._getStage, "DisplayObject.getStage");
/**
- * Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one.
- * @property stage
- * @type {Stage}
- * @readonly
+ * @method _run
+ * @protected
**/
+ p._run = function() {
+ var ts = Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50;
+ var t = (new Date()).getTime()+ts;
+ var complete = false;
+ while (t > (new Date()).getTime()) {
+ if (!this._drawNext()) { complete = true; break; }
+ }
+ if (complete) {
+ this._endBuild();
+ } else {
+ var _this = this;
+ this._timerID = setTimeout(function() { _this._run(); }, 50-ts);
+ }
+ var p = this.progress = this._index/this._frames.length;
+ if (this.hasEventListener("progress")) {
+ var evt = new createjs.Event("progress");
+ evt.progress = p;
+ this.dispatchEvent(evt);
+ }
+ };
/**
- * Returns an ID number that uniquely identifies the current cache for this display object. This can be used to
- * determine if the cache has changed since a previous check.
- * Moved to {{#crossLink "BitmapCache"}}{{/crossLink}}
- * @property cacheID
- * @deprecated
- * @type {Number}
- * @default 0
- */
+ * @method _drawNext
+ * @protected
+ * @return Boolean Returns false if this is the last draw.
+ **/
+ p._drawNext = function() {
+ var frame = this._frames[this._index];
+ var sc = frame.scale*this._scale;
+ var rect = frame.rect;
+ var sourceRect = frame.sourceRect;
+ var canvas = this._data.images[frame.img];
+ var ctx = canvas.getContext("2d");
+ frame.funct&&frame.funct(frame.source, frame.data);
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
+ ctx.clip();
+ ctx.translate(Math.ceil(rect.x-sourceRect.x*sc), Math.ceil(rect.y-sourceRect.y*sc));
+ ctx.scale(sc,sc);
+ frame.source.draw(ctx); // display object will draw itself.
+ ctx.restore();
+ return (++this._index) < this._frames.length;
+ };
+
+
+ createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher");
+}());
+
+//##############################################################################
+// DOMElement.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+(function() {
+ "use strict";
+
+
+// constructor:
/**
- * Set both the {{#crossLink "DisplayObject/scaleX:property"}}{{/crossLink}} and the {{#crossLink "DisplayObject/scaleY"}}{{/crossLink}}
- * property to the same value. Note that when you get the value, if the `scaleX` and `scaleY` are different values,
- * it will return only the `scaleX`.
- * @property scaleX
- * @type {Number}
- * @default 1
+ * This class is still experimental, and more advanced use is likely to be buggy. Please report bugs.
+ *
+ * A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed
+ * within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is
+ * not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be
+ * drawn in front of or behind the canvas).
+ *
+ * The position of a DOMElement is relative to their parent node in the DOM. It is recommended that
+ * the DOM Object be added to a div that also contains the canvas so that they share the same position
+ * on the page.
+ *
+ * DOMElement is useful for positioning HTML elements over top of canvas content, and for elements
+ * that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML
+ * content.
+ *
+ * Mouse Interaction
+ *
+ * DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse
+ * events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to
+ * the htmlElement (note, this does not support EventDispatcher)
+ *
+ * var domElement = new createjs.DOMElement(htmlElement);
+ * domElement.htmlElement.onclick = function() {
+ * console.log("clicked");
+ * }
+ *
+ * Important: This class needs to be notified it is about to be drawn, this will happen automatically
+ * if you call stage.update, calling stage.draw or disabling tickEnabled will miss important steps and it will render
+ * stale information.
+ *
+ * @class DOMElement
+ * @extends DisplayObject
+ * @constructor
+ * @param {HTMLElement} htmlElement A reference or id for the DOM element to manage.
*/
- try {
- Object.defineProperties(p, {
- stage: { get: p._getStage },
- cacheID: {
- get: function(){ return this.bitmapCache && this.bitmapCache.cacheID },
- set: function(a){ this.bitmapCache && (this.bitmapCache.cacheID = a) }
- },
- scale: {
- get: function() { return this.scaleX; },
- set: function(scale) { this.scaleX = this.scaleY = scale; },
- }
- });
- } catch (e) {}
+ function DOMElement(htmlElement) {
+ this.DisplayObject_constructor();
+
+ if (typeof(htmlElement)=="string") { htmlElement = document.getElementById(htmlElement); }
+ this.mouseEnabled = false;
+
+ var style = htmlElement.style;
+ style.position = "absolute";
+ style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%";
+
+
+ // public properties:
+ /**
+ * The DOM object to manage.
+ * @property htmlElement
+ * @type HTMLElement
+ */
+ this.htmlElement = htmlElement;
+
+
+ // private properties:
+ /**
+ * @property _oldMtx
+ * @type Matrix2D
+ * @protected
+ */
+ this._oldProps = null;
+
+ /**
+ * Used to track the object which this class attached listeners to, helps optimize listener attachment.
+ * @property _oldStage
+ * @type Stage
+ * @protected
+ */
+ this._oldStage = null;
+ /**
+ * The event listener proxy triggered drawing draw for special circumstances.
+ * @property _drawAction
+ * @type function
+ * @protected
+ */
+ this._drawAction = null;
+ }
+ var p = createjs.extend(DOMElement, createjs.DisplayObject);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
// public methods:
/**
* Returns true or false indicating whether the display object would be visible if drawn to a canvas.
* This does not account for whether it would be visible within the boundaries of the stage.
- *
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
* @method isVisible
* @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- **/
+ */
p.isVisible = function() {
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
+ return this.htmlElement != null;
};
/**
* Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true
if the draw was handled (useful for overriding functionality).
- *
+ * Returns true if the draw was handled (useful for overriding functionality).
* NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
* @method draw
* @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
- * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
+ * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
+ * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
+ * into itself).
* @return {Boolean}
- **/
+ */
p.draw = function(ctx, ignoreCache) {
- var cache = this.bitmapCache;
- if(cache && !ignoreCache) {
- return cache.draw(ctx);
- }
- return false;
+ // this relies on the _tick method because draw isn't called if the parent is not visible.
+ // the actual update happens in _handleDrawEnd
+ return true;
};
/**
- * Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow
- * to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}.
- * @method updateContext
- * @param {CanvasRenderingContext2D} ctx The canvas 2D to update.
- **/
- p.updateContext = function(ctx) {
- var o=this, mask=o.mask, mtx= o._props.matrix;
-
- if (mask && mask.graphics && !mask.graphics.isEmpty()) {
- mask.getMatrix(mtx);
- ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
-
- mask.graphics.drawAsPath(ctx);
- ctx.clip();
-
- mtx.invert();
- ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
- }
-
- this.getMatrix(mtx);
- var tx = mtx.tx, ty = mtx.ty;
- if (DisplayObject._snapToPixelEnabled && o.snapToPixel) {
- tx = tx + (tx < 0 ? -0.5 : 0.5) | 0;
- ty = ty + (ty < 0 ? -0.5 : 0.5) | 0;
- }
- ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty);
- ctx.globalAlpha *= o.alpha;
- if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; }
- if (o.shadow) { this._applyShadow(ctx, o.shadow); }
+ * Not applicable to DOMElement.
+ * @method cache
+ */
+ p.cache = function() {};
+
+ /**
+ * Not applicable to DOMElement.
+ * @method uncache
+ */
+ p.uncache = function() {};
+
+ /**
+ * Not applicable to DOMElement.
+ * @method updateCache
+ */
+ p.updateCache = function() {};
+
+ /**
+ * Not applicable to DOMElement.
+ * @method hitTest
+ */
+ p.hitTest = function() {};
+
+ /**
+ * Not applicable to DOMElement.
+ * @method localToGlobal
+ */
+ p.localToGlobal = function() {};
+
+ /**
+ * Not applicable to DOMElement.
+ * @method globalToLocal
+ */
+ p.globalToLocal = function() {};
+
+ /**
+ * Not applicable to DOMElement.
+ * @method localToLocal
+ */
+ p.localToLocal = function() {};
+
+ /**
+ * DOMElement cannot be cloned. Throws an error.
+ * @method clone
+ */
+ p.clone = function() {
+ throw("DOMElement cannot be cloned.")
};
/**
- * Draws the display object into a new element, which is then used for subsequent draws. Intended for complex content
- * that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape),
- * this can provide for much faster rendering because the content does not need to be re-rendered each tick. The
- * cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must manually
- * update the cache by calling updateCache()
again. You must specify the cached area via the x, y, w,
- * and h parameters. This defines the rectangle that will be rendered and cached using this display object's coordinates.
- *
- * Example
- * For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25:
- *
- * var shape = new createjs.Shape();
- * shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25);
- * shape.cache(-25, -25, 50, 50);
- *
- * Note that filters need to be defined before the cache is applied or you will have to call updateCache after
- * application. Check out the {{#crossLink "Filter"}}{{/crossLink}} class for more information. Some filters
- * (ex. BlurFilter) may not work as expected in conjunction with the scale param.
- *
- * Usually, the resulting cacheCanvas will have the dimensions width * scale, height * scale, however some filters (ex. BlurFilter)
- * will add padding to the canvas dimensions.
- *
- * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}.
- * This allows for easier interaction and alternate cache methods like WebGL with {{#crossLink "StageGL"}}{{/crossLink}}.
- * For more information on the options object, see the BitmapCache {{#crossLink "BitmapCache/define"}}{{/crossLink}}.
- *
- * @method cache
- * @param {Number} x The x coordinate origin for the cache region.
- * @param {Number} y The y coordinate origin for the cache region.
- * @param {Number} width The width of the cache region.
- * @param {Number} height The height of the cache region.
- * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using
- * myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate
- * cached elements with greater fidelity. Default is 1.
- * @param {Object} [options=undefined] Specify additional parameters for the cache logic
- **/
- p.cache = function(x, y, width, height, scale, options) {
- if(!this.bitmapCache){
- this.bitmapCache = new createjs.BitmapCache();
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ */
+ p.toString = function() {
+ return "[DOMElement (name="+ this.name +")]";
+ };
+
+ /**
+ * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
+ * are not full EaselJS display objects and do not participate in EaselJS mouse events.
+ * @event click
+ */
+
+ /**
+ * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
+ * are not full EaselJS display objects and do not participate in EaselJS mouse events.
+ * @event dblClick
+ */
+
+ /**
+ * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
+ * are not full EaselJS display objects and do not participate in EaselJS mouse events.
+ * @event mousedown
+ */
+
+ /**
+ * The HTMLElement can listen for the mouseover event, not the DOMElement instance.
+ * Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events.
+ * @event mouseover
+ */
+
+ /**
+ * Not applicable to DOMElement.
+ * @event tick
+ */
+
+
+// private methods:
+ /**
+ * @method _tick
+ * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
+ * function.
+ * @protected
+ */
+ p._tick = function(evtObj) {
+ var stage = this.stage;
+ if(stage && stage !== this._oldStage) {
+ this._drawAction && stage.off("drawend", this._drawAction);
+ this._drawAction = stage.on("drawend", this._handleDrawEnd, this);
+ this._oldStage = stage;
+ }
+ this.DisplayObject__tick(evtObj);
+ };
+
+ /**
+ * @method _handleDrawEnd
+ * @param {Event} evt
+ * @protected
+ */
+ p._handleDrawEnd = function(evt) {
+ var o = this.htmlElement;
+ if (!o) { return; }
+ var style = o.style;
+
+ var props = this.getConcatenatedDisplayProps(this._props), mtx = props.matrix;
+
+ var visibility = props.visible ? "visible" : "hidden";
+ if (visibility != style.visibility) { style.visibility = visibility; }
+ if (!props.visible) { return; }
+
+ var oldProps = this._oldProps, oldMtx = oldProps&&oldProps.matrix;
+ var n = 10000; // precision
+
+ if (!oldMtx || !oldMtx.equals(mtx)) {
+ var str = "matrix(" + (mtx.a*n|0)/n +","+ (mtx.b*n|0)/n +","+ (mtx.c*n|0)/n +","+ (mtx.d*n|0)/n +","+ (mtx.tx+0.5|0);
+ style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str +","+ (mtx.ty+0.5|0) +")";
+ style.MozTransform = str +"px,"+ (mtx.ty+0.5|0) +"px)";
+ if (!oldProps) { oldProps = this._oldProps = new createjs.DisplayProps(true, null); }
+ oldProps.matrix.copy(mtx);
+ }
+
+ if (oldProps.alpha != props.alpha) {
+ style.opacity = ""+(props.alpha*n|0)/n;
+ oldProps.alpha = props.alpha;
}
- this.bitmapCache.define(this, x, y, width, height, scale, options);
};
+
+ createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject");
+}());
+
+//##############################################################################
+// Filter.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
/**
- * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error.
- * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object
- * will be drawn over the existing cache using the specified compositeOperation.
+ * Base class that all filters should inherit from. Filters need to be applied to objects that have been cached using
+ * the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method. If an object changes, please cache it again, or use
+ * {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
*
* Example
- * Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line
- * will be drawn on top of the old one.
*
- * // Not shown: Creating the shape, and caching it.
- * shapeInstance.clear();
- * shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200);
- * shapeInstance.updateCache();
+ * myInstance.filters = [
+ * new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0),
+ * new createjs.BlurFilter(5, 5, 10)
+ * ];
+ * myInstance.cache(0,0, 100, 100);
*
- * In previous versions caching was handled on DisplayObject but has since been moved to {{#crossLink "BitmapCache"}}{{/crossLink}}.
- * This allows for easier interaction and alternate cache methods like WebGL and {{#crossLink "StageGL"}}{{/crossLink}}.
+ * Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the
+ * margins that need to be applied in order to fully display the filter. For example, the {{#crossLink "BlurFilter"}}{{/crossLink}}
+ * will cause an object to feather outwards, resulting in a margin around the shape.
*
- * @method updateCache
- * @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it.
- *
- * whatwg spec on compositing.
+ * EaselJS Filters
+ * EaselJS comes with a number of pre-built filters:
+ * - {{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object
+ * - {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object
+ * - {{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object
+ * - {{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object
+ * - {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}
+ *
+ *
+ * @class Filter
+ * @constructor
**/
- p.updateCache = function(compositeOperation) {
- if(!this.bitmapCache) {
- throw "cache() must be called before updateCache()";
- }
- this.bitmapCache.update(compositeOperation);
- };
+ function Filter() {
+ /**
+ * A flag stating that this filter uses a context draw mode and cannot be batched into imageData processing.
+ * @property usesContext
+ * @type {boolean}
+ * @default false
+ */
+ this.usesContext = false;
+
+ /**
+ * Another filter that is required to act as part of this filter and created and managed under the hood.
+ * @private
+ * @property _multiPass
+ * @type {Filter}
+ * @default null
+ */
+ this._multiPass = null;
+
+ /**
+ * Pre-processed template shader code. It will be parsed before being fed in into the shader compiler.
+ * This should be based upon StageGL.SHADER_VERTEX_BODY_REGULAR
+ * @property VTX_SHADER
+ * @virtual
+ * @type {String}
+ * @readonly
+ */
+ this.VTX_SHADER_BODY = null;
+ /**
+ * Pre-processed template shader code. It will be parsed before being fed in into the shader compiler.
+ * This should be based upon StageGL.SHADER_FRAGMENT_BODY_REGULAR
+ * @property FRAG_SHADER
+ * @virtual
+ * @type {String}
+ * @readonly
+ */
+ this.FRAG_SHADER_BODY = null;
+ }
+ var p = Filter.prototype;
+
+// public methods:
/**
- * Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information.
- * @method uncache
+ * Provides padding values for this filter. That is, how much the filter will extend the visual bounds of an object it is applied to.
+ * @method getBounds
+ * @param {Rectangle} [rect] If specified, the provided Rectangle instance will be expanded by the padding amounts and returned.
+ * @return {Rectangle} If a `rect` param was provided, it is returned. If not, either a new rectangle with the padding values, or null if no padding is required for this filter.
**/
- p.uncache = function() {
- if(this.bitmapCache) {
- this.bitmapCache.release();
- this.bitmapCache = undefined;
- }
+ p.getBounds = function(rect) {
+ return rect;
};
/**
- * Returns a data URL for the cache, or null if this display object is not cached.
- * Only generated if the cache has changed, otherwise returns last result.
- * @method getCacheDataURL
- * @return {String} The image data url for the cache.
- **/
- p.getCacheDataURL = function() {
- return this.bitmapCache?this.bitmapCache.getCacheDataURL():null;
- };
+ * Assign any unique uniforms or other setup functionality here.
+ * @method shaderParamSetup
+ * @virtual
+ * @param {WebGLContext} gl The context associated with the stage performing the render.
+ * @param {StageGL} stage The stage instance that will be rendering.
+ * @param {ShaderProgram} shaderProgram The compiled shader that is going to be used to perform the render.
+ */
+ p.shaderParamSetup = function(gl, stage, shaderProgram) {};
/**
- * Transforms the specified x and y position from the coordinate space of the display object
- * to the global (stage) coordinate space. For example, this could be used to position an HTML label
- * over a specific point on a nested display object. Returns a Point instance with x and y properties
- * correlating to the transformed coordinates on the stage.
- *
- * Example
- *
- * displayObject.x = 300;
- * displayObject.y = 200;
- * stage.addChild(displayObject);
- * var point = displayObject.localToGlobal(100, 100);
- * // Results in x=400, y=300
- *
- * @method localToGlobal
- * @param {Number} x The x position in the source display object to transform.
- * @param {Number} y The y position in the source display object to transform.
- * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
- * @return {Point} A Point instance with x and y properties correlating to the transformed coordinates
- * on the stage.
+ * Applies the filter to the specified context.
+ * @method applyFilter
+ * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
+ * @param {Number} x The x position to use for the source rect.
+ * @param {Number} y The y position to use for the source rect.
+ * @param {Number} width The width to use for the source rect.
+ * @param {Number} height The height to use for the source rect.
+ * @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
+ * @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
+ * @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
+ * @return {Boolean} If the filter was applied successfully.
**/
- p.localToGlobal = function(x, y, pt) {
- return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x,y, pt||new createjs.Point());
+ p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) {
+ // this is the default behaviour because most filters access pixel data. It is overridden when not needed.
+ targetCtx = targetCtx || ctx;
+ if (targetX == null) { targetX = x; }
+ if (targetY == null) { targetY = y; }
+ try {
+ var imageData = ctx.getImageData(x, y, width, height);
+ } catch (e) {
+ return false;
+ }
+ if (this._applyFilter(imageData)) {
+ targetCtx.putImageData(imageData, targetX, targetY);
+ return true;
+ }
+ return false;
};
/**
- * Transforms the specified x and y position from the global (stage) coordinate space to the
- * coordinate space of the display object. For example, this could be used to determine
- * the current mouse position within the display object. Returns a Point instance with x and y properties
- * correlating to the transformed position in the display object's coordinate space.
- *
- * Example
- *
- * displayObject.x = 300;
- * displayObject.y = 200;
- * stage.addChild(displayObject);
- * var point = displayObject.globalToLocal(100, 100);
- * // Results in x=-200, y=-100
- *
- * @method globalToLocal
- * @param {Number} x The x position on the stage to transform.
- * @param {Number} y The y position on the stage to transform.
- * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
- * @return {Point} A Point instance with x and y properties correlating to the transformed position in the
- * display object's coordinate space.
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
**/
- p.globalToLocal = function(x, y, pt) {
- return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x,y, pt||new createjs.Point());
+ p.toString = function() {
+ return "[Filter]";
};
/**
- * Transforms the specified x and y position from the coordinate space of this display object to the coordinate
- * space of the target display object. Returns a Point instance with x and y properties correlating to the
- * transformed position in the target's coordinate space. Effectively the same as using the following code with
- * {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
- *
- * var pt = this.localToGlobal(x, y);
- * pt = target.globalToLocal(pt.x, pt.y);
- *
- * @method localToLocal
- * @param {Number} x The x position in the source display object to transform.
- * @param {Number} y The y position on the source display object to transform.
- * @param {DisplayObject} target The target display object to which the coordinates will be transformed.
- * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned.
- * @return {Point} Returns a Point instance with x and y properties correlating to the transformed position
- * in the target's coordinate space.
+ * Returns a clone of this Filter instance.
+ * @method clone
+ * @return {Filter} A clone of the current Filter instance.
**/
- p.localToLocal = function(x, y, target, pt) {
- pt = this.localToGlobal(x, y, pt);
- return target.globalToLocal(pt.x, pt.y, pt);
+ p.clone = function() {
+ return new Filter();
};
+
+// private methods:
+ /**
+ * @method _applyFilter
+ * @param {ImageData} imageData Target ImageData instance.
+ * @return {Boolean}
+ **/
+ p._applyFilter = function(imageData) { return true; };
+
+
+ createjs.Filter = Filter;
+}());
+
+//##############################################################################
+// BitmapCache.js
+//##############################################################################
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
/**
- * Shortcut method to quickly set the transform properties on the display object. All parameters are optional.
- * Omitted parameters will have the default value set.
- *
- * Example
+ * The BitmapCache is an internal representation of all the cache properties and logic required in order to "cache"
+ * an object. This information and functionality used to be located on a {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
+ * method in {{#crossLink "DisplayObject"}}{{/crossLink}}, but was moved to its own class.
*
- * displayObject.setTransform(100, 100, 2, 2);
+ * Caching in this context is purely visual, and will render the DisplayObject out into an image to be used instead
+ * of the object. The actual cache itself is still stored on the target with the {{#crossLink "DisplayObject/cacheCanvas:property"}}{{/crossLink}}.
+ * Working with a singular image like a {{#crossLink "Bitmap"}}{{/crossLink}} there is little benefit to performing
+ * a cache as it is already a single image. Caching is best done on containers containing multiple complex parts that
+ * do not move often, so that rendering the image instead will improve overall rendering speed. A cached object will
+ * not visually update until explicitly told to do so with a call to update, much like a Stage. If a cache is being
+ * updated every frame it is likely not improving rendering performance. Cache are best used when updates will be sparse.
*
- * @method setTransform
- * @param {Number} [x=0] The horizontal translation (x position) in pixels
- * @param {Number} [y=0] The vertical translation (y position) in pixels
- * @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1
- * @param {Number} [scaleY=1] the vertical scale, as a percentage of 1
- * @param {Number} [rotation=0] The rotation, in degrees
- * @param {Number} [skewX=0] The horizontal skew factor
- * @param {Number} [skewY=0] The vertical skew factor
- * @param {Number} [regX=0] The horizontal registration point in pixels
- * @param {Number} [regY=0] The vertical registration point in pixels
- * @return {DisplayObject} Returns this instance. Useful for chaining commands.
- * @chainable
- */
- p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
- this.x = x || 0;
- this.y = y || 0;
- this.scaleX = scaleX == null ? 1 : scaleX;
- this.scaleY = scaleY == null ? 1 : scaleY;
- this.rotation = rotation || 0;
- this.skewX = skewX || 0;
- this.skewY = skewY || 0;
- this.regX = regX || 0;
- this.regY = regY || 0;
- return this;
- };
+ * Caching is also a co-requisite for applying filters to prevent expensive filters running constantly without need,
+ * and to physically enable some effects. The BitmapCache is also responsible for applying filters to objects and
+ * reads each {{#crossLink "Filter"}}{{/crossLink}} due to this relationship. Real-time Filters are not recommended
+ * performance wise when dealing with a Context2D canvas. For best performance and to still allow for some visual
+ * effects use a compositeOperation when possible.
+ * @class BitmapCache
+ * @constructor
+ **/
+ function BitmapCache() {
+
+ // public:
+ /**
+ * Width of the cache relative to the target object.
+ * @property width
+ * @protected
+ * @type {Number}
+ * @default undefined
+ **/
+ this.width = undefined;
+
+ /**
+ * Height of the cache relative to the target object.
+ * @property height
+ * @protected
+ * @type {Number}
+ * @default undefined
+ * @todo Should the width and height be protected?
+ **/
+ this.height = undefined;
+
+ /**
+ * Horizontal position of the cache relative to the target's origin.
+ * @property x
+ * @protected
+ * @type {Number}
+ * @default undefined
+ **/
+ this.x = undefined;
+
+ /**
+ * Vertical position of the cache relative to target's origin.
+ * @property y
+ * @protected
+ * @type {Number}
+ * @default undefined
+ **/
+ this.y = undefined;
+
+ /**
+ * The internal scale of the cache image, does not affects display size. This is useful to both increase and
+ * decrease render quality. Objects with increased scales are more likely to look good when scaled up or rotated.
+ * Objects with decreased scales can save on rendering performance.
+ * @property scale
+ * @protected
+ * @type {Number}
+ * @default 1
+ **/
+ this.scale = 1;
+
+ /**
+ * The x offset used for drawing into the cache itself, accounts for both transforms applied.
+ * @property offX
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this.offX = 0;
+
+ /**
+ * The y offset used for drawing into the cache itself, accounts for both transforms applied.
+ * @property offY
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this.offY = 0;
+
+ /**
+ * Track how many times the cache has been updated, mostly used for preventing duplicate cacheURLs.
+ * This can be useful to see if a cache has been updated.
+ * @property cacheID
+ * @type {Number}
+ * @default 0
+ **/
+ this.cacheID = 0;
+
+ // protected:
+ /**
+ * The relative offset of the filter's x position, used for drawing the cache onto its container.
+ * Re-calculated every update call before drawing.
+ * @property _filterOffY
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this._filterOffX = 0;
+
+ /**
+ * The relative offset of the filter's y position, used for drawing the cache onto its container.
+ * Re-calculated every update call before drawing.
+ * @property _filterOffY
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this._filterOffY = 0;
- /**
- * Returns a matrix based on this object's current transform.
- * @method getMatrix
- * @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new
- * Matrix object is returned.
- * @return {Matrix2D} A matrix representing this display object's transform.
- **/
- p.getMatrix = function(matrix) {
- var o = this, mtx = matrix&&matrix.identity() || new createjs.Matrix2D();
- return o.transformMatrix ? mtx.copy(o.transformMatrix) : mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
- };
+ /**
+ * The cacheID when a DataURL was requested.
+ * @property _cacheDataURLID
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this._cacheDataURLID = 0;
+
+ /**
+ * The cache's DataURL, generated on-demand using the getter.
+ * @property _cacheDataURL
+ * @protected
+ * @type {String}
+ * @default null
+ **/
+ this._cacheDataURL = null;
+
+ /**
+ * Internal tracking of final bounding width, approximately width*scale; however, filters can complicate the actual value.
+ * @property _drawWidth
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this._drawWidth = 0;
+
+ /**
+ * Internal tracking of final bounding height, approximately height*scale; however, filters can complicate the actual value.
+ * @property _drawHeight
+ * @protected
+ * @type {Number}
+ * @default 0
+ **/
+ this._drawHeight = 0;
+ }
+ var p = BitmapCache.prototype;
/**
- * Generates a Matrix2D object representing the combined transform of the display object and all of its
- * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can
- * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
- * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
- * @method getConcatenatedMatrix
- * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values.
- * If null, a new Matrix2D object is returned.
- * @return {Matrix2D} The combined matrix.
+ * Returns the bounds that surround all applied filters, relies on each filter to describe how it changes bounds.
+ * @method getFilterBounds
+ * @param {DisplayObject} target The object to check the filter bounds for.
+ * @param {Rectangle} [output=null] Optional parameter, if provided then calculated bounds will be applied to that object.
+ * @return {Rectangle} bounds object representing the bounds with filters.
+ * @static
**/
- p.getConcatenatedMatrix = function(matrix) {
- var o = this, mtx = this.getMatrix(matrix);
- while (o = o.parent) {
- mtx.prependMatrix(o.getMatrix(o._props.matrix));
+ BitmapCache.getFilterBounds = function(target, output) {
+ if(!output){ output = new createjs.Rectangle(); }
+ var filters = target.filters;
+ var filterCount = filters && filters.length;
+ if (!!filterCount <= 0) { return output; }
+
+ for(var i=0; i 0 at
- * the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object.
+ * Actually create the correct cache surface and properties associated with it. Caching and it's benefits are discussed
+ * by the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} function and this class description. Here are the detailed
+ * specifics of how to use the options object.
*
- * Example
+ * - If options.useGL is set to "new" a StageGL is created and contained on this for use when rendering the cache.
+ * - If options.useGL is set to "stage" if the current stage is a StageGL it will be used. If not then it will default to "new".
+ * - If options.useGL is a StageGL instance it will not create one but use the one provided.
+ * - If options.useGL is undefined a Context 2D cache will be performed.
*
- * stage.addEventListener("stagemousedown", handleMouseDown);
- * function handleMouseDown(event) {
- * var hit = myShape.hitTest(event.stageX, event.stageY);
- * }
+ * This means you can use any combination of StageGL and 2D with either, neither, or both the stage and cache being
+ * WebGL. Using "new" with a StageGL display list is highly unrecommended, but still an option. It should be avoided
+ * due to negative performance reasons and the Image loading limitation noted in the class complications above.
*
- * Please note that shape-to-shape collision is not currently supported by EaselJS.
- * @method hitTest
- * @param {Number} x The x position to check in the display object's local coordinates.
- * @param {Number} y The y position to check in the display object's local coordinates.
- * @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject intersect the specified
- * local Point.
- */
- p.hitTest = function(x, y) {
- var ctx = DisplayObject._hitTestContext;
- ctx.setTransform(1, 0, 0, 1, -x, -y);
- this.draw(ctx);
+ * When "options.useGL" is set to the parent stage of the target and WebGL, performance is increased by using
+ * "RenderTextures" instead of canvas elements. These are internal Textures on the graphics card stored in the GPU.
+ * Because they are no longer canvases you cannot perform operations you could with a regular canvas. The benefit
+ * is that this avoids the slowdown of copying the texture back and forth from the GPU to a Canvas element.
+ * This means "stage" is the recommended option when available.
+ *
+ * A StageGL cache does not infer the ability to draw objects a StageGL cannot currently draw, i.e. do not use a
+ * WebGL context cache when caching a Shape, Text, etc.
+ * WebGL cache with a 2D context
+ *
+ * var stage = new createjs.Stage();
+ * var bmp = new createjs.Bitmap(src);
+ * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "new"}); // no StageGL to use, so make one
+ *
+ * var shape = new createjs.Shape();
+ * shape.graphics.clear().fill("red").drawRect(0,0,20,20);
+ * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache
+ *
+ * WebGL cache with a WebGL context
+ *
+ * var stageGL = new createjs.StageGL();
+ * var bmp = new createjs.Bitmap(src);
+ * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "stage"}); // use our StageGL to cache
+ *
+ * var shape = new createjs.Shape();
+ * shape.graphics.clear().fill("red").drawRect(0,0,20,20);
+ * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache
+ *
+ * You may wish to create your own StageGL instance to control factors like clear color, transparency, AA, and
+ * others. If you do, pass a new instance in instead of "true", the library will automatically set the
+ * {{#crossLink "StageGL/isCacheControlled"}}{{/crossLink}} to true on your instance. This will trigger it to behave
+ * correctly, and not assume your main context is WebGL.
+ *
+ * @public
+ * @method BitmapCache.cache
+ * @param {Number} x The x coordinate origin for the cache region.
+ * @param {Number} y The y coordinate origin for the cache region.
+ * @param {Number} width The width of the cache region.
+ * @param {Number} height The height of the cache region.
+ * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape
+ * using myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and
+ * rotate cached elements with greater fidelity. Default is 1.
+ * @param {Object} [options=undefined] Specify additional parameters for the cache logic
+ * @param {undefined|"new"|"stage"|StageGL} [options.useGL=undefined] Select whether to use context 2D, or WebGL rendering, and
+ * whether to make a new stage instance or use an existing one. See above for extensive details on use.
+ * @for BitmapCache
+ */
+ p.define = function(target, x, y, width, height, scale, options) {
+ if(!target){ throw "No symbol to cache"; }
+ this._options = options;
+ this.target = target;
- var hit = this._testHit(ctx);
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- ctx.clearRect(0, 0, 2, 2);
- return hit;
+ this.width = width >= 1 ? width : 1;
+ this.height = height >= 1 ? height : 1;
+ this.x = x || 0;
+ this.y = y || 0;
+ this.scale = scale || 1;
+
+ this.update();
};
/**
- * Provides a chainable shortcut method for setting a number of properties on the instance.
- *
- * Example
- *
- * var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25);
- * var shape = stage.addChild(new Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5});
- *
- * @method set
- * @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
- * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.)
- * @chainable
- */
- p.set = function(props) {
- for (var n in props) { this[n] = props[n]; }
- return this;
+ * Directly called via {{#crossLink "DisplayObject/updateCache:method"}}{{/crossLink}}, but also internally. This
+ * has the dual responsibility of making sure the surface is ready to be drawn to, and performing the draw. For
+ * full details of each behaviour, check the protected functions {{#crossLink "BitmapCache/_updateSurface"}}{{/crossLink}}
+ * and {{#crossLink "BitmapCache/_drawToCache"}}{{/crossLink}} respectively.
+ * @method update
+ * @param {String} [compositeOperation=null] The DisplayObject this cache is linked to.
+ **/
+ p.update = function(compositeOperation) {
+ if(!this.target) { throw "define() must be called before update()"; }
+
+ var filterBounds = BitmapCache.getFilterBounds(this.target);
+ var surface = this.target.cacheCanvas;
+
+ this._drawWidth = Math.ceil(this.width*this.scale) + filterBounds.width;
+ this._drawHeight = Math.ceil(this.height*this.scale) + filterBounds.height;
+
+ if(!surface || this._drawWidth != surface.width || this._drawHeight != surface.height) {
+ this._updateSurface();
+ }
+
+ this._filterOffX = filterBounds.x;
+ this._filterOffY = filterBounds.y;
+ this.offX = this.x*this.scale + this._filterOffX;
+ this.offY = this.y*this.scale + this._filterOffY;
+
+ this._drawToCache(compositeOperation);
+
+ this.cacheID = this.cacheID?this.cacheID+1:1;
};
/**
- * Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation).
- * Objects that have been cached will return the bounds of the cache.
- *
- * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
- * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
- * bounds.
- *
- *
- * All |
- * All display objects support setting bounds manually using setBounds(). Likewise, display objects that
- * have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override
- * the automatic calculations listed below.
- * |
- * Bitmap |
- * Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0).
- * |
- * Sprite |
- * Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified
- * in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}}
- * |
- * Container |
- * Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds().
- * |
- * Shape |
- * Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds.
- * |
- * Text |
- * Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are
- * not, especially when using textBaseline values other than "top".
- * |
- * BitmapText |
- * Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close
- * to (x=0,y=0).
- * |
- *
- *
- * Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and
- * are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the
- * bounds explicitly:
- *
- * var bounds = obj.getBounds();
- * obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
- * // getBounds will now use the set values, instead of recalculating
- *
- * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
- * values if you need to retain it.
- *
- * var myBounds = obj.getBounds().clone();
- * // OR:
- * myRect.copy(obj.getBounds());
- *
- * @method getBounds
- * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this
- * object.
+ * Reset and release all the properties and memory associated with this cache.
+ * @method release
**/
- p.getBounds = function() {
- if (this._bounds) { return this._rectangle.copy(this._bounds); }
- var cacheCanvas = this.cacheCanvas;
- if (cacheCanvas) {
- var scale = this._cacheScale;
- return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale);
+ p.release = function() {
+ if (this._webGLCache) {
+ // if it isn't cache controlled clean up after yourself
+ if (!this._webGLCache.isCacheControlled) {
+ if (this.__lastRT){ this.__lastRT = undefined; }
+ if (this.__rtA){ this._webGLCache._killTextureObject(this.__rtA); }
+ if (this.__rtB){ this._webGLCache._killTextureObject(this.__rtB); }
+ if (this.target && this.target.cacheCanvas){ this._webGLCache._killTextureObject(this.target.cacheCanvas); }
+ }
+ // set the context to none and let the garbage collector get the rest when the canvas itself gets removed
+ this._webGLCache = false;
+ } else {
+ var stage = this.target.stage;
+ if (stage instanceof createjs.StageGL){
+ stage.releaseTexture(this.target.cacheCanvas);
+ }
}
- return null;
+
+ this.target = this.target.cacheCanvas = null;
+ this.cacheID = this._cacheDataURLID = this._cacheDataURL = undefined;
+ this.width = this.height = this.x = this.y = this.offX = this.offY = 0;
+ this.scale = 1;
};
/**
- * Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied).
- * Objects that have been cached will return the transformed bounds of the cache.
- *
- * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use
- * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
- * bounds.
- *
- * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
- * values if you need to retain it.
- *
- * Container instances calculate aggregate bounds for all children that return bounds via getBounds.
- * @method getTransformedBounds
- * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object.
+ * Returns a data URL for the cache, or `null` if this display object is not cached.
+ * Uses {{#crossLink "BitmapCache/cacheID:property"}}{{/crossLink}} to ensure a new data URL is not generated if the
+ * cache has not changed.
+ * @method getCacheDataURL
+ * @return {String} The image data url for the cache.
**/
- p.getTransformedBounds = function() {
- return this._getBounds();
+ p.getCacheDataURL = function() {
+ var cacheCanvas = this.target && this.target.cacheCanvas;
+ if (!cacheCanvas) { return null; }
+ if (this.cacheID != this._cacheDataURLID) {
+ this._cacheDataURLID = this.cacheID;
+ this._cacheDataURL = cacheCanvas.toDataURL?cacheCanvas.toDataURL():null; // incase function is
+ }
+ return this._cacheDataURL;
};
/**
- * Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape &
- * Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always
- * override calculated bounds.
- *
- * The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance
- * with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50).
- * @method setBounds
- * @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds.
- * @param {Number} y The y origin of the bounds.
- * @param {Number} width The width of the bounds.
- * @param {Number} height The height of the bounds.
+ * Use context2D drawing commands to display the cache canvas being used.
+ * @method draw
+ * @param {CanvasRenderingContext2D} ctx The context to draw into.
+ * @return {Boolean} Whether the draw was handled successfully.
**/
- p.setBounds = function(x, y, width, height) {
- if (x == null) { this._bounds = x; return; }
- this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height);
+ p.draw = function(ctx) {
+ if(!this.target) { return false; }
+ ctx.drawImage(this.target.cacheCanvas,
+ this.x + (this._filterOffX/this.scale), this.y + (this._filterOffY/this.scale),
+ this._drawWidth/this.scale, this._drawHeight/this.scale
+ );
+ return true;
+ };
+
+// private methods:
+ /**
+ * Create or resize the invisible canvas/surface that is needed for the display object(s) to draw to,
+ * and in turn be used in their stead when drawing. The surface is resized to the size defined
+ * by the width and height, factoring in scaling and filters. Adjust them to adjust the output size.
+ * @method _updateSurface
+ * @protected
+ **/
+ p._updateSurface = function() {
+ if (!this._options || !this._options.useGL) {
+ var surface = this.target.cacheCanvas;
+
+ // create it if it's missing
+ if(!surface) {
+ surface = this.target.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
+ }
+
+ // now size it
+ surface.width = this._drawWidth;
+ surface.height = this._drawHeight;
+ return;
+ }
+
+ // create it if it's missing
+ if (!this._webGLCache) {
+ if (this._options.useGL === "stage") {
+ if(!(this.target.stage && this.target.stage.isWebGL)){
+ var error = "Cannot use 'stage' for cache because the object's parent stage is ";
+ error += this.target.stage ? "non WebGL." : "not set, please addChild to the correct stage.";
+ throw error;
+ }
+ this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks
+ this._webGLCache = this.target.stage;
+
+ } else if(this._options.useGL === "new") {
+ this.target.cacheCanvas = document.createElement("canvas"); // we can turn off autopurge because we wont be making textures here
+ this._webGLCache = new createjs.StageGL(this.target.cacheCanvas, {antialias: true, transparent: true, autoPurge: -1});
+ this._webGLCache.isCacheControlled = true; // use this flag to control stage sizing and final output
+
+ } else if(this._options.useGL instanceof createjs.StageGL) {
+ this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks
+ this._webGLCache = this._options.useGL;
+ this._webGLCache.isCacheControlled = true; // use this flag to control stage sizing and final output
+
+ } else {
+ throw "Invalid option provided to useGL, expected ['stage', 'new', StageGL, undefined], got "+ this._options.useGL;
+ }
+ }
+
+ // now size render surfaces
+ var surface = this.target.cacheCanvas;
+ var stageGL = this._webGLCache;
+
+ // if we have a dedicated stage we've gotta size it
+ if (stageGL.isCacheControlled) {
+ surface.width = this._drawWidth;
+ surface.height = this._drawHeight;
+ stageGL.updateViewport(this._drawWidth, this._drawHeight);
+ }
+ if (this.target.filters) {
+ // with filters we can't tell how many we'll need but the most we'll ever need is two, so make them now
+ stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
+ stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
+ } else {
+ // without filters then we only need one RenderTexture, and that's only if its not a dedicated stage
+ if (!stageGL.isCacheControlled) {
+ stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
+ }
+ }
};
/**
- * Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are
- * reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements
- * are copied by reference (masks, individual filter instances, hit area)
- * @method clone
- * @return {DisplayObject} A clone of the current DisplayObject instance.
+ * Perform the cache draw out for context 2D now that the setup properties have been performed.
+ * @method _drawToCache
+ * @protected
**/
- p.clone = function() {
- return this._cloneProps(new DisplayObject());
+ p._drawToCache = function(compositeOperation) {
+ var surface = this.target.cacheCanvas;
+ var target = this.target;
+ var webGL = this._webGLCache;
+
+ if (webGL){
+ //TODO: auto split blur into an x/y pass
+ webGL.cacheDraw(target, target.filters, this);
+
+ // we may of swapped around which element the surface is, so we re-fetch it
+ surface = this.target.cacheCanvas;
+
+ surface.width = this._drawWidth;
+ surface.height = this._drawHeight;
+ } else {
+ var ctx = surface.getContext("2d");
+
+ if (!compositeOperation) {
+ ctx.clearRect(0, 0, this._drawWidth+1, this._drawHeight+1);
+ }
+
+ ctx.save();
+ ctx.globalCompositeOperation = compositeOperation;
+ ctx.setTransform(this.scale,0,0,this.scale, -this._filterOffX,-this._filterOffY);
+ ctx.translate(-this.x, -this.y);
+ target.draw(ctx, true);
+ ctx.restore();
+
+
+ if (target.filters && target.filters.length) {
+ this._applyFilters(ctx);
+ }
+ }
+ surface._invalid = true;
};
/**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
+ * Work through every filter and apply its individual visual transformation.
+ * @method _applyFilters
+ * @protected
**/
- p.toString = function() {
- return "[DisplayObject (name="+ this.name +")]";
+ p._applyFilters = function(ctx) {
+ var filters = this.target.filters;
+
+ var w = this._drawWidth;
+ var h = this._drawHeight;
+
+ var data;
+
+ var i = 0, filter = filters[i];
+ do { // this is safe because we wouldn't be in apply filters without a filter count of at least 1
+ if(filter.usesContext){
+ if(data) {
+ ctx.putImageData(data, 0,0);
+ data = null;
+ }
+ filter.applyFilter(ctx, 0,0, w,h);
+ } else {
+ if(!data) {
+ data = ctx.getImageData(0,0, w,h);
+ }
+ filter._applyFilter(data);
+ }
+
+ // work through the multipass if it's there, otherwise move on
+ filter = filter._multiPass !== null ? filter._multiPass : filters[++i];
+ } while (filter);
+
+ //done
+ if(data) {
+ ctx.putImageData(data, 0,0);
+ }
};
+ createjs.BitmapCache = BitmapCache;
+}());
-// private methods:
+//##############################################################################
+// BlurFilter.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
/**
- * Called before the object gets drawn and is a chance to ensure the display state of the object is correct.
- * Mostly used by {{#crossLink "MovieClip"}}{{/crossLink}} and {{#crossLink "BitmapText"}}{{/crossLink}} to
- * correct their internal state and children prior to being drawn.
+ * Applies a box blur to DisplayObjects in context 2D and a Gaussian blur in webgl. Note that this filter is fairly
+ * intensive, particularly if the quality is set higher than 1.
*
- * Is manually called via draw in a {{#crossLink "Stage"}}{{/crossLink}} but is automatically called when
- * present in a {{#crossLink "StageGL"}}{{/crossLink}} instance.
+ * Example
+ * This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}}
+ * method to account for the spread that the blur causes.
*
- * @method _updateState
- * @default null
- */
- p._updateState = null;
-
- // separated so it can be used more easily in subclasses:
- /**
- * @method _cloneProps
- * @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject
- * instance copied into.
- * @return {DisplayObject} o
- * @protected
+ * var shape = new createjs.Shape().set({x:100,y:100});
+ * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
+ *
+ * var blurFilter = new createjs.BlurFilter(5, 5, 1);
+ * shape.filters = [blurFilter];
+ * var bounds = blurFilter.getBounds();
+ *
+ * shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height);
+ *
+ * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
+ * @class BlurFilter
+ * @extends Filter
+ * @constructor
+ * @param {Number} [blurX=0] The horizontal blur radius in pixels.
+ * @param {Number} [blurY=0] The vertical blur radius in pixels.
+ * @param {Number} [quality=1] The number of blur iterations.
**/
- p._cloneProps = function(o) {
- o.alpha = this.alpha;
- o.mouseEnabled = this.mouseEnabled;
- o.tickEnabled = this.tickEnabled;
- o.name = this.name;
- o.regX = this.regX;
- o.regY = this.regY;
- o.rotation = this.rotation;
- o.scaleX = this.scaleX;
- o.scaleY = this.scaleY;
- o.shadow = this.shadow;
- o.skewX = this.skewX;
- o.skewY = this.skewY;
- o.visible = this.visible;
- o.x = this.x;
- o.y = this.y;
- o.compositeOperation = this.compositeOperation;
- o.snapToPixel = this.snapToPixel;
- o.filters = this.filters==null?null:this.filters.slice(0);
- o.mask = this.mask;
- o.hitArea = this.hitArea;
- o.cursor = this.cursor;
- o._bounds = this._bounds;
- return o;
- };
+ function BlurFilter( blurX, blurY, quality) {
+ this.Filter_constructor();
- /**
- * @method _applyShadow
- * @protected
- * @param {CanvasRenderingContext2D} ctx
- * @param {Shadow} shadow
- **/
- p._applyShadow = function(ctx, shadow) {
- shadow = shadow || Shadow.identity;
- ctx.shadowColor = shadow.color;
- ctx.shadowOffsetX = shadow.offsetX;
- ctx.shadowOffsetY = shadow.offsetY;
- ctx.shadowBlur = shadow.blur;
+ // public properties:
+ /**
+ * Horizontal blur radius in pixels
+ * @property blurX
+ * @default 0
+ * @type Number
+ **/
+ this._blurX = blurX;
+ this._blurXTable = [];
+ this._lastBlurX = null;
+
+ /**
+ * Vertical blur radius in pixels
+ * @property blurY
+ * @default 0
+ * @type Number
+ **/
+ this._blurY = blurY;
+ this._blurYTable = [];
+ this._lastBlurY = null;
+
+ /**
+ * Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a
+ * smoother blur, but take twice as long to run.
+ * @property quality
+ * @default 1
+ * @type Number
+ **/
+ this._quality;
+ this._lastQuality = null;
+
+ /**
+ * This is a template to generate the shader for {{#crossLink FRAG_SHADER_BODY}}{{/crossLink}}
+ */
+ this.FRAG_SHADER_TEMPLATE = (
+ "uniform float xWeight[{{blurX}}];" +
+ "uniform float yWeight[{{blurY}}];" +
+ "uniform vec2 textureOffset;" +
+ "void main(void) {" +
+ "vec4 color = vec4(0.0);" +
+
+ "float xAdj = ({{blurX}}.0-1.0)/2.0;" +
+ "float yAdj = ({{blurY}}.0-1.0)/2.0;" +
+ "vec2 sampleOffset;" +
+
+ "for(int i=0; i<{{blurX}}; i++) {" +
+ "for(int j=0; j<{{blurY}}; j++) {" +
+ "sampleOffset = vRenderCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" +
+ "color += texture2D(uSampler, sampleOffset) * (xWeight[i] * yWeight[j]);" +
+ "}" +
+ "}" +
+
+ "gl_FragColor = color.rgba;" +
+ "}"
+ );
+
+ // update the filter using the setters
+ if(isNaN(quality) || quality < 1){ quality = 1; }
+ this.setQuality(quality|0);
+ }
+ var p = createjs.extend(BlurFilter, createjs.Filter);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+ p.getBlurX = function() { return this._blurX; };
+ p.getBlurY = function() { return this._blurY; };
+ p.setBlurX = function(value) {
+ if(isNaN(value) || value < 0){ value = 0; }
+ this._blurX = value;
+ };
+ p.setBlurY = function(value) {
+ if(isNaN(value) || value < 0){ value = 0; }
+ this._blurY = value;
};
+ p.getQuality = function() { return this._quality; };
+ p.setQuality = function(value) {
+ if(isNaN(value) || value < 0){ value = 0; }
+ this._quality = value | 0;
+ };
+ p._getShader = function() {
+ var xChange = this._lastBlurX !== this._blurX;
+ var yChange = this._lastBlurY !== this._blurY;
+ var qChange = this._lastQuality !== this._quality;
+ if(xChange || yChange || qChange) {
+ if(xChange || qChange) { this._blurXTable = this._getTable(this._blurX * this._quality); }
+ if(yChange || qChange) { this._blurYTable = this._getTable(this._blurY * this._quality); }
+ this._updateShader();
+ this._lastBlurX = this._blurX;
+ this._lastBlurY = this._blurY;
+ this._lastQuality = this._quality;
+ return undefined; // force a rebuild
+ }
+ return this._compiledShader;
+ };
+ p._setShader = function() { this._compiledShader; };
+
+ try {
+ Object.defineProperties(p, {
+ blurX: { get: p.getBlurX, set: p.setBlurX },
+ blurY: { get: p.getBlurY, set: p.setBlurY },
+ quality: { get: p.getQuality, set: p.setQuality },
+ _builtShader: { get: p._getShader, set: p._setShader}
+ });
+ } catch (e) { console.log(e); }
/**
- * @method _tick
- * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
- * @protected
- **/
- p._tick = function(evtObj) {
- // because tick can be really performance sensitive, check for listeners before calling dispatchEvent.
- var ls = this._listeners;
- if (ls && ls["tick"]) {
- // reset & reuse the event object to avoid construction / GC costs:
- evtObj.target = null;
- evtObj.propagationStopped = evtObj.immediatePropagationStopped = false;
- this.dispatchEvent(evtObj);
+ * Internal lookup function to create gaussian distribution.
+ * @method _getTable
+ * @param {Number} spread How many steps in the curve.
+ * @return {Array} An array with Math.ceil(spread*2) entries with appropriately distributed weights.
+ */
+ p._getTable = function(spread) {
+ var EDGE = 4.2;
+ if(spread<=1) { return [1]; }
+
+ var result = [];
+ var count = Math.ceil(spread*2);
+ count += (count%2)?0:1;
+ var adjust = (count/2)|0;
+ for(var i = -adjust; i<=adjust; i++) {
+ var x = (i/adjust)*EDGE;
+ result.push(1/Math.sqrt(2*Math.PI) * Math.pow(Math.E, -(Math.pow(x,2)/4)));
}
+ var factor = result.reduce(function(a, b) { return a + b; });
+ return result.map(function(currentValue, index, array) { return currentValue/factor; });
};
/**
- * @method _testHit
- * @protected
- * @param {CanvasRenderingContext2D} ctx
- * @return {Boolean}
- **/
- p._testHit = function(ctx) {
- try {
- var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
- } catch (e) {
- if (!DisplayObject.suppressCrossDomainErrors) {
- throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
- }
- }
- return hit;
+ * Internal update function to create shader properties.
+ * @method _updateShader
+ */
+ p._updateShader = function() {
+ if(this._blurX === undefined || this._blurY === undefined){ return; }
+ var result = this.FRAG_SHADER_TEMPLATE;
+ result = result.replace(/\{\{blurX\}\}/g, (this._blurXTable.length).toFixed(0));
+ result = result.replace(/\{\{blurY\}\}/g, (this._blurYTable.length).toFixed(0));
+ this.FRAG_SHADER_BODY = result;
};
+ /** docced in super class **/
+ p.shaderParamSetup = function(gl, stage, shaderProgram) {
+ // load the normalized gaussian weight tables
+ gl.uniform1fv(
+ gl.getUniformLocation(shaderProgram, "xWeight"),
+ this._blurXTable
+ );
+ gl.uniform1fv(
+ gl.getUniformLocation(shaderProgram, "yWeight"),
+ this._blurYTable
+ );
+
+ // what is the size of a single pixel in -1, 1 (webGL) space
+ gl.uniform2f(
+ gl.getUniformLocation(shaderProgram, "textureOffset"),
+ 2/(stage._viewportWidth*this._quality), 2/(stage._viewportHeight*this._quality)
+ );
+ };
+
+// constants:
/**
- * @method _getBounds
- * @param {Matrix2D} matrix
- * @param {Boolean} ignoreTransform If true, does not apply this object's transform.
- * @return {Rectangle}
+ * Array of multiply values for blur calculations.
+ * @property MUL_TABLE
+ * @type Array
* @protected
+ * @static
**/
- p._getBounds = function(matrix, ignoreTransform){
- return this._transformBounds(this.getBounds(), matrix, ignoreTransform);
- };
+ BlurFilter.MUL_TABLE = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1];
/**
- * @method _transformBounds
- * @param {Rectangle} bounds
- * @param {Matrix2D} matrix
- * @param {Boolean} ignoreTransform
- * @return {Rectangle}
+ * Array of shift values for blur calculations.
+ * @property SHG_TABLE
+ * @type Array
* @protected
+ * @static
**/
- p._transformBounds = function(bounds, matrix, ignoreTransform) {
- if (!bounds) { return bounds; }
- var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix;
- mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
-
- if (x || y) { mtx.appendTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this.
- if (matrix) { mtx.prependMatrix(matrix); }
-
- var x_a = width*mtx.a, x_b = width*mtx.b;
- var y_c = height*mtx.c, y_d = height*mtx.d;
- var tx = mtx.tx, ty = mtx.ty;
-
- var minX = tx, maxX = tx, minY = ty, maxY = ty;
+ BlurFilter.SHG_TABLE = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9];
- if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
- if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
- if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
+// public methods:
+ /** docced in super class **/
+ p.getBounds = function (rect) {
+ var x = this.blurX|0, y = this.blurY| 0;
+ if(x <= 0 && y <= 0) { return rect; }
+ var q = Math.pow(this.quality, 0.2);
+ return (rect || new createjs.Rectangle()).pad(y*q+1,x*q+1,y*q+1,x*q+1);
+ };
+
+ /** docced in super class **/
+ p.clone = function() {
+ return new BlurFilter(this.blurX, this.blurY, this.quality);
+ };
+
+ /** docced in super class **/
+ p.toString = function() {
+ return "[BlurFilter]";
+ };
+
+
+// private methods:
+
+ /** docced in super class **/
+ p._applyFilter = function (imageData) {
+ var radiusX = this._blurX >> 1;
+ if (isNaN(radiusX) || radiusX < 0) return false;
+ var radiusY = this._blurY >> 1;
+ if (isNaN(radiusY) || radiusY < 0) return false;
+ if (radiusX == 0 && radiusY == 0) return false;
+
+ var iterations = this.quality;
+ if (isNaN(iterations) || iterations < 1) iterations = 1;
+ iterations |= 0;
+ if (iterations > 3) iterations = 3;
+ if (iterations < 1) iterations = 1;
+
+ var px = imageData.data;
+ var x=0, y=0, i=0, p=0, yp=0, yi=0, yw=0, r=0, g=0, b=0, a=0, pr=0, pg=0, pb=0, pa=0;
+
+ var divx = (radiusX + radiusX + 1) | 0;
+ var divy = (radiusY + radiusY + 1) | 0;
+ var w = imageData.width | 0;
+ var h = imageData.height | 0;
+
+ var w1 = (w - 1) | 0;
+ var h1 = (h - 1) | 0;
+ var rxp1 = (radiusX + 1) | 0;
+ var ryp1 = (radiusY + 1) | 0;
+
+ var ssx = {r:0,b:0,g:0,a:0};
+ var sx = ssx;
+ for ( i = 1; i < divx; i++ )
+ {
+ sx = sx.n = {r:0,b:0,g:0,a:0};
+ }
+ sx.n = ssx;
+
+ var ssy = {r:0,b:0,g:0,a:0};
+ var sy = ssy;
+ for ( i = 1; i < divy; i++ )
+ {
+ sy = sy.n = {r:0,b:0,g:0,a:0};
+ }
+ sy.n = ssy;
+
+ var si = null;
+
+
+ var mtx = BlurFilter.MUL_TABLE[radiusX] | 0;
+ var stx = BlurFilter.SHG_TABLE[radiusX] | 0;
+ var mty = BlurFilter.MUL_TABLE[radiusY] | 0;
+ var sty = BlurFilter.SHG_TABLE[radiusY] | 0;
+
+ while (iterations-- > 0) {
+
+ yw = yi = 0;
+ var ms = mtx;
+ var ss = stx;
+ for (y = h; --y > -1;) {
+ r = rxp1 * (pr = px[(yi) | 0]);
+ g = rxp1 * (pg = px[(yi + 1) | 0]);
+ b = rxp1 * (pb = px[(yi + 2) | 0]);
+ a = rxp1 * (pa = px[(yi + 3) | 0]);
+
+ sx = ssx;
+
+ for( i = rxp1; --i > -1; )
+ {
+ sx.r = pr;
+ sx.g = pg;
+ sx.b = pb;
+ sx.a = pa;
+ sx = sx.n;
+ }
+
+ for( i = 1; i < rxp1; i++ )
+ {
+ p = (yi + ((w1 < i ? w1 : i) << 2)) | 0;
+ r += ( sx.r = px[p]);
+ g += ( sx.g = px[p+1]);
+ b += ( sx.b = px[p+2]);
+ a += ( sx.a = px[p+3]);
+
+ sx = sx.n;
+ }
+
+ si = ssx;
+ for ( x = 0; x < w; x++ )
+ {
+ px[yi++] = (r * ms) >>> ss;
+ px[yi++] = (g * ms) >>> ss;
+ px[yi++] = (b * ms) >>> ss;
+ px[yi++] = (a * ms) >>> ss;
+
+ p = ((yw + ((p = x + radiusX + 1) < w1 ? p : w1)) << 2);
+
+ r -= si.r - ( si.r = px[p]);
+ g -= si.g - ( si.g = px[p+1]);
+ b -= si.b - ( si.b = px[p+2]);
+ a -= si.a - ( si.a = px[p+3]);
+
+ si = si.n;
+
+ }
+ yw += w;
+ }
+
+ ms = mty;
+ ss = sty;
+ for (x = 0; x < w; x++) {
+ yi = (x << 2) | 0;
+
+ r = (ryp1 * (pr = px[yi])) | 0;
+ g = (ryp1 * (pg = px[(yi + 1) | 0])) | 0;
+ b = (ryp1 * (pb = px[(yi + 2) | 0])) | 0;
+ a = (ryp1 * (pa = px[(yi + 3) | 0])) | 0;
+
+ sy = ssy;
+ for( i = 0; i < ryp1; i++ )
+ {
+ sy.r = pr;
+ sy.g = pg;
+ sy.b = pb;
+ sy.a = pa;
+ sy = sy.n;
+ }
+
+ yp = w;
+
+ for( i = 1; i <= radiusY; i++ )
+ {
+ yi = ( yp + x ) << 2;
+
+ r += ( sy.r = px[yi]);
+ g += ( sy.g = px[yi+1]);
+ b += ( sy.b = px[yi+2]);
+ a += ( sy.a = px[yi+3]);
+
+ sy = sy.n;
+
+ if( i < h1 )
+ {
+ yp += w;
+ }
+ }
+
+ yi = x;
+ si = ssy;
+ if ( iterations > 0 )
+ {
+ for ( y = 0; y < h; y++ )
+ {
+ p = yi << 2;
+ px[p+3] = pa =(a * ms) >>> ss;
+ if ( pa > 0 )
+ {
+ px[p] = ((r * ms) >>> ss );
+ px[p+1] = ((g * ms) >>> ss );
+ px[p+2] = ((b * ms) >>> ss );
+ } else {
+ px[p] = px[p+1] = px[p+2] = 0
+ }
+
+ p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2;
+
+ r -= si.r - ( si.r = px[p]);
+ g -= si.g - ( si.g = px[p+1]);
+ b -= si.b - ( si.b = px[p+2]);
+ a -= si.a - ( si.a = px[p+3]);
+
+ si = si.n;
+
+ yi += w;
+ }
+ } else {
+ for ( y = 0; y < h; y++ )
+ {
+ p = yi << 2;
+ px[p+3] = pa =(a * ms) >>> ss;
+ if ( pa > 0 )
+ {
+ pa = 255 / pa;
+ px[p] = ((r * ms) >>> ss ) * pa;
+ px[p+1] = ((g * ms) >>> ss ) * pa;
+ px[p+2] = ((b * ms) >>> ss ) * pa;
+ } else {
+ px[p] = px[p+1] = px[p+2] = 0
+ }
+
+ p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2;
+
+ r -= si.r - ( si.r = px[p]);
+ g -= si.g - ( si.g = px[p+1]);
+ b -= si.b - ( si.b = px[p+2]);
+ a -= si.a - ( si.a = px[p+3]);
- if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
- if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
- if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
+ si = si.n;
- return bounds.setValues(minX, minY, maxX-minX, maxY-minY);
- };
+ yi += w;
+ }
+ }
+ }
- /**
- * Indicates whether the display object has any mouse event listeners or a cursor.
- * @method _isMouseOpaque
- * @return {Boolean}
- * @protected
- **/
- p._hasMouseEventListener = function() {
- var evts = DisplayObject._MOUSE_EVENTS;
- for (var i= 0, l=evts.length; itransform and alpha
properties concatenated with their parent
- * Container.
- *
- * For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with x=50
- * and alpha=0.7
will be rendered to the canvas at x=150
and alpha=0.35
.
- * Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
- *
- * Example
- *
- * var container = new createjs.Container();
- * container.addChild(bitmapInstance, shapeInstance);
- * container.x = 100;
- *
- * @class Container
- * @extends DisplayObject
- * @constructor
- **/
- function Container() {
- this.DisplayObject_constructor();
-
+ /**
+ * Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will
+ * be copied from the red channel of the map, and the RGB channels will be copied from the target.
+ *
+ * Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much
+ * better performance.
+ *
+ * Example
+ * This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image.
+ *
+ * var box = new createjs.Shape();
+ * box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100)
+ * box.graphics.drawRect(0, 0, 100, 100);
+ * box.cache(0, 0, 100, 100);
+ *
+ * var bmp = new createjs.Bitmap("path/to/image.jpg");
+ * bmp.filters = [
+ * new createjs.AlphaMapFilter(box.cacheCanvas)
+ * ];
+ * bmp.cache(0, 0, 100, 100);
+ * stage.addChild(bmp);
+ *
+ * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
+ * @class AlphaMapFilter
+ * @extends Filter
+ * @constructor
+ * @param {HTMLImageElement|HTMLCanvasElement} alphaMap The greyscale image (or canvas) to use as the alpha value for the
+ * result. This should be exactly the same dimensions as the target.
+ **/
+ function AlphaMapFilter(alphaMap) {
+ this.Filter_constructor();
+
// public properties:
/**
- * The array of children in the display list. You should usually use the child management methods such as
- * {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}},
- * {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is
- * included for advanced uses.
- * @property children
- * @type Array
- * @default null
+ * The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same
+ * dimensions as the target.
+ * @property alphaMap
+ * @type HTMLImageElement|HTMLCanvasElement
**/
- this.children = [];
+ this.alphaMap = alphaMap;
+
+ // private properties:
/**
- * Indicates whether the children of this container are independently enabled for mouse/pointer interaction.
- * If false, the children will be aggregated under the container - for example, a click on a child shape would
- * trigger a click event on the container.
- * @property mouseChildren
- * @type Boolean
- * @default true
+ * @property _alphaMap
+ * @protected
+ * @type HTMLImageElement|HTMLCanvasElement
**/
- this.mouseChildren = true;
+ this._alphaMap = null;
/**
- * If false, the tick will not be propagated to children of this Container. This can provide some performance benefits.
- * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
- * on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
- * @property tickChildren
- * @type Boolean
- * @default true
+ * @property _mapData
+ * @protected
+ * @type Uint8ClampedArray
**/
- this.tickChildren = true;
+ this._mapData = null;
+ this._mapTexture = null;
+
+ this.FRAG_SHADER_BODY = (
+ "uniform sampler2D uAlphaSampler;"+
+
+ "void main(void) {" +
+ "vec4 color = texture2D(uSampler, vRenderCoord);" +
+ "vec4 alphaMap = texture2D(uAlphaSampler, vTextureCoord);" +
+
+ // some image formats can have transparent white rgba(1,1,1, 0) when put on the GPU, this means we need a slight tweak
+ // using ceil ensure that the colour will be used so long as it exists but pure transparency will be treated black
+ "gl_FragColor = vec4(color.rgb, color.a * (alphaMap.r * ceil(alphaMap.a)));" +
+ "}"
+ );
}
- var p = createjs.extend(Container, createjs.DisplayObject);
-
-
-// getter / setters:
- /**
- * Use the {{#crossLink "Container/numChildren:property"}}{{/crossLink}} property instead.
- * @method _getNumChildren
- * @protected
- * @return {Number}
- **/
- p._getNumChildren = function() {
- return this.children.length;
- };
- // Container.getNumChildren is @deprecated. Remove for 1.1+
- p.getNumChildren = createjs.deprecate(p._getNumChildren, "Container.getNumChildren");
+ var p = createjs.extend(AlphaMapFilter, createjs.Filter);
- /**
- * Returns the number of children in the container.
- * @property numChildren
- * @type {Number}
- * @readonly
- **/
- try {
- Object.defineProperties(p, {
- numChildren: { get: p._getNumChildren }
- });
- } catch (e) {}
-
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-// public methods:
- /**
- * Constructor alias for backwards compatibility. This method will be removed in future versions.
- * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
- * @method initialize
- * @deprecated in favour of `createjs.promote()`
- **/
- p.initialize = Container; // TODO: deprecated.
-
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- var hasContent = this.cacheCanvas || this.children.length;
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
+ /** docced in super class **/
+ p.shaderParamSetup = function(gl, stage, shaderProgram) {
+ if(!this._mapTexture) { this._mapTexture = gl.createTexture(); }
+
+ gl.activeTexture(gl.TEXTURE1);
+ gl.bindTexture(gl.TEXTURE_2D, this._mapTexture);
+ stage.setTextureParams(gl);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.alphaMap);
+
+ gl.uniform1i(
+ gl.getUniformLocation(shaderProgram, "uAlphaSampler"),
+ 1
+ );
};
- /**
- * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
- * into itself).
- **/
- p.draw = function(ctx, ignoreCache) {
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
-
- // this ensures we don't have issues with display list changes that occur during a draw:
- var list = this.children.slice();
- for (var i=0,l=list.length; iExample
- *
- * container.addChild(bitmapInstance);
- *
- * You can also add multiple children at once:
- *
- * container.addChild(bitmapInstance, shapeInstance, textInstance);
- *
- * @method addChild
- * @param {DisplayObject} child The display object to add.
- * @return {DisplayObject} The child that was added, or the last child if multiple children were added.
- **/
- p.addChild = function(child) {
- if (child == null) { return child; }
- var l = arguments.length;
- if (l > 1) {
- for (var i=0; iExample
- *
- * addChildAt(child1, index);
- *
- * You can also add multiple children, such as:
- *
- * addChildAt(child1, child2, ..., index);
- *
- * The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list,
- * you could use:
- *
- * container.addChildAt(myShape, container.getChildIndex(otherShape));
- *
- * This would also bump otherShape's index up by one. Fails silently if the index is out of range.
- *
- * @method addChildAt
- * @param {DisplayObject} child The display object to add.
- * @param {Number} index The index to add the child at.
- * @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added.
- **/
- p.addChildAt = function(child, index) {
- var l = arguments.length;
- var indx = arguments[l-1]; // can't use the same name as the index param or it replaces arguments[1]
- if (indx < 0 || indx > this.children.length) { return arguments[l-2]; }
- if (l > 2) {
- for (var i=0; iExample
- *
- * container.removeChild(child);
- *
- * You can also remove multiple children:
- *
- * removeChild(child1, child2, ...);
- *
- * Returns true if the child (or children) was removed, or false if it was not in the display list.
- * @method removeChild
- * @param {DisplayObject} child The child to remove.
- * @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list.
+ * @method _prepAlphaMap
+ * @protected
**/
- p.removeChild = function(child) {
- var l = arguments.length;
- if (l > 1) {
- var good = true;
- for (var i=0; iExample
+ * IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly.
*
- * container.removeChildAt(2);
+ * Example
+ * This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image.
*
- * You can also remove multiple children:
+ * var box = new createjs.Shape();
+ * box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100)
+ * box.graphics.drawRect(0, 0, 100, 100);
+ * box.cache(0, 0, 100, 100);
*
- * container.removeChild(2, 7, ...)
+ * var bmp = new createjs.Bitmap("path/to/image.jpg");
+ * bmp.filters = [
+ * new createjs.AlphaMaskFilter(box.cacheCanvas)
+ * ];
+ * bmp.cache(0, 0, 100, 100);
*
- * Returns true if the child (or children) was removed, or false if any index was out of range.
- * @method removeChildAt
- * @param {Number} index The index of the child to remove.
- * @return {Boolean} true if the child (or children) was removed, or false if any index was out of range.
+ * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
+ * @class AlphaMaskFilter
+ * @extends Filter
+ * @constructor
+ * @param {HTMLImageElement|HTMLCanvasElement} mask
**/
- p.removeChildAt = function(index) {
- var l = arguments.length;
- if (l > 1) {
- var a = [];
- for (var i=0; iExample
+ * Applies the filter to the specified context.
*
- * container.removeAllChildren();
- *
- * @method removeAllChildren
+ * IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters
+ * correctly.
+ * @method applyFilter
+ * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
+ * @param {Number} x The x position to use for the source rect.
+ * @param {Number} y The y position to use for the source rect.
+ * @param {Number} width The width to use for the source rect.
+ * @param {Number} height The height to use for the source rect.
+ * @param {CanvasRenderingContext2D} [targetCtx] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx.
+ * @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x.
+ * @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y.
+ * @return {Boolean} If the filter was applied successfully.
**/
- p.removeAllChildren = function() {
- var kids = this.children;
- while (kids.length) { this._removeChildAt(0); }
+ p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
+ if (!this.mask) { return true; }
+ targetCtx = targetCtx || ctx;
+ if (targetX == null) { targetX = x; }
+ if (targetY == null) { targetY = y; }
+
+ targetCtx.save();
+ if (ctx != targetCtx) {
+ // TODO: support targetCtx and targetX/Y
+ // clearRect, then draw the ctx in?
+ return false;
+ }
+
+ targetCtx.globalCompositeOperation = "destination-in";
+ targetCtx.drawImage(this.mask, targetX, targetY);
+ targetCtx.restore();
+ return true;
+ };
+
+ /** docced in super class **/
+ p.clone = function () {
+ return new AlphaMaskFilter(this.mask);
};
+ /** docced in super class **/
+ p.toString = function () {
+ return "[AlphaMaskFilter]";
+ };
+
+
+ createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter");
+}());
+
+//##############################################################################
+// ColorFilter.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+
+// constructor:
/**
- * Returns the child at the specified index.
+ * Applies a color transform to DisplayObjects.
*
* Example
+ * This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels
+ * to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel.
*
- * container.getChildAt(2);
+ * var shape = new createjs.Shape().set({x:100,y:100});
+ * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
*
- * @method getChildAt
- * @param {Number} index The index of the child to return.
- * @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index.
+ * shape.filters = [
+ * new createjs.ColorFilter(0,0,0,1, 0,0,255,0)
+ * ];
+ * shape.cache(-50, -50, 100, 100);
+ *
+ * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
+ * @class ColorFilter
+ * @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1.
+ * @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1.
+ * @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1.
+ * @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1.
+ * @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range
+ * between -255 and 255.
+ * @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range
+ * between -255 and 255.
+ * @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range
+ * between -255 and 255.
+ * @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range
+ * between -255 and 255.
+ * @constructor
+ * @extends Filter
**/
- p.getChildAt = function(index) {
- return this.children[index];
- };
+ function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) {
+ this.Filter_constructor();
+
+ // public properties:
+ /**
+ * Red channel multiplier.
+ * @property redMultiplier
+ * @type Number
+ **/
+ this.redMultiplier = redMultiplier != null ? redMultiplier : 1;
- /**
- * Returns the child with the specified name.
- * @method getChildByName
- * @param {String} name The name of the child to return.
- * @return {DisplayObject} The child with the specified name.
- **/
- p.getChildByName = function(name) {
- var kids = this.children;
- for (var i=0,l=kids.length;iExample: Display children with a higher y in front.
- *
- * var sortFunction = function(obj1, obj2, options) {
- * if (obj1.y > obj2.y) { return 1; }
- * if (obj1.y < obj2.y) { return -1; }
- * return 0;
- * }
- * container.sortChildren(sortFunction);
- *
- * @method sortChildren
- * @param {Function} sortFunction the function to use to sort the child list. See JavaScript's Array.sort
- * documentation for details.
- **/
- p.sortChildren = function(sortFunction) {
- this.children.sort(sortFunction);
+ /** docced in super class **/
+ p.toString = function() {
+ return "[ColorFilter]";
+ };
+
+ /** docced in super class **/
+ p.clone = function() {
+ return new ColorFilter(
+ this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier,
+ this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset
+ );
+ };
+
+// private methods:
+ /** docced in super class **/
+ p._applyFilter = function(imageData) {
+ var data = imageData.data;
+ var l = data.length;
+ for (var i=0; iExample
*
- * var index = container.getChildIndex(child);
+ * myColorMatrix.adjustHue(20).adjustBrightness(50);
*
- * @method getChildIndex
- * @param {DisplayObject} child The child to return the index of.
- * @return {Number} The index of the specified child. -1 if the child is not found.
+ * See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}
+ * for an example of how to use ColorMatrix to change a DisplayObject's color.
+ * @class ColorMatrix
+ * @param {Number} brightness
+ * @param {Number} contrast
+ * @param {Number} saturation
+ * @param {Number} hue
+ * @constructor
**/
- p.getChildIndex = function(child) {
- return createjs.indexOf(this.children, child);
- };
-
+ function ColorMatrix(brightness, contrast, saturation, hue) {
+ this.setColor(brightness, contrast, saturation, hue);
+ }
+ var p = ColorMatrix.prototype;
+
+// constants:
/**
- * Swaps the children at the specified indexes. Fails silently if either index is out of range.
- * @method swapChildrenAt
- * @param {Number} index1
- * @param {Number} index2
+ * Array of delta values for contrast calculations.
+ * @property DELTA_INDEX
+ * @type Array
+ * @protected
+ * @static
**/
- p.swapChildrenAt = function(index1, index2) {
- var kids = this.children;
- var o1 = kids[index1];
- var o2 = kids[index2];
- if (!o1 || !o2) { return; }
- kids[index1] = o2;
- kids[index2] = o1;
- };
-
+ ColorMatrix.DELTA_INDEX = [
+ 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
+ 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
+ 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
+ 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
+ 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
+ 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
+ 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
+ 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
+ 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
+ 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
+ 10.0
+ ];
+
/**
- * Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this
- * Container.
- * @method swapChildren
- * @param {DisplayObject} child1
- * @param {DisplayObject} child2
+ * Identity matrix values.
+ * @property IDENTITY_MATRIX
+ * @type Array
+ * @protected
+ * @static
**/
- p.swapChildren = function(child1, child2) {
- var kids = this.children;
- var index1,index2;
- for (var i=0,l=kids.length;i= l) { return; }
- for (var i=0;i 0 at the
- * specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all
- * transform properties including regX/Y.
- * @method hitTest
- * @param {Number} x The x position to check in the display object's local coordinates.
- * @param {Number} y The y position to check in the display object's local coordinates.
- * @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified
- * coordinates.
+ * Adjusts the brightness of pixel color by adding the specified value to the red, green and blue channels.
+ * Positive values will make the image brighter, negative values will make it darker.
+ * @method adjustBrightness
+ * @param {Number} value A value between -255 & 255 that will be added to the RGB channels.
+ * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
+ * @chainable
**/
- p.hitTest = function(x, y) {
- // TODO: optimize to use the fast cache check where possible.
- return (this.getObjectUnderPoint(x, y) != null);
+ p.adjustBrightness = function(value) {
+ if (value == 0 || isNaN(value)) { return this; }
+ value = this._cleanValue(value,255);
+ this._multiplyMatrix([
+ 1,0,0,0,value,
+ 0,1,0,0,value,
+ 0,0,1,0,value,
+ 0,0,0,1,0,
+ 0,0,0,0,1
+ ]);
+ return this;
};
/**
- * Returns an array of all display objects under the specified coordinates that are in this container's display
- * list. This routine ignores any display objects with {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
- * set to `false`. The array will be sorted in order of visual depth, with the top-most display object at index 0.
- * This uses shape based hit detection, and can be an expensive operation to run, so it is best to use it carefully.
- * For example, if testing for objects under the mouse, test on tick (instead of on {{#crossLink "DisplayObject/mousemove:event"}}{{/crossLink}}),
- * and only if the mouse's position has changed.
- *
- *
- * - By default (mode=0) this method evaluates all display objects.
- * - By setting the `mode` parameter to `1`, the {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
- * and {{#crossLink "mouseChildren:property"}}{{/crossLink}} properties will be respected.
- * - Setting the `mode` to `2` additionally excludes display objects that do not have active mouse event
- * listeners or a {{#crossLink "DisplayObject:cursor:property"}}{{/crossLink}} property. That is, only objects
- * that would normally intercept mouse interaction will be included. This can significantly improve performance
- * in some cases by reducing the number of display objects that need to be tested.
- *
- *
- * This method accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}.
- * @method getObjectsUnderPoint
- * @param {Number} x The x position in the container to test.
- * @param {Number} y The y position in the container to test.
- * @param {Number} [mode=0] The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
- * @return {Array} An Array of DisplayObjects under the specified coordinates.
+ * Adjusts the contrast of pixel color.
+ * Positive values will increase contrast, negative values will decrease contrast.
+ * @method adjustContrast
+ * @param {Number} value A value between -100 & 100.
+ * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
+ * @chainable
**/
- p.getObjectsUnderPoint = function(x, y, mode) {
- var arr = [];
- var pt = this.localToGlobal(x, y);
- this._getObjectsUnderPoint(pt.x, pt.y, arr, mode>0, mode==1);
- return arr;
+ p.adjustContrast = function(value) {
+ if (value == 0 || isNaN(value)) { return this; }
+ value = this._cleanValue(value,100);
+ var x;
+ if (value<0) {
+ x = 127+value/100*127;
+ } else {
+ x = value%1;
+ if (x == 0) {
+ x = ColorMatrix.DELTA_INDEX[value];
+ } else {
+ x = ColorMatrix.DELTA_INDEX[(value<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(value<<0)+1]*x; // use linear interpolation for more granularity.
+ }
+ x = x*127+127;
+ }
+ this._multiplyMatrix([
+ x/127,0,0,0,0.5*(127-x),
+ 0,x/127,0,0,0.5*(127-x),
+ 0,0,x/127,0,0.5*(127-x),
+ 0,0,0,1,0,
+ 0,0,0,0,1
+ ]);
+ return this;
};
/**
- * Similar to {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}, but returns only the top-most display
- * object. This runs significantly faster than getObjectsUnderPoint()
, but is still potentially an expensive
- * operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information.
- * @method getObjectUnderPoint
- * @param {Number} x The x position in the container to test.
- * @param {Number} y The y position in the container to test.
- * @param {Number} mode The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
- * @return {DisplayObject} The top-most display object under the specified coordinates.
+ * Adjusts the color saturation of the pixel.
+ * Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale).
+ * @method adjustSaturation
+ * @param {Number} value A value between -100 & 100.
+ * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
+ * @chainable
**/
- p.getObjectUnderPoint = function(x, y, mode) {
- var pt = this.localToGlobal(x, y);
- return this._getObjectsUnderPoint(pt.x, pt.y, null, mode>0, mode==1);
- };
-
- /**
- * Docced in superclass.
- */
- p.getBounds = function() {
- return this._getBounds(null, true);
+ p.adjustSaturation = function(value) {
+ if (value == 0 || isNaN(value)) { return this; }
+ value = this._cleanValue(value,100);
+ var x = 1+((value > 0) ? 3*value/100 : value/100);
+ var lumR = 0.3086;
+ var lumG = 0.6094;
+ var lumB = 0.0820;
+ this._multiplyMatrix([
+ lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0,
+ lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0,
+ lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0,
+ 0,0,0,1,0,
+ 0,0,0,0,1
+ ]);
+ return this;
};
-
-
+
+
/**
- * Docced in superclass.
- */
- p.getTransformedBounds = function() {
- return this._getBounds();
+ * Adjusts the hue of the pixel color.
+ * @method adjustHue
+ * @param {Number} value A value between -180 & 180.
+ * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
+ * @chainable
+ **/
+ p.adjustHue = function(value) {
+ if (value == 0 || isNaN(value)) { return this; }
+ value = this._cleanValue(value,180)/180*Math.PI;
+ var cosVal = Math.cos(value);
+ var sinVal = Math.sin(value);
+ var lumR = 0.213;
+ var lumG = 0.715;
+ var lumB = 0.072;
+ this._multiplyMatrix([
+ lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0,
+ lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0,
+ lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0,
+ 0,0,0,1,0,
+ 0,0,0,0,1
+ ]);
+ return this;
};
/**
- * Returns a clone of this Container. Some properties that are specific to this instance's current context are
- * reverted to their defaults (for example .parent).
- * @method clone
- * @param {Boolean} [recursive=false] If true, all of the descendants of this container will be cloned recursively. If false, the
- * properties of the container will be cloned, but the new instance will not have any children.
- * @return {Container} A clone of the current Container instance.
+ * Concatenates (multiplies) the specified matrix with this one.
+ * @method concat
+ * @param {Array} matrix An array or ColorMatrix instance.
+ * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
+ * @chainable
**/
- p.clone = function(recursive) {
- var o = this._cloneProps(new Container());
- if (recursive) { this._cloneChildren(o); }
- return o;
+ p.concat = function(matrix) {
+ matrix = this._fixMatrix(matrix);
+ if (matrix.length != ColorMatrix.LENGTH) { return this; }
+ this._multiplyMatrix(matrix);
+ return this;
};
/**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
+ * Returns a clone of this ColorMatrix.
+ * @method clone
+ * @return {ColorMatrix} A clone of this ColorMatrix.
**/
- p.toString = function() {
- return "[Container (name="+ this.name +")]";
+ p.clone = function() {
+ return (new ColorMatrix()).copy(this);
};
-
-// private methods:
/**
- * @method _tick
- * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
- * @protected
+ * Return a length 25 (5x5) array instance containing this matrix's values.
+ * @method toArray
+ * @return {Array} An array holding this matrix's values.
**/
- p._tick = function(evtObj) {
- if (this.tickChildren) {
- for (var i=this.children.length-1; i>=0; i--) {
- var child = this.children[i];
- if (child.tickEnabled && child._tick) { child._tick(evtObj); }
- }
+ p.toArray = function() {
+ var arr = [];
+ for (var i= 0, l=ColorMatrix.LENGTH; i this.children.length-1) { return false; }
- var child = this.children[index];
- if (child) { child.parent = null; }
- this.children.splice(index, 1);
- if (!silent) { child.dispatchEvent("removed"); }
- return true;
+ p.toString = function() {
+ return "[ColorMatrix]";
};
+
+// private methods:
/**
- * @method _getObjectsUnderPoint
- * @param {Number} x
- * @param {Number} y
- * @param {Array} arr
- * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners.
- * @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object.
- * @param {Number} currentDepth Indicates the current depth of the search.
- * @return {DisplayObject}
+ * @method _multiplyMatrix
+ * @param {Array} matrix
* @protected
**/
- p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener, currentDepth) {
- currentDepth = currentDepth || 0;
- if (!currentDepth && !this._testMask(this, x, y)) { return null; }
- var mtx, ctx = createjs.DisplayObject._hitTestContext;
- activeListener = activeListener || (mouse&&this._hasMouseEventListener());
+ p._multiplyMatrix = function(matrix) {
+ var i, j, k, col = [];
- // draw children one at a time, and check if we get a hit:
- var children = this.children, l = children.length;
- for (var i=l-1; i>=0; i--) {
- var child = children[i];
- var hitArea = child.hitArea;
- if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; }
- if (!hitArea && !this._testMask(child, x, y)) { continue; }
-
- // if a child container has a hitArea then we only need to check its hitAre2a, so we can treat it as a normal DO:
- if (!hitArea && child instanceof Container) {
- var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth+1);
- if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; }
- } else {
- if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; }
-
- // TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense.
- var props = child.getConcatenatedDisplayProps(child._props);
- mtx = props.matrix;
-
- if (hitArea) {
- mtx.appendMatrix(hitArea.getMatrix(hitArea._props.matrix));
- props.alpha = hitArea.alpha;
+ for (i=0;i<5;i++) {
+ for (j=0;j<5;j++) {
+ col[j] = this[j+i*5];
+ }
+ for (j=0;j<5;j++) {
+ var val=0;
+ for (k=0;k<5;k++) {
+ val += matrix[j+k*5]*col[k];
}
-
- ctx.globalAlpha = props.alpha;
- ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
- (hitArea||child).draw(ctx);
- if (!this._testHit(ctx)) { continue; }
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- ctx.clearRect(0, 0, 2, 2);
- if (arr) { arr.push(child); }
- else { return (mouse && !this.mouseChildren) ? this : child; }
+ this[j+i*5] = val;
}
}
- return null;
- };
-
- /**
- * @method _testMask
- * @param {DisplayObject} target
- * @param {Number} x
- * @param {Number} y
- * @return {Boolean} Indicates whether the x/y is within the masked region.
- * @protected
- **/
- p._testMask = function(target, x, y) {
- var mask = target.mask;
- if (!mask || !mask.graphics || mask.graphics.isEmpty()) { return true; }
-
- var mtx = this._props.matrix, parent = target.parent;
- mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity();
- mtx = mask.getMatrix(mask._props.matrix).prependMatrix(mtx);
-
- var ctx = createjs.DisplayObject._hitTestContext;
- ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
-
- // draw the mask as a solid fill:
- mask.graphics.drawAsPath(ctx);
- ctx.fillStyle = "#000";
- ctx.fill();
-
- if (!this._testHit(ctx)) { return false; }
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- ctx.clearRect(0, 0, 2, 2);
-
- return true;
};
-
+
/**
- * @method _getBounds
- * @param {Matrix2D} matrix
- * @param {Boolean} ignoreTransform If true, does not apply this object's transform.
- * @return {Rectangle}
+ * Make sure values are within the specified range, hue has a limit of 180, brightness is 255, others are 100.
+ * @method _cleanValue
+ * @param {Number} value The raw number
+ * @param {Number} limit The maximum that the number can be. The minimum is the limit * -1.
* @protected
**/
- p._getBounds = function(matrix, ignoreTransform) {
- var bounds = this.DisplayObject_getBounds();
- if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
-
- var mtx = this._props.matrix;
- mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
- if (matrix) { mtx.prependMatrix(matrix); }
-
- var l = this.children.length, rect=null;
- for (var i=0; i ColorMatrix.LENGTH) {
+ matrix = matrix.slice(0,ColorMatrix.LENGTH);
}
- return rect;
+ return matrix;
};
- createjs.Container = createjs.promote(Container, "DisplayObject");
-}());
-
-//##############################################################################
-// Stage.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * A stage is the root level {{#crossLink "Container"}}{{/crossLink}} for a display list. Each time its {{#crossLink "Stage/tick"}}{{/crossLink}}
- * method is called, it will render its display list to its target canvas.
- *
- * Example
- * This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child
- * and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}.
- *
- * var stage = new createjs.Stage("canvasElementId");
- * var image = new createjs.Bitmap("imagePath.png");
- * stage.addChild(image);
- * createjs.Ticker.addEventListener("tick", handleTick);
- * function handleTick(event) {
- * image.x += 10;
- * stage.update();
- * }
- *
- * @class Stage
- * @extends Container
- * @constructor
- * @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id
- * of a canvas object in the current document.
- **/
- function Stage(canvas) {
- this.Container_constructor();
-
-
- // public properties:
- /**
- * Indicates whether the stage should automatically clear the canvas before each render. You can set this to false
- * to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for
- * example).
- *
- * Example
- *
- * var stage = new createjs.Stage("canvasId");
- * stage.autoClear = false;
- *
- * @property autoClear
- * @type Boolean
- * @default true
- **/
- this.autoClear = true;
-
- /**
- * The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the
- * first stage that will be ticked (or they will clear each other's render).
- *
- * When changing the canvas property you must disable the events on the old canvas, and enable events on the
- * new canvas or mouse events will not work as expected. For example:
- *
- * myStage.enableDOMEvents(false);
- * myStage.canvas = anotherCanvas;
- * myStage.enableDOMEvents(true);
- *
- * @property canvas
- * @type HTMLCanvasElement | Object
- **/
- this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas;
-
- /**
- * The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
- * position over the canvas, and mouseInBounds will be set to false.
- * @property mouseX
- * @type Number
- * @readonly
- **/
- this.mouseX = 0;
-
- /**
- * The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
- * position over the canvas, and mouseInBounds will be set to false.
- * @property mouseY
- * @type Number
- * @readonly
- **/
- this.mouseY = 0;
-
- /**
- * Specifies the area of the stage to affect when calling update. This can be use to selectively
- * re-draw specific regions of the canvas. If null, the whole canvas area is drawn.
- * @property drawRect
- * @type {Rectangle}
- */
- this.drawRect = null;
-
- /**
- * Indicates whether display objects should be rendered on whole pixels. You can set the
- * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of
- * display objects to false to enable/disable this behaviour on a per instance basis.
- * @property snapToPixelEnabled
- * @type Boolean
- * @default false
- **/
- this.snapToPixelEnabled = false;
-
- /**
- * Indicates whether the mouse is currently within the bounds of the canvas.
- * @property mouseInBounds
- * @type Boolean
- * @default false
- **/
- this.mouseInBounds = false;
-
- /**
- * If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas.
- * @property tickOnUpdate
- * @type Boolean
- * @default true
- **/
- this.tickOnUpdate = true;
-
- /**
- * If true, mouse move events will continue to be called when the mouse leaves the target canvas. See
- * {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}}
- * x/y/rawX/rawY.
- * @property mouseMoveOutside
- * @type Boolean
- * @default false
- **/
- this.mouseMoveOutside = false;
-
-
- /**
- * Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas.
- * This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas.
- * @property preventSelection
- * @type Boolean
- * @default true
- **/
- this.preventSelection = true;
-
- /**
- * The hitArea property is not supported for Stage.
- * @property hitArea
- * @type {DisplayObject}
- * @default null
- */
-
-
- // private properties:
- /**
- * Holds objects with data for each active pointer id. Each object has the following properties:
- * x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position)
- * @property _pointerData
- * @type {Object}
- * @private
- */
- this._pointerData = {};
-
- /**
- * Number of active pointers.
- * @property _pointerCount
- * @type {Object}
- * @private
- */
- this._pointerCount = 0;
-
- /**
- * The ID of the primary pointer.
- * @property _primaryPointerID
- * @type {Object}
- * @private
- */
- this._primaryPointerID = null;
-
- /**
- * @property _mouseOverIntervalID
- * @protected
- * @type Number
- **/
- this._mouseOverIntervalID = null;
-
- /**
- * @property _nextStage
- * @protected
- * @type Stage
- **/
- this._nextStage = null;
-
- /**
- * @property _prevStage
- * @protected
- * @type Stage
- **/
- this._prevStage = null;
-
-
- // initialize:
- this.enableDOMEvents(true);
- }
- var p = createjs.extend(Stage, createjs.Container);
-
-// events:
- /**
- * Dispatched when the user moves the mouse over the canvas.
- * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
- * @event stagemousemove
- * @since 0.6.0
- */
-
- /**
- * Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}}
- * class for a listing of event properties.
- * @event stagemousedown
- * @since 0.6.0
- */
-
- /**
- * Dispatched when the user the user presses somewhere on the stage, then releases the mouse button anywhere that the page can detect it (this varies slightly between browsers).
- * You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds.
- * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
- * @event stagemouseup
- * @since 0.6.0
- */
-
- /**
- * Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false).
- * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
- * class for a listing of event properties.
- * @event mouseleave
- * @since 0.7.0
- */
-
- /**
- * Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true).
- * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
- * class for a listing of event properties.
- * @event mouseenter
- * @since 0.7.0
- */
-
- /**
- * Dispatched each update immediately before the tick event is propagated through the display list.
- * You can call preventDefault on the event object to cancel propagating the tick event.
- * @event tickstart
- * @since 0.7.0
- */
-
- /**
- * Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if
- * tickOnUpdate is false. Precedes the "drawstart" event.
- * @event tickend
- * @since 0.7.0
- */
-
- /**
- * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it.
- * You can call preventDefault on the event object to cancel the draw.
- * @event drawstart
- * @since 0.7.0
- */
-
- /**
- * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
- * @event drawend
- * @since 0.7.0
- */
-
-
-// getter / setters:
- /**
- * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them.
- * This can be useful in cases where you have multiple layered canvases and want user interactions
- * events to pass through. For example, this would relay mouse events from topStage to bottomStage:
- *
- * topStage.nextStage = bottomStage;
- *
- * To disable relaying, set nextStage to null.
- *
- * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings
- * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled.
- * Considerations when using roll over in relay targets:
- * - The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)
- * - All stages that wish to participate in mouse over interaction must enable them via enableMouseOver
- * - All relay targets will share the frequency value of the top-most stage
- *
- * To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing
- * 30 as it's desired frequency):
- * topStage.nextStage = targetStage;
- * topStage.enableMouseOver(10);
- * targetStage.enableMouseOver(30);
- *
- * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its
- * DOM events using:
- *
- * targetStage.enableDOMEvents(false);
- *
- * @property nextStage
- * @type {Stage}
- **/
- p._get_nextStage = function() {
- return this._nextStage;
- };
- p._set_nextStage = function(value) {
- if (this._nextStage) { this._nextStage._prevStage = null; }
- if (value) { value._prevStage = this; }
- this._nextStage = value;
- };
-
- try {
- Object.defineProperties(p, {
- nextStage: { get: p._get_nextStage, set: p._set_nextStage }
- });
- } catch (e) {} // TODO: use Log
-
-
-// public methods:
- /**
- * Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}}
- * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false,
- * and then render the display list to the canvas.
- *
- * @method update
- * @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property.
- **/
- p.update = function(props) {
- if (!this.canvas) { return; }
- if (this.tickOnUpdate) { this.tick(props); }
- if (this.dispatchEvent("drawstart", false, true) === false) { return; }
- createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
- var r = this.drawRect, ctx = this.canvas.getContext("2d");
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- if (this.autoClear) {
- if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); }
- else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); }
- }
- ctx.save();
- if (this.drawRect) {
- ctx.beginPath();
- ctx.rect(r.x, r.y, r.width, r.height);
- ctx.clip();
- }
- this.updateContext(ctx);
- this.draw(ctx, false);
- ctx.restore();
- this.dispatchEvent("drawend");
- };
-
- /**
- * Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
- * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
- *
- * If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is
- * propagated to listeners.
- *
- * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
- * a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be
- * passed as the `props` parameter to `tick()`. For example:
- *
- * Ticker.on("tick", handleTick);
- * function handleTick(evtObj) {
- * // clone the event object from Ticker, and add some custom data to it:
- * var evt = evtObj.clone().set({greeting:"hello", name:"world"});
- *
- * // pass it to stage.update():
- * myStage.update(evt); // subsequently calls tick() with the same param
- * }
- *
- * // ...
- * myDisplayObject.on("tick", handleDisplayObjectTick);
- * function handleDisplayObjectTick(evt) {
- * console.log(evt.delta); // the delta property from the Ticker tick event object
- * console.log(evt.greeting, evt.name); // custom data: "hello world"
- * }
- *
- * @method tick
- * @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property.
- **/
- p.tick = function(props) {
- if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) { return; }
- var evtObj = new createjs.Event("tick");
- if (props) {
- for (var n in props) {
- if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; }
- }
- }
- this._tick(evtObj);
- this.dispatchEvent("tickend");
- };
-
- /**
- * Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}}
- * event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}}
- * directly, using:
- *
- * Ticker.addEventListener("tick", myStage);
- *
- * Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to
- * display object tick handlers, instead of delta
and paused
parameters.
- * @property handleEvent
- * @type Function
- **/
- p.handleEvent = function(evt) {
- if (evt.type == "tick") { this.update(evt); }
- };
-
- /**
- * Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`.
- * @method clear
- **/
- p.clear = function() {
- if (!this.canvas) { return; }
- var ctx = this.canvas.getContext("2d");
- ctx.setTransform(1, 0, 0, 1, 0, 0);
- ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1);
- };
-
- /**
- * Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can
- * be specified as the src value of an image element.
- * @method toDataURL
- * @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color
- * value is allowed. The default value is a transparent background.
- * @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type
- * is passed in, or if the browser does not support the specified MIME type, the default value will be used.
- * @return {String} a Base64 encoded image.
- **/
- p.toDataURL = function(backgroundColor, mimeType) {
- var data, ctx = this.canvas.getContext('2d'), w = this.canvas.width, h = this.canvas.height;
-
- if (backgroundColor) {
- data = ctx.getImageData(0, 0, w, h);
- var compositeOperation = ctx.globalCompositeOperation;
- ctx.globalCompositeOperation = "destination-over";
-
- ctx.fillStyle = backgroundColor;
- ctx.fillRect(0, 0, w, h);
- }
-
- var dataURL = this.canvas.toDataURL(mimeType||"image/png");
-
- if(backgroundColor) {
- ctx.putImageData(data, 0, 0);
- ctx.globalCompositeOperation = compositeOperation;
- }
-
- return dataURL;
- };
-
- /**
- * Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}
- * and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}
- * and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can
- * be expensive to generate, so they are disabled by default. The frequency of the events can be controlled
- * independently of mouse move events via the optional `frequency` parameter.
- *
- * Example
- *
- * var stage = new createjs.Stage("canvasId");
- * stage.enableMouseOver(10); // 10 updates per second
- *
- * @method enableMouseOver
- * @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast
- * mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less
- * responsive, but uses less CPU.
- **/
- p.enableMouseOver = function(frequency) {
- if (this._mouseOverIntervalID) {
- clearInterval(this._mouseOverIntervalID);
- this._mouseOverIntervalID = null;
- if (frequency == 0) {
- this._testMouseOver(true);
- }
- }
- if (frequency == null) { frequency = 20; }
- else if (frequency <= 0) { return; }
- var o = this;
- this._mouseOverIntervalID = setInterval(function(){ o._testMouseOver(); }, 1000/Math.min(50,frequency));
- };
-
- /**
- * Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good
- * practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive
- * events from the page.
- *
- * When changing the canvas property you must disable the events on the old canvas, and enable events on the
- * new canvas or mouse events will not work as expected. For example:
- *
- * myStage.enableDOMEvents(false);
- * myStage.canvas = anotherCanvas;
- * myStage.enableDOMEvents(true);
- *
- * @method enableDOMEvents
- * @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true.
- **/
- p.enableDOMEvents = function(enable) {
- if (enable == null) { enable = true; }
- var n, o, ls = this._eventListeners;
- if (!enable && ls) {
- for (n in ls) {
- o = ls[n];
- o.t.removeEventListener(n, o.f, false);
- }
- this._eventListeners = null;
- } else if (enable && !ls && this.canvas) {
- var t = window.addEventListener ? window : document;
- var _this = this;
- ls = this._eventListeners = {};
- ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} };
- ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} };
- ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} };
- ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} };
-
- for (n in ls) {
- o = ls[n];
- o.t.addEventListener(n, o.f, false);
- }
- }
- };
-
- /**
- * Stage instances cannot be cloned.
- * @method clone
- **/
- p.clone = function() {
- throw("Stage cannot be cloned.");
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Stage (name="+ this.name +")]";
- };
-
-
-// private methods:
- /**
- * @method _getElementRect
- * @protected
- * @param {HTMLElement} e
- **/
- p._getElementRect = function(e) {
- var bounds;
- try { bounds = e.getBoundingClientRect(); } // this can fail on disconnected DOM elements in IE9
- catch (err) { bounds = {top: e.offsetTop, left: e.offsetLeft, width:e.offsetWidth, height:e.offsetHeight}; }
-
- var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0);
- var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || document.body.clientTop || 0);
-
- var styles = window.getComputedStyle ? getComputedStyle(e,null) : e.currentStyle; // IE <9 compatibility.
- var padL = parseInt(styles.paddingLeft)+parseInt(styles.borderLeftWidth);
- var padT = parseInt(styles.paddingTop)+parseInt(styles.borderTopWidth);
- var padR = parseInt(styles.paddingRight)+parseInt(styles.borderRightWidth);
- var padB = parseInt(styles.paddingBottom)+parseInt(styles.borderBottomWidth);
-
- // note: in some browsers bounds properties are read only.
- return {
- left: bounds.left+offX+padL,
- right: bounds.right+offX-padR,
- top: bounds.top+offY+padT,
- bottom: bounds.bottom+offY-padB
- }
- };
-
- /**
- * @method _getPointerData
- * @protected
- * @param {Number} id
- **/
- p._getPointerData = function(id) {
- var data = this._pointerData[id];
- if (!data) { data = this._pointerData[id] = {x:0,y:0}; }
- return data;
- };
-
- /**
- * @method _handleMouseMove
- * @protected
- * @param {MouseEvent} e
- **/
- p._handleMouseMove = function(e) {
- if(!e){ e = window.event; }
- this._handlePointerMove(-1, e, e.pageX, e.pageY);
- };
-
- /**
- * @method _handlePointerMove
- * @protected
- * @param {Number} id
- * @param {Event} e
- * @param {Number} pageX
- * @param {Number} pageY
- * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
- **/
- p._handlePointerMove = function(id, e, pageX, pageY, owner) {
- if (this._prevStage && owner === undefined) { return; } // redundant listener.
- if (!this.canvas) { return; }
- var nextStage=this._nextStage, o=this._getPointerData(id);
-
- var inBounds = o.inBounds;
- this._updatePointerPosition(id, e, pageX, pageY);
- if (inBounds || o.inBounds || this.mouseMoveOutside) {
- if (id === -1 && o.inBounds == !inBounds) {
- this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e);
- }
-
- this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e);
- this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e);
- }
-
- nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null);
- };
-
- /**
- * @method _updatePointerPosition
- * @protected
- * @param {Number} id
- * @param {Event} e
- * @param {Number} pageX
- * @param {Number} pageY
- **/
- p._updatePointerPosition = function(id, e, pageX, pageY) {
- var rect = this._getElementRect(this.canvas);
- pageX -= rect.left;
- pageY -= rect.top;
-
- var w = this.canvas.width;
- var h = this.canvas.height;
- pageX /= (rect.right-rect.left)/w;
- pageY /= (rect.bottom-rect.top)/h;
- var o = this._getPointerData(id);
- if (o.inBounds = (pageX >= 0 && pageY >= 0 && pageX <= w-1 && pageY <= h-1)) {
- o.x = pageX;
- o.y = pageY;
- } else if (this.mouseMoveOutside) {
- o.x = pageX < 0 ? 0 : (pageX > w-1 ? w-1 : pageX);
- o.y = pageY < 0 ? 0 : (pageY > h-1 ? h-1 : pageY);
- }
-
- o.posEvtObj = e;
- o.rawX = pageX;
- o.rawY = pageY;
-
- if (id === this._primaryPointerID || id === -1) {
- this.mouseX = o.x;
- this.mouseY = o.y;
- this.mouseInBounds = o.inBounds;
- }
- };
-
- /**
- * @method _handleMouseUp
- * @protected
- * @param {MouseEvent} e
- **/
- p._handleMouseUp = function(e) {
- this._handlePointerUp(-1, e, false);
- };
-
- /**
- * @method _handlePointerUp
- * @protected
- * @param {Number} id
- * @param {Event} e
- * @param {Boolean} clear
- * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
- **/
- p._handlePointerUp = function(id, e, clear, owner) {
- var nextStage = this._nextStage, o = this._getPointerData(id);
- if (this._prevStage && owner === undefined) { return; } // redundant listener.
-
- var target=null, oTarget = o.target;
- if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
-
- if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; }
-
- if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); }
- this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e);
-
- if (clear) {
- if (id==this._primaryPointerID) { this._primaryPointerID = null; }
- delete(this._pointerData[id]);
- } else { o.target = null; }
-
- nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this);
- };
-
- /**
- * @method _handleMouseDown
- * @protected
- * @param {MouseEvent} e
- **/
- p._handleMouseDown = function(e) {
- this._handlePointerDown(-1, e, e.pageX, e.pageY);
- };
-
- /**
- * @method _handlePointerDown
- * @protected
- * @param {Number} id
- * @param {Event} e
- * @param {Number} pageX
- * @param {Number} pageY
- * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
- **/
- p._handlePointerDown = function(id, e, pageX, pageY, owner) {
- if (this.preventSelection) { e.preventDefault(); }
- if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over.
-
- if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); }
- var target = null, nextStage = this._nextStage, o = this._getPointerData(id);
- if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
-
- if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; }
- this._dispatchMouseEvent(target, "mousedown", true, id, o, e);
-
- nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this);
- };
-
- /**
- * @method _testMouseOver
- * @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target)
- * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
- * @param {Stage} eventTarget The stage that the cursor is actively over.
- * @protected
- **/
- p._testMouseOver = function(clear, owner, eventTarget) {
- if (this._prevStage && owner === undefined) { return; } // redundant listener.
-
- var nextStage = this._nextStage;
- if (!this._mouseOverIntervalID) {
- // not enabled for mouseover, but should still relay the event.
- nextStage&&nextStage._testMouseOver(clear, owner, eventTarget);
- return;
- }
- var o = this._getPointerData(-1);
- // only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs.
- if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; }
-
- var e = o.posEvtObj;
- var isEventTarget = eventTarget || e&&(e.target == this.canvas);
- var target=null, common = -1, cursor="", t, i, l;
-
- if (!owner && (clear || this.mouseInBounds && isEventTarget)) {
- target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true);
- this._mouseOverX = this.mouseX;
- this._mouseOverY = this.mouseY;
- }
-
- var oldList = this._mouseOverTarget||[];
- var oldTarget = oldList[oldList.length-1];
- var list = this._mouseOverTarget = [];
-
- // generate ancestor list and check for cursor:
- t = target;
- while (t) {
- list.unshift(t);
- if (!cursor) { cursor = t.cursor; }
- t = t.parent;
- }
- this.canvas.style.cursor = cursor;
- if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; }
-
- // find common ancestor:
- for (i=0,l=list.length; icommon; i--) {
- this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e, target);
- }
-
- for (i=list.length-1; i>common; i--) {
- this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e, oldTarget);
- }
-
- if (oldTarget != target) {
- this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget);
- }
-
- nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this);
- };
-
- /**
- * @method _handleDoubleClick
- * @protected
- * @param {MouseEvent} e
- * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
- **/
- p._handleDoubleClick = function(e, owner) {
- var target=null, nextStage=this._nextStage, o=this._getPointerData(-1);
- if (!owner) {
- target = this._getObjectsUnderPoint(o.x, o.y, null, true);
- this._dispatchMouseEvent(target, "dblclick", true, -1, o, e);
- }
- nextStage&&nextStage._handleDoubleClick(e, owner || target && this);
- };
-
- /**
- * @method _dispatchMouseEvent
- * @protected
- * @param {DisplayObject} target
- * @param {String} type
- * @param {Boolean} bubbles
- * @param {Number} pointerId
- * @param {Object} o
- * @param {MouseEvent} [nativeEvent]
- * @param {DisplayObject} [relatedTarget]
- **/
- p._dispatchMouseEvent = function(target, type, bubbles, pointerId, o, nativeEvent, relatedTarget) {
- // TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC.
- if (!target || (!bubbles && !target.hasEventListener(type))) { return; }
- /*
- // TODO: account for stage transformations?
- this._mtx = this.getConcatenatedMatrix(this._mtx).invert();
- var pt = this._mtx.transformPoint(o.x, o.y);
- var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID || pointerId==-1, o.rawX, o.rawY);
- */
- var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId === this._primaryPointerID || pointerId === -1, o.rawX, o.rawY, relatedTarget);
- target.dispatchEvent(evt);
- };
-
-
- createjs.Stage = createjs.promote(Stage, "Container");
-}());
-
-//##############################################################################
-// StageGL.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/*
- * README IF EDITING:
- * Terminology for developers:
- *
- * Vertex: a point that help defines a shape, 3 per triangle. Usually has an x,y,z but can have more/less info.
- * Vertex Property: a piece of information attached to the vertex like a vector3 containing x,y,z
- * Index/Indices: used in groups of 3 to define a triangle, points to vertices by their index in an array (some render
- * modes do not use these)
- * Card: a group of 2 triangles used to display a rectangular image
- * U/V: common names for the [0-1] texture co-ordinates on an image
- * Batch: a single call to the renderer, best done as little as possible so multiple cards are put into a single batch
- * Buffer: WebGL array data
- * Program/Shader: For every vertex we run the Vertex shader. The results are used per pixel by the Fragment shader. When
- * combined and paired these are a shader "program"
- * Texture: WebGL representation of image data and associated extra information
- * Slot: A space on the GPU into which textures can be loaded for use in a batch, using "ActiveTexture" switches texture slot.
- */
-
-(function () {
- "use strict";
-
- /**
- * A StageGL instance is the root level {{#crossLink "Container"}}{{/crossLink}} for an WebGL-optimized display list,
- * which is used in place of the usual {{#crossLink "Stage"}}{{/crossLink}}. This class should behave identically to
- * a {{#crossLink "Stage"}}{{/crossLink}} except for WebGL-specific functionality.
- *
- * Each time the {{#crossLink "Stage/tick"}}{{/crossLink}} method is called, the display list is rendered to the
- * target <canvas/> instance, ignoring non-WebGL-compatible display objects. On devices and browsers that don't
- * support WebGL, content will automatically be rendered to canvas 2D context instead.
- *
- * Limitations
- * - {{#crossLink "Shape"}}{{/crossLink}}, {{#crossLink "Shadow"}}{{/crossLink}}, and {{#crossLink "Text"}}{{/crossLink}}
- * are not rendered when added to the display list.
- * - To display something StageGL cannot render, {{#crossLink "displayObject/cache"}}{{/crossLink}} the object.
- * Caches can be rendered regardless of source.
- * - Images are wrapped as a webGL "Texture". Each graphics card has a limit to its concurrent Textures, too many
- * Textures will noticeably slow performance.
- * - Each cache counts as an individual Texture. As such {{#crossLink "SpriteSheet"}}{{/crossLink}} and
- * {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}} are recommended practices to help keep texture counts low.
- * - To use any image node (DOM Image/Canvas Element) between multiple StageGL instances it must be a
- * {{#crossLink "Bitmap/clone"}}{{/crossLink}}, otherwise the GPU texture loading and tracking will get confused.
- * - to avoid an up/down scaled render you must call {{#crossLink "StageGL/updateViewport"}}{{/crossLink}} if you
- * resize your canvas after making a StageGL instance, this will properly size the WebGL context stored in memory.
- * - Best performance in demanding scenarios will come from manual management of texture memory, but it is handled
- * automatically by default. See {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} for details.
- *
- * Example
- * This example creates a StageGL instance, adds a child to it, then uses the EaselJS {{#crossLink "Ticker"}}{{/crossLink}}
- * to update the child and redraw the stage.
- *
- * var stage = new createjs.StageGL("canvasElementId");
- *
- * var image = new createjs.Bitmap("imagePath.png");
- * stage.addChild(image);
- *
- * createjs.Ticker.on("tick", handleTick);
- *
- * function handleTick(event) {
- * image.x += 10;
- * stage.update();
- * }
- *
- * Notes
- * - StageGL is not currently included in the minified version of EaselJS.
- * - {{#crossLink "SpriteContainer"}}{{/crossLink}} (the previous approach to WebGL with EaselJS) has been deprecated.
- * - Earlier versions of WebGL support in EaselJS (SpriteStage and SpriteContainer) had hard limitations on images
- * per container, which have been solved.
- *
- * @class StageGL
- * @extends Stage
- * @constructor
- * @param {HTMLCanvasElement | String | Object} canvas A canvas object that StageGL will render to, or the string id
- * of a canvas object in the current DOM.
- * @param {Object} options All the option parameters in a reference object, some are not supported by some browsers.
- * @param {Boolean} [options.preserveBuffer=false] If `true`, the canvas is NOT auto-cleared by WebGL (the spec
- * discourages setting this to `true`). This is useful if you want persistent draw effects.
- * @param {Boolean} [options.antialias=false] Specifies whether or not the browser's WebGL implementation should try
- * to perform anti-aliasing. This will also enable linear pixel sampling on power-of-two textures (smoother images).
- * @param {Boolean} [options.transparent=false] If `true`, the canvas is transparent. This is very
- * expensive, and should be used with caution.
- * @param {Boolean} [options.premultiply=false] Alters color handling. If `true`, this assumes the shader must
- * account for pre-multiplied alpha. This can help avoid visual halo effects with some assets, but may also cause
- * problems with other assets.
- * @param {Integer} [options.autoPurge=1200] How often the system should automatically dump unused textures with
- * `purgeTextures(autoPurge)` every `autoPurge/2` draws. See {{#crossLink "StageGL/purgeTextures"}}{{/crossLink}} for more
- * information.
- */
- function StageGL(canvas, options) {
- this.Stage_constructor(canvas);
-
- if (options !== undefined) {
- if (typeof options !== "object"){ throw("Invalid options object"); }
- var premultiply = options.premultiply;
- var transparent = options.transparent;
- var antialias = options.antialias;
- var preserveBuffer = options.preserveBuffer;
- var autoPurge = options.autoPurge;
- }
-
-// public properties:
- /**
- * Console log potential issues and problems. This is designed to have minimal performance impact, so
- * if extensive debugging information is required, this may be inadequate. See {{#crossLink "WebGLInspector"}}{{/crossLink}}
- * @property vocalDebug
- * @type {Boolean}
- * @default false
- */
- this.vocalDebug = false;
-
-// private properties:
- /**
- * Specifies whether or not the canvas is auto-cleared by WebGL. The WebGL spec discourages `true`.
- * If true, the canvas is NOT auto-cleared by WebGL. Used when the canvas context is created and requires
- * context re-creation to update.
- * @property _preserveBuffer
- * @protected
- * @type {Boolean}
- * @default false
- */
- this._preserveBuffer = preserveBuffer||false;
-
- /**
- * Specifies whether or not the browser's WebGL implementation should try to perform anti-aliasing.
- * @property _antialias
- * @protected
- * @type {Boolean}
- * @default false
- */
- this._antialias = antialias||false;
-
- /**
- * Specifies whether or not the browser's WebGL implementation should be transparent.
- * @property _transparent
- * @protected
- * @type {Boolean}
- * @default false
- */
- this._transparent = transparent||false;
-
- /**
- * Specifies whether or not StageGL is handling colours as premultiplied alpha.
- * @property _premultiply
- * @protected
- * @type {Boolean}
- * @default false
- */
- this._premultiply = premultiply||false;
-
- /**
- * Internal value of {{#crossLink "StageGL/autoPurge"}}{{/crossLink}}
- * @property _autoPurge
- * @protected
- * @type {Integer}
- * @default null
- */
- this._autoPurge = undefined;
- this.autoPurge = autoPurge; //getter/setter handles setting the real value and validating
-
- /**
- * The width in px of the drawing surface saved in memory.
- * @property _viewportWidth
- * @protected
- * @type {Number}
- * @default 0
- */
- this._viewportWidth = 0;
-
- /**
- * The height in px of the drawing surface saved in memory.
- * @property _viewportHeight
- * @protected
- * @type {Number}
- * @default 0
- */
- this._viewportHeight = 0;
-
- /**
- * A 2D projection matrix used to convert WebGL's viewspace into canvas co-ordinates. Regular canvas display
- * uses Top-Left values of [0,0] where WebGL uses a Center [0,0] Top-Right [1,1] (euclidean) system.
- * @property _projectionMatrix
- * @protected
- * @type {Float32Array}
- * @default null
- */
- this._projectionMatrix = null;
-
- /**
- * The current WebGL canvas context. Often shorthanded to just "gl" in many parts of the code.
- * @property _webGLContext
- * @protected
- * @type {WebGLRenderingContext}
- * @default null
- */
- this._webGLContext = null;
-
- /**
- * The color to use when the WebGL canvas has been cleared. May appear as a background color. Defaults to grey.
- * @property _clearColor
- * @protected
- * @type {Object}
- * @default {r: 0.50, g: 0.50, b: 0.50, a: 0.00}
- */
- this._clearColor = {r: 0.50, g: 0.50, b: 0.50, a: 0.00};
-
- /**
- * The maximum number of cards (aka a single sprite) that can be drawn in one draw call. Use getter/setters to
- * modify otherwise internal buffers may be incorrect sizes.
- * @property _maxCardsPerBatch
- * @protected
- * @type {Number}
- * @default StageGL.DEFAULT_MAX_BATCH_SIZE (10000)
- */
- this._maxCardsPerBatch = StageGL.DEFAULT_MAX_BATCH_SIZE; //TODO: write getter/setters for this
-
- /**
- * The shader program used to draw the current batch.
- * @property _activeShader
- * @protected
- * @type {WebGLProgram}
- * @default null
- */
- this._activeShader = null;
-
- /**
- * The vertex position data for the current draw call.
- * @property _vertices
- * @protected
- * @type {Float32Array}
- * @default null
- */
- this._vertices = null;
-
- /**
- * The WebGL buffer attached to {{#crossLink "StageGL/_vertices:property"}}{{/crossLink}}.
- * @property _vertexPositionBuffer
- * @protected
- * @type {WebGLBuffer}
- * @default null
- */
- this._vertexPositionBuffer = null;
-
- /**
- * The vertex U/V data for the current draw call.
- * @property _uvs
- * @protected
- * @type {Float32Array}
- * @default null
- */
- this._uvs = null;
-
- /**
- * The WebGL buffer attached to {{#crossLink "StageGL/_uvs:property"}}{{/crossLink}}.
- * @property _uvPositionBuffer
- * @protected
- * @type {WebGLBuffer}
- * @default null
- */
- this._uvPositionBuffer = null;
-
- /**
- * The vertex indices data for the current draw call.
- * @property _indices
- * @protected
- * @type {Float32Array}
- * @default null
- */
- this._indices = null;
-
- /**
- * The WebGL buffer attached to {{#crossLink "StageGL/_indices:property"}}{{/crossLink}}.
- * @property _textureIndexBuffer
- * @protected
- * @type {WebGLBuffer}
- * @default null
- */
- this._textureIndexBuffer = null;
-
- /**
- * The vertices data for the current draw call.
- * @property _alphas
- * @protected
- * @type {Float32Array}
- * @default null
- */
- this._alphas = null;
-
- /**
- * The WebGL buffer attached to {{#crossLink "StageGL/_alphas:property"}}{{/crossLink}}.
- * @property _alphaBuffer
- * @protected
- * @type {WebGLBuffer}
- * @default null
- */
- this._alphaBuffer = null;
-
- /**
- * An index based lookup of every WebGL Texture currently in use.
- * @property _drawTexture
- * @protected
- * @type {Array}
- */
- this._textureDictionary = [];
-
- /**
- * A string based lookup hash of which index a texture is stored at in the dictionary. The lookup string is
- * often the src url.
- * @property _textureIDs
- * @protected
- * @type {Object}
- */
- this._textureIDs = {};
-
- /**
- * An array of all the textures currently loaded into the GPU. The index in the array matches the GPU index.
- * @property _batchTextures
- * @protected
- * @type {Array}
- */
- this._batchTextures = [];
-
- /**
- * An array of all the simple filler textures used to prevent issues with missing textures in a batch.
- * @property _baseTextures
- * @protected
- * @type {Array}
- */
- this._baseTextures = [];
-
- /**
- * The number of concurrent textures the GPU can handle. This value is dynamically set from WebGL during initialization
- * via `gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)`. The WebGL spec states that the lowest guaranteed value is 8,
- * but it could be higher. Do not set this value higher than the value returned by the GPU. Setting it lower will
- * probably reduce performance, but may be advisable to reserve slots for custom filter work.
- * NOTE: Can also act as a length for {{#crossLink "StageGL/_batchTextures:property"}}.
- * @property _batchTextureCount
- * @protected
- * @type {Number}
- * @default 8
- */
- this._batchTextureCount = 8;
-
- /**
- * The location at which the last texture was inserted into a GPU slot in {{#crossLink "StageGL/_batchTextures:property"}}{{/crossLink}}.
- * Manual control of this variable can yield improvements in performance by intelligently replacing textures
- * inside a batch to reduce texture re-load. It is impossible to write automated general use code, as it requires
- * display list look ahead inspection and/or render foreknowledge.
- * @property _lastTextureInsert
- * @protected
- * @type {Number}
- * @default -1
- */
- this._lastTextureInsert = -1;
-
- /**
- * The current batch being drawn, A batch consists of a call to `drawElements` on the GPU. Many of these calls
- * can occur per draw.
- * @property _batchId
- * @protected
- * @type {Number}
- * @default 0
- */
- this._batchID = 0;
-
- /**
- * The current draw being performed, may contain multiple batches. Comparing to {{#crossLink "StageGL/_batchID:property"}}{{/crossLink}}
- * can reveal batching efficiency.
- * @property _drawID
- * @protected
- * @type {Number}
- * @default 0
- */
- this._drawID = 0;
-
- /**
- * Used to prevent textures in certain GPU slots from being replaced by an insert.
- * @property _slotBlackList
- * @protected
- * @type {Array}
- */
- this._slotBlacklist = [];
-
- /**
- * Used to prevent nested draw calls from accidentally overwriting drawing information by tracking depth.
- * @property _isDrawing
- * @protected
- * @type {Number}
- * @default 0
- */
- this._isDrawing = 0;
-
- /**
- * Used to ensure every canvas used as a texture source has a unique ID.
- * @property _lastTrackedCanvas
- * @protected
- * @type {Number}
- * @default 0
- */
- this._lastTrackedCanvas = 0;
-
- /**
- * Controls whether final rendering output of a {{#crossLink "cacheDraw"}}{{/crossLink}} is the canvas or a render
- * texture. See the {{#crossLink "cache"}}{{/crossLink}} function modifications for full implications and discussion.
- * @property isCacheControlled
- * @protected
- * @type {Boolean}
- * @default false
- * @todo LM: is this supposed to be _isCacheControlled since its private?
- */
- this.isCacheControlled = false;
-
- /**
- * Used to counter-position the object being cached so it aligns with the cache surface. Additionally ensures
- * that all rendering starts with a top level container.
- * @property _cacheContainer
- * @protected
- * @type {Container}
- * @default An instance of an EaselJS Container.
- */
- this._cacheContainer = new createjs.Container();
-
- // and begin
- this._initializeWebGL();
- }
- var p = createjs.extend(StageGL, createjs.Stage);
-
-// static methods:
- /**
- * Calculate the U/V co-ordinate based info for sprite frames. Instead of pixel count it uses a 0-1 space. Also includes
- * the ability to get info back for a specific frame, or only calculate that one frame.
- *
- * //generate UV rects for all entries
- * StageGL.buildUVRects( spriteSheetA );
- * //generate all, fetch the first
- * var firstFrame = StageGL.buildUVRects( spriteSheetB, 0 );
- * //generate the rect for just a single frame for performance's sake
- * var newFrame = StageGL.buildUVRects( dynamicSpriteSheet, newFrameIndex, true );
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method buildUVRects
- * @param {SpriteSheet} spritesheet The spritesheet to find the frames on
- * @param {int} [target=-1] The index of the frame to return
- * @param {Boolean} [onlyTarget=false] Whether "target" is the only frame that gets calculated
- * @static
- * @return {Object} the target frame if supplied and present or a generic frame {t, l, b, r}
- */
- StageGL.buildUVRects = function (spritesheet, target, onlyTarget) {
- if (!spritesheet || !spritesheet._frames) { return null; }
- if (target === undefined) { target = -1; }
- if (onlyTarget === undefined) { onlyTarget = false; }
-
- var start = (target != -1 && onlyTarget)?(target):(0);
- var end = (target != -1 && onlyTarget)?(target+1):(spritesheet._frames.length);
- for (var i=start; i 0.0035) {" + // 1/255 = 0.0039, so ignore any value below 1 because it's probably noise
- "gl_FragColor = vec4(color.rgb/color.a, color.a * alphaValue);" +
- "} else {" +
- "gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);" +
- "}"
- );
-
- //TODO: DHG: a real particle shader
- /**
- * @property PARTICLE_VERTEX_BODY
- * @todo
- * @final
- * @static
- * @type {String}
- * @readonly
- */
- StageGL.PARTICLE_VERTEX_BODY = (
- StageGL.REGULAR_VERTEX_BODY
- );
- /**
- * @property PARTICLE_FRAGMENT_BODY
- * @todo
- * @final
- * @static
- * @type {String}
- * @readonly
- */
- StageGL.PARTICLE_FRAGMENT_BODY = (
- StageGL.REGULAR_FRAGMENT_BODY
- );
-
- /**
- * Portion of the shader that contains the "varying" properties required in both vertex and fragment shaders. The
- * cover shader is designed to be a simple vertex/uv only texture render that covers the render surface. Shader
- * code may contain templates that are replaced pre-compile.
- * @property COVER_VARYING_HEADER
- * @static
- * @final
- * @type {String}
- * @readonly
- */
- StageGL.COVER_VARYING_HEADER = (
- "precision mediump float;" +
-
- "varying highp vec2 vRenderCoord;" +
- "varying highp vec2 vTextureCoord;"
- );
-
- /**
- * Actual full header for the vertex shader. Includes the varying header. The cover shader is designed to be a
- * simple vertex/uv only texture render that covers the render surface. Shader code may contain templates that are
- * replaced pre-compile.
- * @property COVER_VERTEX_HEADER
- * @static
- * @final
- * @type {String}
- * @readonly
- */
- StageGL.COVER_VERTEX_HEADER = (
- StageGL.COVER_VARYING_HEADER +
- "attribute vec2 vertexPosition;" +
- "attribute vec2 uvPosition;" +
- "uniform float uUpright;"
- );
-
- /**
- * Actual full header for the fragment shader. Includes the varying header. The cover shader is designed to be a
- * simple vertex/uv only texture render that covers the render surface. Shader code may contain templates that are
- * replaced pre-compile.
- * @property COVER_FRAGMENT_HEADER
- * @static
- * @final
- * @type {String}
- * @readonly
- */
- StageGL.COVER_FRAGMENT_HEADER = (
- StageGL.COVER_VARYING_HEADER +
- "uniform sampler2D uSampler;"
- );
-
- /**
- * Body of the vertex shader. The cover shader is designed to be a simple vertex/uv only texture render that covers
- * the render surface. Shader code may contain templates that are replaced pre-compile.
- * @property COVER_VERTEX_BODY
- * @static
- * @final
- * @type {String}
- * @readonly
- */
- StageGL.COVER_VERTEX_BODY = (
- "void main(void) {" +
- "gl_Position = vec4(vertexPosition.x, vertexPosition.y, 0.0, 1.0);" +
- "vRenderCoord = uvPosition;" +
- "vTextureCoord = vec2(uvPosition.x, abs(uUpright - uvPosition.y));" +
- "}"
- );
-
- /**
- * Body of the fragment shader. The cover shader is designed to be a simple vertex/uv only texture render that
- * covers the render surface. Shader code may contain templates that are replaced pre-compile.
- * @property COVER_FRAGMENT_BODY
- * @static
- * @final
- * @type {String}
- * @readonly
- */
- StageGL.COVER_FRAGMENT_BODY = (
- "void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
- "gl_FragColor = color;" +
- "}"
- );
-
-// events:
- /**
- * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it. You can call
- * {{#crossLink "Event/preventDefault"}}{{/crossLink}} on the event to cancel the draw.
- * @event drawstart
- */
-
- /**
- * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
- * @event drawend
- */
-
-// getter / setters:
- p._get_isWebGL = function () {
- return !!this._webGLContext;
- };
-
- p._set_autoPurge = function (value) {
- value = isNaN(value)?1200:value;
- if (value != -1) {
- value = value<10?10:value;
- }
- this._autoPurge = value;
- };
- p._get_autoPurge = function () {
- return Number(this._autoPurge);
- };
-
- try {
- Object.defineProperties(p, {
- /**
- * Indicates whether WebGL is being used for rendering. For example, this would be `false` if WebGL is not
- * supported in the browser.
- * @property isWebGL
- * @type {Boolean}
- * @readonly
- */
- isWebGL: { get: p._get_isWebGL },
-
- /**
- * Specifies whether or not StageGL is automatically purging unused textures. Higher numbers purge less
- * often. Values below 10 are upgraded to 10, and -1 disables this feature.
- * @property autoPurge
- * @protected
- * @type {Integer}
- * @default 1000
- */
- autoPurge: { get: p._get_autoPurge, set: p._set_autoPurge }
- });
- } catch (e) {} // TODO: use Log
-
-
-// constructor methods:
- /**
- * Create and properly initialize the WebGL instance.
- * @method _initializeWebGL
- * @protected
- * @return {WebGLRenderingContext}
- */
- p._initializeWebGL = function () {
- if (this.canvas) {
- if (!this._webGLContext || this._webGLContext.canvas !== this.canvas) {
- // A context hasn't been defined yet,
- // OR the defined context belongs to a different canvas, so reinitialize.
-
- // defaults and options
- var options = {
- depth: false, // Disable the depth buffer as it isn't used.
- alpha: this._transparent, // Make the canvas background transparent.
- stencil: true,
- antialias: this._antialias,
- premultipliedAlpha: this._premultiply, // Assume the drawing buffer contains colors with premultiplied alpha.
- preserveDrawingBuffer: this._preserveBuffer
- };
-
- var gl = this._webGLContext = this._fetchWebGLContext(this.canvas, options);
- if (!gl) { return null; }
-
- this.updateSimultaneousTextureCount(gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS));
- this._maxTextureSlots = gl.getParameter(gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS);
- this._createBuffers(gl);
- this._initTextures(gl);
-
- gl.disable(gl.DEPTH_TEST);
- gl.enable(gl.BLEND);
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
- gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, this._premultiply);
- //gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
-
- this._webGLContext.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a);
- this.updateViewport(this._viewportWidth || this.canvas.width, this._viewportHeight || this.canvas.height);
- }
- } else {
- this._webGLContext = null;
- }
- return this._webGLContext;
- };
-
-// public methods:
- /**
- * Docced in superclass
- */
- p.update = function (props) {
- if (!this.canvas) { return; }
- if (this.tickOnUpdate) { this.tick(props); }
- this.dispatchEvent("drawstart");
- if (this.autoClear) { this.clear(); }
-
- if (this._webGLContext) {
- // Use WebGL.
- this._batchDraw(this, this._webGLContext);
- if (this._autoPurge != -1 && !(this._drawID%((this._autoPurge/2)|0))) {
- this.purgeTextures(this._autoPurge);
- }
- } else {
- // Use 2D.
- var ctx = this.canvas.getContext("2d");
- ctx.save();
- this.updateContext(ctx);
- this.draw(ctx, false);
- ctx.restore();
- }
- this.dispatchEvent("drawend");
- };
-
- /**
- * Docced in superclass
- */
- p.clear = function () {
- if (!this.canvas) { return; }
- if (StageGL.isWebGLActive(this._webGLContext)) {
- var gl = this._webGLContext;
- var cc = this._clearColor;
- var adjust = this._transparent ? cc.a : 1.0;
- // Use WebGL settings; adjust for pre multiplied alpha appropriate to scenario
- this._webGLContext.clearColor(cc.r * adjust, cc.g * adjust, cc.b * adjust, adjust);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._webGLContext.clearColor(cc.r, cc.g, cc.b, cc.a);
- } else {
- // Use 2D.
- this.Stage_clear();
- }
- };
-
- /**
- * Draws the stage into the supplied context if possible. Many WebGL properties only exist on their context. As such
- * you cannot share contexts among many StageGLs and each context requires a unique StageGL instance. Contexts that
- * don't match the context managed by this StageGL will be treated as a 2D context.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D | WebGLRenderingContext} context The context object to draw into.
- * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For
- * example, used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
- * @return {Boolean} If the draw was handled by this function
- */
- p.draw = function (context, ignoreCache) {
- if (context === this._webGLContext && StageGL.isWebGLActive(this._webGLContext)) {
- var gl = this._webGLContext;
- this._batchDraw(this, gl, ignoreCache);
- return true;
- } else {
- return this.Stage_draw(context, ignoreCache);
- }
- };
-
- /**
- * Draws the target into the correct context, be it a canvas or Render Texture using WebGL.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method cacheDraw
- * @param {DisplayObject} target The object we're drawing into cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
- * @param {Array} filters The filters we're drawing into cache.
- * @param {BitmapCache} manager The BitmapCache instance looking after the cache
- * @return {Boolean} If the draw was handled by this function
- */
- p.cacheDraw = function (target, filters, manager) {
- if (StageGL.isWebGLActive(this._webGLContext)) {
- var gl = this._webGLContext;
- this._cacheDraw(gl, target, filters, manager);
- return true;
- } else {
- return false;
- }
- };
-
- /**
- * Blocks, or frees a texture "slot" on the GPU. Can be useful if you are overflowing textures. When overflowing
- * textures they are re-uploaded to the GPU every time they're encountered, this can be expensive with large textures.
- * By blocking the slot you reduce available slots, potentially increasing draw calls, but mostly you prevent a
- * texture being re-uploaded if it would have moved slots due to overflow.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * For example, block the slot a background image is stored in so there is less re-loading of that image.
- * @method protectTextureSlot
- * @param {Number} id The slot to be affected
- * @param {Boolean} [lock=false] Whether this slot is the one being locked.
- */
- p.protectTextureSlot = function (id, lock) {
- if (id > this._maxTextureSlots || id < 0) {
- throw "Slot outside of acceptable range";
- }
- this._slotBlacklist[id] = !!lock;
- };
-
- /**
- * Render textures can't draw into themselves so any item being used for renderTextures needs two to alternate between.
- * This function creates, gets, and toggles the render surface between the two.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method getTargetRenderTexture
- * @param {DisplayObject} target The object associated with the render textures, usually a cached object.
- * @param {Number} w The width to create the texture at.
- * @param {Number} h The height to create the texture at.
- * @return {Objet}
- * @todo fill in return type
- */
- p.getTargetRenderTexture = function (target, w, h) {
- var result, toggle = false;
- var gl = this._webGLContext;
- if (target.__lastRT !== undefined && target.__lastRT === target.__rtA) { toggle = true; }
- if (!toggle) {
- if (target.__rtA === undefined) {
- target.__rtA = this.getRenderBufferTexture(w, h);
- } else {
- if (w != target.__rtA._width || h != target.__rtA._height) {
- this.resizeTexture(target.__rtA, w, h);
- }
- this.setTextureParams(gl);
- }
- result = target.__rtA;
- } else {
- if (target.__rtB === undefined) {
- target.__rtB = this.getRenderBufferTexture(w, h);
- } else {
- if (w != target.__rtB._width || h != target.__rtB._height) {
- this.resizeTexture(target.__rtB, w, h);
- }
- this.setTextureParams(gl);
- }
- result = target.__rtB;
- }
- if (!result) {
- throw "Problems creating render textures, known causes include using too much VRAM by not releasing WebGL texture instances";
- }
- target.__lastRT = result;
- return result;
- };
-
- /**
- * For every image encountered StageGL registers and tracks it automatically. This tracking can cause memory leaks
- * if not purged. StageGL, by default, automatically purges them. This does have a cost and may unfortunately find
- * false positives. This function is for manual management of this memory instead of the automatic system controlled
- * by the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property.
- *
- * This function will recursively remove all textures found on the object, its children, cache, etc. It will uncache
- * objects and remove any texture it finds REGARDLESS of whether it is currently in use elsewhere. It is up to the
- * developer to ensure that a texture in use is not removed.
- *
- * Textures in use, or to be used again shortly, should not be removed. This is simply for performance reasons.
- * Removing a texture in use will cause the texture to have to be re-uploaded slowing rendering.
- * @method releaseTexture
- * @param {DisplayObject | Texture | Image | Canvas} item An object that used the texture to be discarded.
- */
- p.releaseTexture = function (item) {
- var i, l;
- if (!item) { return; }
-
- // this is a container object
- if (item.children) {
- for (i = 0, l = item.children.length; i < l; i++) {
- this.releaseTexture(item.children[i]);
- }
- }
-
- // this has a cache canvas
- if (item.cacheCanvas) {
- item.uncache();
- }
-
- var foundImage = undefined;
- if (item._storeID !== undefined) {
- // this is a texture itself
- if (item === this._textureDictionary[item._storeID]) {
- this._killTextureObject(item);
- item._storeID = undefined;
- return;
- }
-
- // this is an image or canvas
- foundImage = item;
- } else if (item._webGLRenderStyle === 2) {
- // this is a Bitmap class
- foundImage = item.image;
- } else if (item._webGLRenderStyle === 1) {
- // this is a SpriteSheet, we can't tell which image we used from the list easily so remove them all!
- for (i = 0, l = item.spriteSheet._images.length; i < l; i++) {
- this.releaseTexture(item.spriteSheet._images[i]);
- }
- return;
- }
-
- // did we find anything
- if (foundImage === undefined) {
- if (this.vocalDebug) {
- console.log("No associated texture found on release");
- }
- return;
- }
-
- // remove it
- this._killTextureObject(this._textureDictionary[foundImage._storeID]);
- foundImage._storeID = undefined;
- };
-
- /**
- * Similar to {{#crossLink "releaseTexture"}}{{/crossLink}}, but this function differs by searching for textures to
- * release. It works by assuming that it can purge any texture which was last used more than "count" draw calls ago.
- * Because this process is unaware of the objects and whether they may be used on your stage, false positives can
- * occur. It is recommended to manually manage your memory with {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}},
- * however, there are many use cases where this is simpler and error-free. This process is also run by default under
- * the hood to prevent leaks. To disable it see the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property.
- * @method purgeTextures
- * @param {Number} [count=100] How many renders ago the texture was last used
- */
- p.purgeTextures = function (count) {
- if (count == undefined){ count = 100; }
-
- var dict = this._textureDictionary;
- var l = dict.length;
- for (var i= 0; inot update the canvas element's width/height, but
- * the render surface's instead. This is necessary after manually resizing the canvas element on the DOM to avoid a
- * up/down scaled render.
- * @method updateViewport
- * @param {Integer} width The width of the render surface in pixels.
- * @param {Integer} height The height of the render surface in pixels.
- */
- p.updateViewport = function (width, height) {
- this._viewportWidth = width|0;
- this._viewportHeight = height|0;
- var gl = this._webGLContext;
-
- if (gl) {
- gl.viewport(0, 0, this._viewportWidth, this._viewportHeight);
-
- // WebGL works with a -1,1 space on its screen. It also follows Y-Up
- // we need to flip the y, scale and then translate the co-ordinates to match this
- // additionally we offset into they Y so the polygons are inside the camera's "clipping" plane
- this._projectionMatrix = new Float32Array([
- 2 / this._viewportWidth, 0, 0, 0,
- 0, -2 / this._viewportHeight, 1, 0,
- 0, 0, 1, 0,
- -1, 1, 0.1, 0
- ]);
- // create the flipped version for use with render texture flipping
- // DHG: this would be a slice/clone but some platforms don't offer them for Float32Array
- this._projectionMatrixFlip = new Float32Array([0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]);
- this._projectionMatrixFlip.set(this._projectionMatrix);
- this._projectionMatrixFlip[5] *= -1;
- this._projectionMatrixFlip[13] *= -1;
- }
- };
-
- /**
- * Fetches the shader compiled and set up to work with the provided filter/object. The shader is compiled on first
- * use and returned on subsequent calls.
- * @method getFilterShader
- * @param {Filter|Object} filter The object which will provide the information needed to construct the filter shader.
- * @return {WebGLProgram}
- */
- p.getFilterShader = function (filter) {
- if (!filter) { filter = this; }
-
- var gl = this._webGLContext;
- var targetShader = this._activeShader;
-
- if (filter._builtShader) {
- targetShader = filter._builtShader;
- if (filter.shaderParamSetup) {
- gl.useProgram(targetShader);
- filter.shaderParamSetup(gl, this, targetShader);
- }
- } else {
- try {
- targetShader = this._fetchShaderProgram(
- gl, "filter",
- filter.VTX_SHADER_BODY, filter.FRAG_SHADER_BODY,
- filter.shaderParamSetup && filter.shaderParamSetup.bind(filter)
- );
- filter._builtShader = targetShader;
- targetShader._name = filter.toString();
- } catch (e) {
- console && console.log("SHADER SWITCH FAILURE", e);
- }
- }
- return targetShader;
- };
-
- /**
- * Returns a base texture that has no image or data loaded. Not intended for loading images. It may return `null`
- * in some error cases, and trying to use a "null" texture can cause renders to fail.
- * @method getBaseTexture
- * @param {uint} [w=1] The width of the texture in pixels, defaults to 1
- * @param {uint} [h=1] The height of the texture in pixels, defaults to 1
- */
- p.getBaseTexture = function (w, h) {
- var width = Math.ceil(w > 0 ? w : 1) || 1;
- var height = Math.ceil(h > 0 ? h : 1) || 1;
-
- var gl = this._webGLContext;
- var texture = gl.createTexture();
- this.resizeTexture(texture, width, height);
- this.setTextureParams(gl, false);
-
- return texture;
- };
-
- /**
- * Resizes a supplied texture element. May generate invalid textures in some error cases such as; when the texture
- * is too large, when an out of texture memory error occurs, or other error scenarios. Trying to use an invalid texture
- * can cause renders to hard stop on some devices. Check the WebGL bound texture after running this function.
- *
- * NOTE: The supplied texture must have been made with the WebGL "texImage2D" function, all default APIs in StageGL
- * use this, so this note only matters for library developers and plugins.
- *
- * @protected
- * @method resizeTexture
- * @param {WebGLTexture} texture The GL Texture to be modified.
- * @param {uint} [width=1] The width of the texture in pixels, defaults to 1
- * @param {uint} [height=1] The height of the texture in pixels, defaults to 1
- */
- p.resizeTexture = function (texture, width,height) {
- var gl = this._webGLContext;
- gl.bindTexture(gl.TEXTURE_2D, texture);
- gl.texImage2D(
- gl.TEXTURE_2D, // target
- 0, // level of detail
- gl.RGBA, // internal format
- width, height, 0, // width, height, border (only for array/null sourced textures)
- gl.RGBA, // format (match internal format)
- gl.UNSIGNED_BYTE, // type of texture(pixel color depth)
- null // image data, we can do null because we're doing array data
- );
- texture.width = width;
- texture.height = height;
- };
-
- /**
- * Returns a base texture (see {{#crossLink "StageGL/getBaseTexture"}}{{/crossLink}}) for details. Also includes an
- * attached and linked render buffer in `texture._frameBuffer`. RenderTextures can be thought of as an internal
- * canvas on the GPU that can be drawn to.
- * @method getRenderBufferTexture
- * @param {Number} w The width of the texture in pixels.
- * @param {Number} h The height of the texture in pixels.
- * @return {Texture} the basic texture instance with a render buffer property.
- */
- p.getRenderBufferTexture = function (w, h) {
- var gl = this._webGLContext;
-
- // get the texture
- var renderTexture = this.getBaseTexture(w, h);
- if (!renderTexture) { return null; }
-
- // get the frame buffer
- var frameBuffer = gl.createFramebuffer();
- if (!frameBuffer) { return null; }
-
- // set its width and height for spoofing as an image
- renderTexture.width = w;
- renderTexture.height = h;
-
- // attach frame buffer to texture and provide cross links to look up each other
- gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, renderTexture, 0);
- frameBuffer._renderTexture = renderTexture;
- renderTexture._frameBuffer = frameBuffer;
-
- // these keep track of themselves simply to reduce complexity of some lookup code
- renderTexture._storeID = this._textureDictionary.length;
- this._textureDictionary[renderTexture._storeID] = renderTexture;
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- return renderTexture;
- };
-
- /**
- * Common utility function used to apply the correct texture processing parameters for the bound texture.
- * @method setTextureParams
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {Boolean} [isPOT=false] Marks whether the texture is "Power of Two", this may allow better quality AA.
- */
- p.setTextureParams = function (gl, isPOT) {
- if (isPOT && this._antialias) {
- //non POT linear works in some devices, but performance is NOT good, investigate
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
- } else {
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
- }
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
- };
-
- /**
- * Changes the webGL clear, aka "background" color to the provided value. A transparent clear is recommended, as
- * non-transparent colours may create undesired boxes around some visuals.
- *
- * The clear color will also be used for filters and other "render textures". The stage background will ignore the
- * transparency value and display a solid color normally. For the stage to recognize and use transparency it must be
- * created with the transparent flag set to `true` (see {{#crossLink "StageGL/constructor"}}{{/crossLink}})).
- *
- * Using "transparent white" to demonstrate, the valid data formats are as follows:
- *
- * - "#FFF"
- * - "#FFFFFF"
- * - "#FFFFFF00"
- * - "rgba(255,255,255,0.0)"
- *
- * @method setClearColor
- * @param {String|int} [color=0x00000000] The new color to use as the background
- */
- p.setClearColor = function (color) {
- var r, g, b, a, output;
-
- if (typeof color == "string") {
- if (color.indexOf("#") == 0) {
- if (color.length == 4) {
- color = "#" + color.charAt(1)+color.charAt(1) + color.charAt(2)+color.charAt(2) + color.charAt(3)+color.charAt(3)
- }
- r = Number("0x"+color.slice(1, 3))/255;
- g = Number("0x"+color.slice(3, 5))/255;
- b = Number("0x"+color.slice(5, 7))/255;
- a = Number("0x"+color.slice(7, 9))/255;
- } else if (color.indexOf("rgba(") == 0) {
- output = color.slice(5, -1).split(",");
- r = Number(output[0])/255;
- g = Number(output[1])/255;
- b = Number(output[2])/255;
- a = Number(output[3]);
- }
- } else { // >>> is an unsigned shift which is what we want as 0x80000000 and up are negative values
- r = ((color & 0xFF000000) >>> 24)/255;
- g = ((color & 0x00FF0000) >>> 16)/255;
- b = ((color & 0x0000FF00) >>> 8)/255;
- a = (color & 0x000000FF)/255;
- }
-
- this._clearColor.r = r || 0;
- this._clearColor.g = g || 0;
- this._clearColor.b = b || 0;
- this._clearColor.a = a || 0;
-
- if (!this._webGLContext) { return; }
- this._webGLContext.clearColor(this._clearColor.r, this._clearColor.g, this._clearColor.b, this._clearColor.a);
- };
-
- /**
- * docced in subclass
- */
- p.toString = function () {
- return "[StageGL (name="+ this.name +")]";
- };
-
-// private methods:
- /**
- * Sets up and returns the WebGL context for the canvas. May return undefined in error scenarios. These can include
- * situations where the canvas element already has a context, 2D or GL.
- * @param {Canvas} canvas The DOM canvas element to attach to
- * @param {Object} options The options to be handed into the WebGL object, see WebGL spec
- * @method _fetchWebGLContext
- * @protected
- * @return {WebGLRenderingContext} The WebGL context, may return undefined in error scenarios
- */
- p._fetchWebGLContext = function (canvas, options) {
- var gl;
-
- try {
- gl = canvas.getContext("webgl", options) || canvas.getContext("experimental-webgl", options);
- } catch (e) {
- // don't do anything in catch, null check will handle it
- }
-
- if (!gl) {
- var msg = "Could not initialize WebGL";
- console.error?console.error(msg):console.log(msg);
- } else {
- gl.viewportWidth = canvas.width;
- gl.viewportHeight = canvas.height;
- }
-
- return gl;
- };
-
- /**
- * Create the completed Shader Program from the vertex and fragment shaders. Allows building of custom shaders for
- * filters. Once compiled, shaders are saved so. If the Shader code requires dynamic alterations re-run this function
- * to generate a new shader.
- * @method _fetchShaderProgram
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {String} [shaderName="regular"] Working values: "regular", "override", and "filter". Which type of shader to build.
- * Filter and override both accept the custom params. Regular and override have all features. Filter is a special case reduced feature shader meant to be over-ridden.
- * @param {String} [customVTX] Extra vertex shader information to replace a regular draw, see
- * {{#crossLink "StageGL/COVER_VERTEX_BODY"}}{{/crossLink}} for default and {{#crossLink "Filter"}}{{/crossLink}} for examples.
- * @param {String} [customFRAG] Extra fragment shader information to replace a regular draw, see
- * {{#crossLink "StageGL/COVER_FRAGMENT_BODY"}}{{/crossLink}} for default and {{#crossLink "Filter"}}{{/crossLink}} for examples.
- * @param {Function} [shaderParamSetup] Function to run so custom shader parameters can get applied for the render.
- * @protected
- * @return {WebGLProgram} The compiled and linked shader
- */
- p._fetchShaderProgram = function (gl, shaderName, customVTX, customFRAG, shaderParamSetup) {
- gl.useProgram(null); // safety to avoid collisions
-
- // build the correct shader string out of the right headers and bodies
- var targetFrag, targetVtx;
- switch (shaderName) {
- case "filter":
- targetVtx = StageGL.COVER_VERTEX_HEADER + (customVTX || StageGL.COVER_VERTEX_BODY);
- targetFrag = StageGL.COVER_FRAGMENT_HEADER + (customFRAG || StageGL.COVER_FRAGMENT_BODY);
- break;
- case "particle": //TODO
- targetVtx = StageGL.REGULAR_VERTEX_HEADER + StageGL.PARTICLE_VERTEX_BODY;
- targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + StageGL.PARTICLE_FRAGMENT_BODY;
- break;
- case "override":
- targetVtx = StageGL.REGULAR_VERTEX_HEADER + (customVTX || StageGL.REGULAR_VERTEX_BODY);
- targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + (customFRAG || StageGL.REGULAR_FRAGMENT_BODY);
- break;
- case "regular":
- default:
- targetVtx = StageGL.REGULAR_VERTEX_HEADER + StageGL.REGULAR_VERTEX_BODY;
- targetFrag = StageGL.REGULAR_FRAGMENT_HEADER + StageGL.REGULAR_FRAGMENT_BODY;
- break;
- }
-
- // create the separate vars
- var vertexShader = this._createShader(gl, gl.VERTEX_SHADER, targetVtx);
- var fragmentShader = this._createShader(gl, gl.FRAGMENT_SHADER, targetFrag);
-
- // link them together
- var shaderProgram = gl.createProgram();
- gl.attachShader(shaderProgram, vertexShader);
- gl.attachShader(shaderProgram, fragmentShader);
- gl.linkProgram(shaderProgram);
- shaderProgram._type = shaderName;
-
- // check compile status
- if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
- gl.useProgram(this._activeShader);
- throw gl.getProgramInfoLog(shaderProgram);
- }
-
- // set up the parameters on the shader
- gl.useProgram(shaderProgram);
- switch (shaderName) {
- case "filter":
- // get the places in memory the shader is stored so we can feed information into them
- // then save it off on the shader because it's so tied to the shader itself
- shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
- gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
-
- shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
- gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
-
- shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
- gl.uniform1i(shaderProgram.samplerUniform, 0);
-
- shaderProgram.uprightUniform = gl.getUniformLocation(shaderProgram, "uUpright");
- gl.uniform1f(shaderProgram.uprightUniform, 0);
-
- // if there's some custom attributes be sure to hook them up
- if (shaderParamSetup) {
- shaderParamSetup(gl, this, shaderProgram);
- }
- break;
- case "override":
- case "particle":
- case "regular":
- default:
- // get the places in memory the shader is stored so we can feed information into them
- // then save it off on the shader because it's so tied to the shader itself
- shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "vertexPosition");
- gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
-
- shaderProgram.uvPositionAttribute = gl.getAttribLocation(shaderProgram, "uvPosition");
- gl.enableVertexAttribArray(shaderProgram.uvPositionAttribute);
-
- shaderProgram.textureIndexAttribute = gl.getAttribLocation(shaderProgram, "textureIndex");
- gl.enableVertexAttribArray(shaderProgram.textureIndexAttribute);
-
- shaderProgram.alphaAttribute = gl.getAttribLocation(shaderProgram, "objectAlpha");
- gl.enableVertexAttribArray(shaderProgram.alphaAttribute);
-
- var samplers = [];
- for (var i = 0; i < this._batchTextureCount; i++) {
- samplers[i] = i;
- }
-
- shaderProgram.samplerData = samplers;
- shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
- gl.uniform1iv(shaderProgram.samplerUniform, samplers);
-
- shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "pMatrix");
- break;
- }
-
- gl.useProgram(this._activeShader);
- return shaderProgram;
- };
-
- /**
- * Creates a shader from the specified string replacing templates. Template items are defined via `{{` `key` `}}``.
- * @method _createShader
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {Number} type The type of shader to create. gl.VERTEX_SHADER | gl.FRAGMENT_SHADER
- * @param {String} str The definition for the shader.
- * @return {WebGLShader}
- * @protected
- */
- p._createShader = function (gl, type, str) {
- // inject the static number
- str = str.replace(/{{count}}/g, this._batchTextureCount);
-
- // resolve issue with no dynamic samplers by creating correct samplers in if else chain
- // TODO: WebGL 2.0 does not need this support
- var insert = "";
- for (var i = 1; i gl.MAX_TEXTURE_SIZE || image.height > gl.MAX_TEXTURE_SIZE){
- console && console.error("Oversized Texture: "+ image.width+"x"+image.height +" vs "+ gl.MAX_TEXTURE_SIZE +"max");
- }
- }
- };
-
- /**
- * Adds the texture to a spot in the current batch, forcing a draw if no spots are free.
- * @method _insertTextureInBatch
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {WebGLTexture} texture The texture to be inserted.
- * @protected
- */
- p._insertTextureInBatch = function (gl, texture) {
- // if it wasn't used last batch
- if (this._batchTextures[texture._activeIndex] !== texture) {
- // we've got to find it a a spot.
- var found = -1;
- var start = (this._lastTextureInsert+1) % this._batchTextureCount;
- var look = start;
- do {
- if (this._batchTextures[look]._batchID != this._batchID && !this._slotBlacklist[look]) {
- found = look;
- break;
- }
- look = (look+1) % this._batchTextureCount;
- } while (look !== start);
-
- // we couldn't find anywhere for it go, meaning we're maxed out
- if (found === -1) {
- this.batchReason = "textureOverflow";
- this._drawBuffers(gl); // <------------------------------------------------------------------------
- this.batchCardCount = 0;
- found = start;
- }
-
- // lets put it into that spot
- this._batchTextures[found] = texture;
- texture._activeIndex = found;
- var image = texture._imageData;
- if (image && image._invalid && texture._drawID !== undefined) {
- this._updateTextureImageData(gl, image);
- } else {
- gl.activeTexture(gl.TEXTURE0 + found);
- gl.bindTexture(gl.TEXTURE_2D, texture);
- this.setTextureParams(gl);
- }
- this._lastTextureInsert = found;
- } else {
- var image = texture._imageData;
- if (texture._storeID != undefined && image && image._invalid) {
- this._updateTextureImageData(gl, image);
- }
- }
-
- texture._drawID = this._drawID;
- texture._batchID = this._batchID;
- };
-
- /**
- * Remove and clean the texture, expects a texture and is inflexible. Mostly for internal use, recommended to call
- * {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} instead as it will call this with the correct texture object(s).
- * Note: Testing shows this may not happen immediately, have to wait frames for WebGL to have actually adjust memory.
- * @method _killTextureObject
- * @param {Texture} tex The texture to be cleaned out
- * @protected
- */
- p._killTextureObject = function (tex) {
- if (!tex) { return; }
- var gl = this._webGLContext;
-
- // remove linkage
- if (tex._storeID !== undefined && tex._storeID >= 0) {
- this._textureDictionary[tex._storeID] = undefined;
- for (var n in this._textureIDs) {
- if (this._textureIDs[n] == tex._storeID) { delete this._textureIDs[n]; }
- }
- if(tex._imageData) { tex._imageData._storeID = undefined; }
- tex._imageData = tex._storeID = undefined;
- }
-
- // make sure to drop it out of an active slot
- if (tex._activeIndex !== undefined && this._batchTextures[tex._activeIndex] === tex) {
- this._batchTextures[tex._activeIndex] = this._baseTextures[tex._activeIndex];
- }
-
- // remove buffers if present
- try {
- if (tex._frameBuffer) { gl.deleteFramebuffer(tex._frameBuffer); }
- tex._frameBuffer = undefined;
- } catch(e) {
- /* suppress delete errors because it's already gone or didn't need deleting probably */
- if (this.vocalDebug) { console.log(e); }
- }
-
- // remove entry
- try {
- gl.deleteTexture(tex);
- } catch(e) {
- /* suppress delete errors because it's already gone or didn't need deleting probably */
- if (this.vocalDebug) { console.log(e); }
- }
- };
-
- /**
- * Store or restore current batch textures into a backup array
- * @method _backupBatchTextures
- * @param {Boolean} restore Perform a restore instead of a store.
- * @param {Array} [target=this._backupTextures] Where to perform the backup, defaults to internal backup.
- * @protected
- */
- p._backupBatchTextures = function (restore, target) {
- var gl = this._webGLContext;
-
- if (!this._backupTextures) { this._backupTextures = []; }
- if (target === undefined) { target = this._backupTextures; }
-
- for (var i=0; i 0) {
- this._drawBuffers(gl);
- }
- this._isDrawing++;
- this._drawID++;
-
- this.batchCardCount = 0;
- this.depth = 0;
-
- this._appendToBatchGroup(sceneGraph, gl, new createjs.Matrix2D(), this.alpha, ignoreCache);
-
- this.batchReason = "drawFinish";
- this._drawBuffers(gl); // <--------------------------------------------------------
- this._isDrawing--;
- };
-
- /**
- * Perform the drawing process to fill a specific cache texture, including applying filters.
- * @method _cacheDraw
- * @param {DisplayObject} target The object we're drawing into the cache. For example, used for drawing the cache
- * (to prevent it from simply drawing an existing cache back into itself).
- * @param {Array} filters The filters we're drawing into cache.
- * @param {BitmapCache} manager The BitmapCache instance looking after the cache
- * @protected
- */
- p._cacheDraw = function (gl, target, filters, manager) {
- /*
- Implicitly there are 4 modes to this function: filtered-sameContext, filtered-uniqueContext, sameContext, uniqueContext.
- Each situation must be handled slightly differently as 'sameContext' or 'uniqueContext' define how the output works,
- one drawing directly into the main context and the other drawing into a stored renderTexture respectively.
- When the draw is a 'filtered' draw, the filters are applied sequentially and will draw into saved textures repeatedly.
- Once the final filter is done the final output is treated depending upon whether it is a same or unique context.
- The internal complexity comes from reducing over-draw, shared code, and issues like textures needing to be flipped
- sometimes when written to render textures.
- */
- var renderTexture;
- var shaderBackup = this._activeShader;
- var blackListBackup = this._slotBlacklist;
- var lastTextureSlot = this._maxTextureSlots-1;
- var wBackup = this._viewportWidth, hBackup = this._viewportHeight;
-
- // protect the last slot so that we have somewhere to bind the renderTextures so it doesn't get upset
- this.protectTextureSlot(lastTextureSlot, true);
-
- // create offset container for drawing item
- var mtx = target.getMatrix();
- mtx = mtx.clone();
- mtx.scale(1/manager.scale, 1/manager.scale);
- mtx = mtx.invert();
- mtx.translate(-manager.offX/manager.scale*target.scaleX, -manager.offY/manager.scale*target.scaleY);
- var container = this._cacheContainer;
- container.children = [target];
- container.transformMatrix = mtx;
-
- this._backupBatchTextures(false);
-
- if (filters && filters.length) {
- this._drawFilters(target, filters, manager);
- } else {
- // is this for another stage or mine?
- if (this.isCacheControlled) {
- // draw item to canvas I -> C
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._batchDraw(container, gl, true);
- } else {
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- target.cacheCanvas = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
- renderTexture = target.cacheCanvas;
-
- // draw item to render texture I -> T
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
- this.updateViewport(manager._drawWidth, manager._drawHeight);
- this._projectionMatrix = this._projectionMatrixFlip;
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._batchDraw(container, gl, true);
-
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.updateViewport(wBackup, hBackup);
- }
- }
-
- this._backupBatchTextures(true);
-
- this.protectTextureSlot(lastTextureSlot, false);
- this._activeShader = shaderBackup;
- this._slotBlacklist = blackListBackup;
- };
-
- /**
- * Sub portion of _cacheDraw, split off for readability. Do not call independently.
- * @method _drawFilters
- * @param {DisplayObject} target The object we're drawing with a filter.
- * @param {Array} filters The filters we're drawing into cache.
- * @param {BitmapCache} manager The BitmapCache instance looking after the cache
- */
- p._drawFilters = function (target, filters, manager) {
- var gl = this._webGLContext;
- var renderTexture;
- var lastTextureSlot = this._maxTextureSlots-1;
- var wBackup = this._viewportWidth, hBackup = this._viewportHeight;
-
- var container = this._cacheContainer;
- var filterCount = filters.length;
-
- // we don't know which texture slot we're dealing with previously and we need one out of the way
- // once we're using that slot activate it so when we make and bind our RenderTexture it's safe there
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
-
- // draw item to render texture I -> T
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
- this.updateViewport(manager._drawWidth, manager._drawHeight);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._batchDraw(container, gl, true);
-
- // bind the result texture to slot 0 as all filters and cover draws assume original content is in slot 0
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, renderTexture);
- this.setTextureParams(gl);
-
- var flipY = false;
- var i = 0, filter = filters[i];
- do { // this is safe because we wouldn't be in apply filters without a filter count of at least 1
-
- // swap to correct shader
- this._activeShader = this.getFilterShader(filter);
- if (!this._activeShader) { continue; }
-
- // now the old result is stored in slot 0, make a new render texture
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
-
- // draw result to render texture R -> T
- gl.viewport(0, 0, manager._drawWidth, manager._drawHeight);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._drawCover(gl, flipY);
-
- // bind the result texture to slot 0 as all filters and cover draws assume original content is in slot 0
- gl.activeTexture(gl.TEXTURE0);
- gl.bindTexture(gl.TEXTURE_2D, renderTexture);
- this.setTextureParams(gl);
-
- // use flipping to keep things upright, things already cancel out on a single filter
- // this needs to be here as multiPass is not accurate to _this_ frame until after shader acquisition
- if (filterCount > 1 || filters[0]._multiPass) {
- flipY = !flipY;
- }
-
- // work through the multipass if it's there, otherwise move on
- filter = filter._multiPass !== null ? filter._multiPass : filters[++i];
- } while (filter);
-
- // is this for another stage or mine
- if (this.isCacheControlled) {
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.updateViewport(wBackup, hBackup);
-
- // draw result to canvas R -> C
- this._activeShader = this.getFilterShader(this);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._drawCover(gl, flipY);
- } else {
- //TODO: DHG: this is less than ideal. A flipped initial render for this circumstance might help. Adjust the perspective matrix?
- if (flipY) {
- gl.activeTexture(gl.TEXTURE0 + lastTextureSlot);
- renderTexture = this.getTargetRenderTexture(target, manager._drawWidth, manager._drawHeight);
- gl.bindFramebuffer(gl.FRAMEBUFFER, renderTexture._frameBuffer);
-
- this._activeShader = this.getFilterShader(this);
- gl.viewport(0, 0, manager._drawWidth, manager._drawHeight);
- gl.clear(gl.COLOR_BUFFER_BIT);
- this._drawCover(gl, !flipY);
- }
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
- this.updateViewport(wBackup, hBackup);
-
- // make sure the last texture is the active thing to draw
- target.cacheCanvas = renderTexture;
- }
- };
-
- /**
- * Add all the contents of a container to the pending buffers, called recursively on each container. This may
- * trigger a draw if a buffer runs out of space. This is the main workforce of the render loop.
- * @method _appendToBatchGroup
- * @param {Container} container The {{#crossLink "Container"}}{{/crossLink}} that contains everything to be drawn.
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {Matrix2D} concatMtx The effective (concatenated) transformation matrix when beginning this container
- * @param {Number} concatAlpha The effective (concatenated) alpha when beginning this container
- * @param {Boolean} ignoreCache Don't use an element's cache during this draw
- * @protected
- */
- p._appendToBatchGroup = function (container, gl, concatMtx, concatAlpha, ignoreCache) {
- // sort out shared properties
- if (!container._glMtx) { container._glMtx = new createjs.Matrix2D(); }
- var cMtx = container._glMtx;
- cMtx.copy(concatMtx);
- if (container.transformMatrix) {
- cMtx.appendMatrix(container.transformMatrix);
- } else {
- cMtx.appendTransform(
- container.x, container.y,
- container.scaleX, container.scaleY,
- container.rotation, container.skewX, container.skewY,
- container.regX, container.regY
- );
- }
-
- // sub components of figuring out the position an object holds
- var subL, subT, subR, subB;
-
- // actually apply its data to the buffers
- var l = container.children.length;
- for (var i = 0; i < l; i++) {
- var item = container.children[i];
-
- if (!(item.visible && concatAlpha)) { continue; }
- if (!item.cacheCanvas || ignoreCache) {
- if (item._updateState){
- item._updateState();
- }
- if (item.children) {
- this._appendToBatchGroup(item, gl, cMtx, item.alpha * concatAlpha);
- continue;
- }
- }
-
- // check for overflowing batch, if yes then force a render
- // TODO: DHG: consider making this polygon count dependant for things like vector draws
- if (this.batchCardCount+1 > this._maxCardsPerBatch) {
- this.batchReason = "vertexOverflow";
- this._drawBuffers(gl); // <------------------------------------------------------------
- this.batchCardCount = 0;
- }
-
- // keep track of concatenated position
- if (!item._glMtx) { item._glMtx = new createjs.Matrix2D(); }
- var iMtx = item._glMtx;
- iMtx.copy(cMtx);
- if (item.transformMatrix) {
- iMtx.appendMatrix(item.transformMatrix);
- } else {
- iMtx.appendTransform(
- item.x, item.y,
- item.scaleX, item.scaleY,
- item.rotation, item.skewX, item.skewY,
- item.regX, item.regY
- );
- }
-
- var uvRect, texIndex, image, frame, texture, src;
- var useCache = item.cacheCanvas && !ignoreCache;
-
- if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas
- image = (ignoreCache?false:item.cacheCanvas) || item.image;
- } else if (item._webGLRenderStyle === 1) { // SPRITE
- frame = item.spriteSheet.getFrame(item.currentFrame); //TODO: Faster way?
- if (frame === null) { continue; }
- image = frame.image;
- } else { // MISC (DOM objects render themselves later)
- continue;
- }
-
- var uvs = this._uvs;
- var vertices = this._vertices;
- var texI = this._indices;
- var alphas = this._alphas;
-
- // calculate texture
- if (!image) { continue; }
- if (image._storeID === undefined) {
- // this texture is new to us so load it and add it to the batch
- texture = this._loadTextureImage(gl, image);
- this._insertTextureInBatch(gl, texture);
- } else {
- // fetch the texture (render textures know how to look themselves up to simplify this logic)
- texture = this._textureDictionary[image._storeID];
- if (!texture){
- if (this.vocalDebug){ console.log("Texture should not be looked up while not being stored."); }
- continue;
- }
-
- // put it in the batch if needed
- if (texture._batchID !== this._batchID) {
- this._insertTextureInBatch(gl, texture);
- }
- }
- texIndex = texture._activeIndex;
-
- if (item._webGLRenderStyle === 2 || useCache) { // BITMAP / Cached Canvas
- if (!useCache && item.sourceRect) {
- // calculate uvs
- if (!item._uvRect) { item._uvRect = {}; }
- src = item.sourceRect;
- uvRect = item._uvRect;
- uvRect.t = (src.y)/image.height;
- uvRect.l = (src.x)/image.width;
- uvRect.b = (src.y + src.height)/image.height;
- uvRect.r = (src.x + src.width)/image.width;
-
- // calculate vertices
- subL = 0; subT = 0;
- subR = src.width+subL; subB = src.height+subT;
- } else {
- // calculate uvs
- uvRect = StageGL.UV_RECT;
- // calculate vertices
- if (useCache) {
- src = item.bitmapCache;
- subL = src.x+(src._filterOffX/src.scale); subT = src.y+(src._filterOffY/src.scale);
- subR = (src._drawWidth/src.scale)+subL; subB = (src._drawHeight/src.scale)+subT;
- } else {
- subL = 0; subT = 0;
- subR = image.width+subL; subB = image.height+subT;
- }
- }
- } else if (item._webGLRenderStyle === 1) { // SPRITE
- var rect = frame.rect;
-
- // calculate uvs
- uvRect = frame.uvRect;
- if (!uvRect) {
- uvRect = StageGL.buildUVRects(item.spriteSheet, item.currentFrame, false);
- }
-
- // calculate vertices
- subL = -frame.regX; subT = -frame.regY;
- subR = rect.width-frame.regX; subB = rect.height-frame.regY;
- }
-
- // These must be calculated here else a forced draw might happen after they're set
- var offV1 = this.batchCardCount*StageGL.INDICIES_PER_CARD; // offset for 1 component vectors
- var offV2 = offV1*2; // offset for 2 component vectors
-
- //DHG: See Matrix2D.transformPoint for why this math specifically
- // apply vertices
- vertices[offV2] = subL *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+1] = subL *iMtx.b + subT *iMtx.d +iMtx.ty;
- vertices[offV2+2] = subL *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+3] = subL *iMtx.b + subB *iMtx.d +iMtx.ty;
- vertices[offV2+4] = subR *iMtx.a + subT *iMtx.c +iMtx.tx; vertices[offV2+5] = subR *iMtx.b + subT *iMtx.d +iMtx.ty;
- vertices[offV2+6] = vertices[offV2+2]; vertices[offV2+7] = vertices[offV2+3];
- vertices[offV2+8] = vertices[offV2+4]; vertices[offV2+9] = vertices[offV2+5];
- vertices[offV2+10] = subR *iMtx.a + subB *iMtx.c +iMtx.tx; vertices[offV2+11] = subR *iMtx.b + subB *iMtx.d +iMtx.ty;
-
- // apply uvs
- uvs[offV2] = uvRect.l; uvs[offV2+1] = uvRect.t;
- uvs[offV2+2] = uvRect.l; uvs[offV2+3] = uvRect.b;
- uvs[offV2+4] = uvRect.r; uvs[offV2+5] = uvRect.t;
- uvs[offV2+6] = uvRect.l; uvs[offV2+7] = uvRect.b;
- uvs[offV2+8] = uvRect.r; uvs[offV2+9] = uvRect.t;
- uvs[offV2+10] = uvRect.r; uvs[offV2+11] = uvRect.b;
-
- // apply texture
- texI[offV1] = texI[offV1+1] = texI[offV1+2] = texI[offV1+3] = texI[offV1+4] = texI[offV1+5] = texIndex;
-
- // apply alpha
- alphas[offV1] = alphas[offV1+1] = alphas[offV1+2] = alphas[offV1+3] = alphas[offV1+4] = alphas[offV1+5] = item.alpha * concatAlpha;
-
- this.batchCardCount++;
- }
- };
-
- /**
- * Draws all the currently defined cards in the buffer to the render surface.
- * @method _drawBuffers
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @protected
- */
- p._drawBuffers = function (gl) {
- if (this.batchCardCount <= 0) { return; } // prevents error logs on stages filled with un-renederable content.
-
- if (this.vocalDebug) {
- console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ this.batchReason);
- }
- var shaderProgram = this._activeShader;
- var vertexPositionBuffer = this._vertexPositionBuffer;
- var textureIndexBuffer = this._textureIndexBuffer;
- var uvPositionBuffer = this._uvPositionBuffer;
- var alphaBuffer = this._alphaBuffer;
-
- gl.useProgram(shaderProgram);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
- gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._vertices);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, textureIndexBuffer);
- gl.vertexAttribPointer(shaderProgram.textureIndexAttribute, textureIndexBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._indices);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer);
- gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._uvs);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, alphaBuffer);
- gl.vertexAttribPointer(shaderProgram.alphaAttribute, alphaBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, this._alphas);
-
- gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, gl.FALSE, this._projectionMatrix);
-
- for (var i = 0; i < this._batchTextureCount; i++) {
- var texture = this._batchTextures[i];
- gl.activeTexture(gl.TEXTURE0 + i);
- gl.bindTexture(gl.TEXTURE_2D, texture);
- this.setTextureParams(gl, texture.isPOT);
- }
-
- gl.drawArrays(gl.TRIANGLES, 0, this.batchCardCount*StageGL.INDICIES_PER_CARD);
- this._batchID++;
- };
-
- /**
- * Draws a card that covers the entire render surface. Mainly used for filters.
- * @method _drawBuffers
- * @param {WebGLRenderingContext} gl The canvas WebGL context object to draw into.
- * @param {Boolean} flipY Covers are used for things like RenderTextures and because of 3D vs Canvas space this can
- * end up meaning the `y` space sometimes requires flipping in the render.
- * @protected
- */
- p._drawCover = function (gl, flipY) {
- if (this._isDrawing > 0) {
- this._drawBuffers(gl);
- }
-
- if (this.vocalDebug) {
- console.log("Draw["+ this._drawID +":"+ this._batchID +"] : "+ "Cover");
- }
- var shaderProgram = this._activeShader;
- var vertexPositionBuffer = this._vertexPositionBuffer;
- var uvPositionBuffer = this._uvPositionBuffer;
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- gl.useProgram(shaderProgram);
-
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
- gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, StageGL.COVER_VERT);
- gl.bindBuffer(gl.ARRAY_BUFFER, uvPositionBuffer);
- gl.vertexAttribPointer(shaderProgram.uvPositionAttribute, uvPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
- gl.bufferSubData(gl.ARRAY_BUFFER, 0, flipY?StageGL.COVER_UV_FLIP:StageGL.COVER_UV);
-
- gl.uniform1i(shaderProgram.samplerUniform, 0);
- gl.uniform1f(shaderProgram.uprightUniform, flipY?0:1);
-
- gl.drawArrays(gl.TRIANGLES, 0, StageGL.INDICIES_PER_CARD);
- };
-
- createjs.StageGL = createjs.promote(StageGL, "Stage");
-}());
-
-//##############################################################################
-// Bitmap.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
-
- /**
- * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
- * HTML element, or a string.
- *
- * Example
- *
- * var bitmap = new createjs.Bitmap("imagePath.jpg");
- *
- * Notes:
- *
- * - When using a video source that may loop or seek, use a {{#crossLink "VideoBuffer"}}{{/crossLink}} object to
- * prevent blinking / flashing.
- *
- When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
- * will be displayed.
- * - Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
- * the Bitmap can be cached.
- * - Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
- * happens in all browsers except recent Firefox builds.
- * - Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using
- * methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting
- * `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`
- *
- *
- * @class Bitmap
- * @extends DisplayObject
- * @constructor
- * @param {CanvasImageSource | String | Object} imageOrUri The source image to display. This can be a CanvasImageSource
- * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image.
- * If the latter, a new Image instance with the URL as its src will be used.
- **/
- function Bitmap(imageOrUri) {
- this.DisplayObject_constructor();
-
-
- // public properties:
- /**
- * The source image to display. This can be a CanvasImageSource
- * (image, video, canvas), an object with a `getImage` method that returns a CanvasImageSource, or a string URL to an image.
- * If the latter, a new Image instance with the URL as its src will be used.
- * @property image
- * @type CanvasImageSource | Object
- **/
- if (typeof imageOrUri == "string") {
- this.image = document.createElement("img");
- this.image.src = imageOrUri;
- } else {
- this.image = imageOrUri;
- }
-
- /**
- * Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
- * Notes:
- *
- * - that video sources must have a width / height set to work correctly with `sourceRect`
- * - Cached objects will ignore the `sourceRect` property
- *
- * @property sourceRect
- * @type Rectangle
- * @default null
- */
- this.sourceRect = null;
-
- // private properties:
- /**
- * Docced in superclass.
- */
- this._webGLRenderStyle = createjs.DisplayObject._StageGL_BITMAP;
- }
- var p = createjs.extend(Bitmap, createjs.DisplayObject);
-
-
-// public methods:
- /**
- * Constructor alias for backwards compatibility. This method will be removed in future versions.
- * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
- * @method initialize
- * @deprecated in favour of `createjs.promote()`
- **/
- p.initialize = Bitmap; // TODO: deprecated.
-
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- var image = this.image;
- var hasContent = this.cacheCanvas || (image && (image.naturalWidth || image.getContext || image.readyState >= 2));
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
- };
-
- /**
- * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
- * into itself).
- * @return {Boolean}
- **/
- p.draw = function(ctx, ignoreCache) {
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
- var img = this.image, rect = this.sourceRect;
- if (img.getImage) { img = img.getImage(); }
- if (!img) { return true; }
- if (rect) {
- // some browsers choke on out of bound values, so we'll fix them:
- var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, x = 0, y = 0, w = img.width, h = img.height;
- if (x1 < 0) { x -= x1; x1 = 0; }
- if (x2 > w) { x2 = w; }
- if (y1 < 0) { y -= y1; y1 = 0; }
- if (y2 > h) { y2 = h; }
- ctx.drawImage(img, x1, y1, x2-x1, y2-y1, x, y, x2-x1, y2-y1);
- } else {
- ctx.drawImage(img, 0, 0);
- }
- return true;
- };
-
- //Note, the doc sections below document using the specified APIs (from DisplayObject) from
- //Bitmap. This is why they have no method implementations.
-
- /**
- * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
- * You should not cache Bitmap instances as it can degrade performance.
- *
- * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
- * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
- * method.
- * @method cache
- **/
-
- /**
- * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
- * You should not cache Bitmap instances as it can degrade performance.
- *
- * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
- * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
- * method.
- * @method updateCache
- **/
-
- /**
- * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
- * You should not cache Bitmap instances as it can degrade performance.
- *
- * However: If you want to use a filter on a Bitmap, you MUST cache it, or it will not work.
- * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
- * method.
- * @method uncache
- **/
-
- /**
- * Docced in superclass.
- */
- p.getBounds = function() {
- var rect = this.DisplayObject_getBounds();
- if (rect) { return rect; }
- var image = this.image, o = this.sourceRect || image;
- var hasContent = (image && (image.naturalWidth || image.getContext || image.readyState >= 2));
- return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null;
- };
-
- /**
- * Returns a clone of the Bitmap instance.
- * @method clone
- * @param {Boolean} node Whether the underlying dom element should be cloned as well.
- * @return {Bitmap} a clone of the Bitmap instance.
- **/
- p.clone = function(node) {
- var image = this.image;
- if(image && node){ image = image.cloneNode(); }
- var o = new Bitmap(image);
- if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
- this._cloneProps(o);
- return o;
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Bitmap (name="+ this.name +")]";
- };
-
-
- createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject");
-}());
-
-//##############################################################################
-// Sprite.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of
- * images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100
- * images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames,
- * play frames as an animation, and even sequence animations together.
- *
- * See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations.
- *
- * Example
- *
- * var instance = new createjs.Sprite(spriteSheet);
- * instance.gotoAndStop("frameName");
- *
- * Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called,
- * only the first defined frame defined in the sprite sheet will be displayed.
- *
- * @class Sprite
- * @extends DisplayObject
- * @constructor
- * @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame
- * dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
- * @param {String|Number} [frameOrAnimation] The frame number or animation to play initially.
- **/
- function Sprite(spriteSheet, frameOrAnimation) {
- this.DisplayObject_constructor();
-
-
- // public properties:
- /**
- * The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}}
- * definitions, this will advance non-sequentially. This will always be an integer value.
- * @property currentFrame
- * @type {Number}
- * @default 0
- * @readonly
- **/
- this.currentFrame = 0;
-
- /**
- * Returns the name of the currently playing animation.
- * @property currentAnimation
- * @type {String}
- * @final
- * @readonly
- **/
- this.currentAnimation = null;
-
- /**
- * Prevents the animation from advancing each tick automatically. For example, you could create a sprite
- * sheet of icons, set paused to true, and display the appropriate icon by setting currentFrame
.
- * @property paused
- * @type {Boolean}
- * @default false
- **/
- this.paused = true;
-
- /**
- * The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame
- * data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
- * @property spriteSheet
- * @type {SpriteSheet}
- * @readonly
- **/
- this.spriteSheet = spriteSheet;
-
- /**
- * Specifies the current frame index within the currently playing animation. When playing normally, this will increase
- * from 0 to n-1, where n is the number of frames in the current animation.
- *
- * This could be a non-integer value if
- * using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is
- * not an integer.
- * @property currentAnimationFrame
- * @type {Number}
- * @default 0
- **/
- this.currentAnimationFrame = 0;
-
- /**
- * By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related
- * SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
- * framerate.
- *
- * For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will
- * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
- * vary slightly between frames.
- *
- * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
- * passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
- * @property framerate
- * @type {Number}
- * @default 0
- **/
- this.framerate = 0;
-
-
- // private properties:
- /**
- * Current animation object.
- * @property _animation
- * @protected
- * @type {Object}
- * @default null
- **/
- this._animation = null;
-
- /**
- * Current frame index.
- * @property _currentFrame
- * @protected
- * @type {Number}
- * @default null
- **/
- this._currentFrame = null;
-
- /**
- * Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame
- * @property _skipAdvance
- * @protected
- * @type {Boolean}
- * @default false
- **/
- this._skipAdvance = false;
-
- /**
- * Docced in superclass.
- */
- this._webGLRenderStyle = createjs.DisplayObject._StageGL_SPRITE;
-
- if (frameOrAnimation != null) { this.gotoAndPlay(frameOrAnimation); }
- }
- var p = createjs.extend(Sprite, createjs.DisplayObject);
-
- /**
- * Constructor alias for backwards compatibility. This method will be removed in future versions.
- * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
- * @method initialize
- * @deprecated in favour of `createjs.promote()`
- **/
- p.initialize = Sprite; // TODO: Deprecated. This is for backwards support of Flash/Animate spritesheet export.
-
-
-// events:
- /**
- * Dispatched when an animation reaches its ends.
- * @event animationend
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {String} name The name of the animation that just ended.
- * @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping.
- * @since 0.6.0
- */
-
- /**
- * Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick,
- * or calling gotoAndPlay() or gotoAndStop().
- * @event change
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- */
-
-
-// public methods:
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- var hasContent = this.cacheCanvas || this.spriteSheet.complete;
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
- };
-
- /**
- * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
- * into itself).
- **/
- p.draw = function(ctx, ignoreCache) {
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
- this._normalizeFrame();
- var o = this.spriteSheet.getFrame(this._currentFrame|0);
- if (!o) { return false; }
- var rect = o.rect;
- if (rect.width && rect.height) { ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height); }
- return true;
- };
-
- //Note, the doc sections below document using the specified APIs (from DisplayObject) from
- //Bitmap. This is why they have no method implementations.
-
- /**
- * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
- * You should not cache Sprite instances as it can degrade performance.
- * @method cache
- **/
-
- /**
- * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
- * You should not cache Sprite instances as it can degrade performance.
- * @method updateCache
- **/
-
- /**
- * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
- * You should not cache Sprite instances as it can degrade performance.
- * @method uncache
- **/
-
- /**
- * Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}}
- * or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain
- * unchanged.
- * @method play
- **/
- p.play = function() {
- this.paused = false;
- };
-
- /**
- * Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}
- * is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}}
- * will resume playback.
- * @method stop
- **/
- p.stop = function() {
- this.paused = true;
- };
-
- /**
- * Sets paused to false and plays the specified animation name, named frame, or frame number.
- * @method gotoAndPlay
- * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
- * and begin playing.
- **/
- p.gotoAndPlay = function(frameOrAnimation) {
- this.paused = false;
- this._skipAdvance = true;
- this._goto(frameOrAnimation);
- };
-
- /**
- * Sets paused to true and seeks to the specified animation name, named frame, or frame number.
- * @method gotoAndStop
- * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
- * and stop.
- **/
- p.gotoAndStop = function(frameOrAnimation) {
- this.paused = true;
- this._goto(frameOrAnimation);
- };
-
- /**
- * Advances the playhead. This occurs automatically each tick by default.
- * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite
- * or its SpriteSheet.
- * @method advance
- */
- p.advance = function(time) {
- var fps = this.framerate || this.spriteSheet.framerate;
- var t = (fps && time != null) ? time/(1000/fps) : 1;
- this._normalizeFrame(t);
- };
-
- /**
- * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to
- * the origin. For example, a 90 x 70 frame with regX=50
and regY=40
would return a
- * rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object.
- *
- * Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method.
- * @method getBounds
- * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully
- * loaded.
- **/
- p.getBounds = function() {
- // TODO: should this normalizeFrame?
- return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle);
- };
-
- /**
- * Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned
- * instances.
- * @method clone
- * @return {Sprite} a clone of the Sprite instance.
- **/
- p.clone = function() {
- return this._cloneProps(new Sprite(this.spriteSheet));
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Sprite (name="+ this.name +")]";
- };
-
-// private methods:
- /**
- * @method _cloneProps
- * @param {Sprite} o
- * @return {Sprite} o
- * @protected
- **/
- p._cloneProps = function(o) {
- this.DisplayObject__cloneProps(o);
- o.currentFrame = this.currentFrame;
- o.currentAnimation = this.currentAnimation;
- o.paused = this.paused;
- o.currentAnimationFrame = this.currentAnimationFrame;
- o.framerate = this.framerate;
-
- o._animation = this._animation;
- o._currentFrame = this._currentFrame;
- o._skipAdvance = this._skipAdvance;
- return o;
- };
-
- /**
- * Advances the currentFrame
if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}}
- * ticks.
- * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
- * @protected
- * @method _tick
- **/
- p._tick = function(evtObj) {
- if (!this.paused) {
- if (!this._skipAdvance) { this.advance(evtObj&&evtObj.delta); }
- this._skipAdvance = false;
- }
- this.DisplayObject__tick(evtObj);
- };
-
-
- /**
- * Normalizes the current frame, advancing animations and dispatching callbacks as appropriate.
- * @protected
- * @method _normalizeFrame
- **/
- p._normalizeFrame = function(frameDelta) {
- frameDelta = frameDelta || 0;
- var animation = this._animation;
- var paused = this.paused;
- var frame = this._currentFrame;
- var l;
-
- if (animation) {
- var speed = animation.speed || 1;
- var animFrame = this.currentAnimationFrame;
- l = animation.frames.length;
- if (animFrame + frameDelta * speed >= l) {
- var next = animation.next;
- if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) {
- // something changed in the event stack, so we shouldn't make any more changes here.
- return;
- } else if (next) {
- // sequence. Automatically calls _normalizeFrame again with the remaining frames.
- return this._goto(next, frameDelta - (l - animFrame) / speed);
- } else {
- // end.
- this.paused = true;
- animFrame = animation.frames.length - 1;
- }
- } else {
- animFrame += frameDelta * speed;
- }
- this.currentAnimationFrame = animFrame;
- this._currentFrame = animation.frames[animFrame | 0]
- } else {
- frame = (this._currentFrame += frameDelta);
- l = this.spriteSheet.getNumFrames();
- if (frame >= l && l > 0) {
- if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) {
- // looped.
- if ((this._currentFrame -= l) >= l) { return this._normalizeFrame(); }
- }
- }
- }
- frame = this._currentFrame | 0;
- if (this.currentFrame != frame) {
- this.currentFrame = frame;
- this.dispatchEvent("change");
- }
- };
-
- /**
- * Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}},
- * {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.)
- * @property _dispatchAnimationEnd
- * @private
- * @type {Function}
- **/
- p._dispatchAnimationEnd = function(animation, frame, paused, next, end) {
- var name = animation ? animation.name : null;
- if (this.hasEventListener("animationend")) {
- var evt = new createjs.Event("animationend");
- evt.name = name;
- evt.next = next;
- this.dispatchEvent(evt);
- }
- // did the animation get changed in the event stack?:
- var changed = (this._animation != animation || this._currentFrame != frame);
- // if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame:
- if (!changed && !paused && this.paused) { this.currentAnimationFrame = end; changed = true; }
- return changed;
- };
-
- /**
- * Moves the playhead to the specified frame number or animation.
- * @method _goto
- * @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to.
- * @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0.
- * @protected
- **/
- p._goto = function(frameOrAnimation, frame) {
- this.currentAnimationFrame = 0;
- if (isNaN(frameOrAnimation)) {
- var data = this.spriteSheet.getAnimation(frameOrAnimation);
- if (data) {
- this._animation = data;
- this.currentAnimation = frameOrAnimation;
- this._normalizeFrame(frame);
- }
- } else {
- this.currentAnimation = this._animation = null;
- this._currentFrame = frameOrAnimation;
- this._normalizeFrame();
- }
- };
-
-
- createjs.Sprite = createjs.promote(Sprite, "DisplayObject");
-}());
-
-//##############################################################################
-// Shape.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}}
- * instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape
- * instances to display the same vector graphics with different positions or transforms.
- *
- * If the vector art will not
- * change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the
- * rendering cost.
- *
- * Example
- *
- * var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100);
- * var shape = new createjs.Shape(graphics);
- *
- * //Alternatively use can also use the graphics property of the Shape class to renderer the same as above.
- * var shape = new createjs.Shape();
- * shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
- *
- * @class Shape
- * @extends DisplayObject
- * @constructor
- * @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created.
- **/
- function Shape(graphics) {
- this.DisplayObject_constructor();
-
-
- // public properties:
- /**
- * The graphics instance to display.
- * @property graphics
- * @type Graphics
- **/
- this.graphics = graphics ? graphics : new createjs.Graphics();
- }
- var p = createjs.extend(Shape, createjs.DisplayObject);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// public methods:
- /**
- * Returns true or false indicating whether the Shape would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- var hasContent = this.cacheCanvas || (this.graphics && !this.graphics.isEmpty());
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
- };
-
- /**
- * Draws the Shape into the specified context ignoring its visible, alpha, shadow, and transform. Returns true if
- * the draw was handled (useful for overriding functionality).
- *
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
- * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
- * @return {Boolean}
- **/
- p.draw = function(ctx, ignoreCache) {
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
- this.graphics.draw(ctx, this);
- return true;
- };
-
- /**
- * Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to
- * their defaults (for example .parent).
- * @method clone
- * @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be
- * cloned. If false, the Graphics instance will be shared with the new Shape.
- **/
- p.clone = function(recursive) {
- var g = (recursive && this.graphics) ? this.graphics.clone() : this.graphics;
- return this._cloneProps(new Shape(g));
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Shape (name="+ this.name +")]";
- };
-
-
- createjs.Shape = createjs.promote(Shape, "DisplayObject");
-}());
-
-//##############################################################################
-// Text.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the
- * lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML
- * text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
- * method, or using {{#crossLink "DOMElement"}}{{/crossLink}}.
- *
- * Please note that Text does not support HTML text, and can only display one font style at a time. To use
- * multiple font styles, you will need to create multiple text instances, and position them manually.
- *
- * Example
- *
- * var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
- * text.x = 100;
- * text.textBaseline = "alphabetic";
- *
- * CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser
- * before it can be displayed.
- *
- * Note: Text can be expensive to generate, so cache instances where possible. Be aware that not all
- * browsers will render Text exactly the same.
- * @class Text
- * @extends DisplayObject
- * @constructor
- * @param {String} [text] The text to display.
- * @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold
- * 36px Arial").
- * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
- * "#F00", "red", or "#FF0000").
- **/
- function Text(text, font, color) {
- this.DisplayObject_constructor();
-
-
- // public properties:
- /**
- * The text to display.
- * @property text
- * @type String
- **/
- this.text = text;
-
- /**
- * The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial").
- * @property font
- * @type String
- **/
- this.font = font;
-
- /**
- * The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000".
- * It will also accept valid canvas fillStyle values.
- * @property color
- * @type String
- **/
- this.color = color;
-
- /**
- * The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed
- * information view the
- *
- * whatwg spec. Default is "left".
- * @property textAlign
- * @type String
- **/
- this.textAlign = "left";
-
- /**
- * The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or
- * "bottom". For detailed information view the
- * whatwg spec. Default is "top".
- * @property textBaseline
- * @type String
- */
- this.textBaseline = "top";
-
- /**
- * The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or
- * shrunk to make it fit in this width. For detailed information view the
- *
- * whatwg spec.
- * @property maxWidth
- * @type Number
- */
- this.maxWidth = null;
-
- /**
- * If greater than 0, the text will be drawn as a stroke (outline) of the specified width.
- * @property outline
- * @type Number
- **/
- this.outline = 0;
-
- /**
- * Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0,
- * the value of getMeasuredLineHeight is used.
- * @property lineHeight
- * @type Number
- **/
- this.lineHeight = 0;
-
- /**
- * Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null,
- * the text will not be wrapped.
- * @property lineWidth
- * @type Number
- **/
- this.lineWidth = null;
- }
- var p = createjs.extend(Text, createjs.DisplayObject);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// static properties:
- /**
- * @property _workingContext
- * @type CanvasRenderingContext2D
- * @private
- **/
- var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
- if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; }
-
-
-// constants:
- /**
- * Lookup table for the ratio to offset bounds x calculations based on the textAlign property.
- * @property H_OFFSETS
- * @type Object
- * @protected
- * @static
- **/
- Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1};
-
- /**
- * Lookup table for the ratio to offset bounds y calculations based on the textBaseline property.
- * @property H_OFFSETS
- * @type Object
- * @protected
- * @static
- **/
- Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1};
-
-
-// public methods:
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Whether the display object would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- var hasContent = this.cacheCanvas || (this.text != null && this.text !== "");
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
- };
-
- /**
- * Draws the Text into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
- * into itself).
- **/
- p.draw = function(ctx, ignoreCache) {
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
-
- var col = this.color || "#000";
- if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; }
- else { ctx.fillStyle = col; }
-
- this._drawText(this._prepContext(ctx));
- return true;
- };
-
- /**
- * Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value.
- * @method getMeasuredWidth
- * @return {Number} The measured, untransformed width of the text.
- **/
- p.getMeasuredWidth = function() {
- return this._getMeasuredWidth(this.text);
- };
-
- /**
- * Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured
- * width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts.
- * @method getMeasuredLineHeight
- * @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is
- * based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts.
- **/
- p.getMeasuredLineHeight = function() {
- return this._getMeasuredWidth("M")*1.2;
- };
-
- /**
- * Returns the approximate height of multi-line text by multiplying the number of lines against either the
- * lineHeight
(if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
- * this operation requires the text flowing logic to run, which has an associated CPU cost.
- * @method getMeasuredHeight
- * @return {Number} The approximate height of the untransformed multi-line text.
- **/
- p.getMeasuredHeight = function() {
- return this._drawText(null,{}).height;
- };
-
- /**
- * Docced in superclass.
- */
- p.getBounds = function() {
- var rect = this.DisplayObject_getBounds();
- if (rect) { return rect; }
- if (this.text == null || this.text === "") { return null; }
- var o = this._drawText(null, {});
- var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width;
- var x = w * Text.H_OFFSETS[this.textAlign||"left"];
- var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
- var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
- return this._rectangle.setValues(x, y, w, o.height);
- };
-
- /**
- * Returns an object with width, height, and lines properties. The width and height are the visual width and height
- * of the drawn text. The lines property contains an array of strings, one for
- * each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing
- * whitespace removed.
- * @method getMetrics
- * @return {Object} An object with width, height, and lines properties.
- **/
- p.getMetrics = function() {
- var o = {lines:[]};
- o.lineHeight = this.lineHeight || this.getMeasuredLineHeight();
- o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
- return this._drawText(null, o, o.lines);
- };
-
- /**
- * Returns a clone of the Text instance.
- * @method clone
- * @return {Text} a clone of the Text instance.
- **/
- p.clone = function() {
- return this._cloneProps(new Text(this.text, this.font, this.color));
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Text (text="+ (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
- };
-
-
-// private methods:
- /**
- * @method _cloneProps
- * @param {Text} o
- * @protected
- * @return {Text} o
- **/
- p._cloneProps = function(o) {
- this.DisplayObject__cloneProps(o);
- o.textAlign = this.textAlign;
- o.textBaseline = this.textBaseline;
- o.maxWidth = this.maxWidth;
- o.outline = this.outline;
- o.lineHeight = this.lineHeight;
- o.lineWidth = this.lineWidth;
- return o;
- };
-
- /**
- * @method _getWorkingContext
- * @param {CanvasRenderingContext2D} ctx
- * @return {CanvasRenderingContext2D}
- * @protected
- **/
- p._prepContext = function(ctx) {
- ctx.font = this.font||"10px sans-serif";
- ctx.textAlign = this.textAlign||"left";
- ctx.textBaseline = this.textBaseline||"top";
- ctx.lineJoin = "miter";
- ctx.miterLimit = 2.5;
- return ctx;
- };
-
- /**
- * Draws multiline text.
- * @method _drawText
- * @param {CanvasRenderingContext2D} ctx
- * @param {Object} o
- * @param {Array} lines
- * @return {Object}
- * @protected
- **/
- p._drawText = function(ctx, o, lines) {
- var paint = !!ctx;
- if (!paint) {
- ctx = Text._workingContext;
- ctx.save();
- this._prepContext(ctx);
- }
- var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
-
- var maxW = 0, count = 0;
- var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);
- for (var i=0, l=hardLines.length; i this.lineWidth) {
- // text wrapping:
- var words = str.split(/(\s)/);
- str = words[0];
- w = ctx.measureText(str).width;
-
- for (var j=1, jl=words.length; j this.lineWidth) {
- if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
- if (lines) { lines.push(str); }
- if (w > maxW) { maxW = w; }
- str = words[j+1];
- w = ctx.measureText(str).width;
- count++;
- } else {
- str += words[j] + words[j+1];
- w += wordW;
- }
- }
- }
-
- if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
- if (lines) { lines.push(str); }
- if (o && w == null) { w = ctx.measureText(str).width; }
- if (w > maxW) { maxW = w; }
- count++;
- }
-
- if (o) {
- o.width = maxW;
- o.height = count*lineHeight;
- }
- if (!paint) { ctx.restore(); }
- return o;
- };
-
- /**
- * @method _drawTextLine
- * @param {CanvasRenderingContext2D} ctx
- * @param {String} text
- * @param {Number} y
- * @protected
- **/
- p._drawTextLine = function(ctx, text, y) {
- // Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
- if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); }
- else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); }
- };
-
-
- /**
- * @method _getMeasuredWidth
- * @param {String} text
- * @protected
- **/
- p._getMeasuredWidth = function(text) {
- var ctx = Text._workingContext;
- ctx.save();
- var w = this._prepContext(ctx).measureText(text).width;
- ctx.restore();
- return w;
- };
-
-
- createjs.Text = createjs.promote(Text, "DisplayObject");
-}());
-
-//##############################################################################
-// BitmapText.js
-//##############################################################################
-
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
-
-// constructor:
- /**
- * Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported using new line characters,
- * but automatic wrapping is not supported. See the {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}}
- * property for more information on defining glyphs.
- *
- * Important: While BitmapText extends Container, it is not designed to be used as one.
- * As such, methods like addChild and removeChild are disabled.
- *
- *
- * @class BitmapText
- * @extends DisplayObject
- * @param {String} [text=""] The text to display.
- * @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs.
- * @constructor
- **/
- function BitmapText(text, spriteSheet) {
- this.Container_constructor();
-
-
- // public properties:
- /**
- * The text to display.
- * @property text
- * @type String
- * @default ""
- **/
- this.text = text||"";
-
- /**
- * A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character
- * should have a single frame animation defined in the sprite sheet named the same as
- * corresponding character. For example, the following animation definition:
- *
- * "A": {frames: [0]}
- *
- * would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form
- * is also acceptable:
- *
- * "A": 0
- *
- * Note that if a character in the text is not found in the sprite sheet, it will also
- * try to use the alternate case (upper or lower).
- *
- * See SpriteSheet for more information on defining sprite sheet data.
- * @property spriteSheet
- * @type SpriteSheet
- * @default null
- **/
- this.spriteSheet = spriteSheet;
-
- /**
- * The height of each line of text. If 0, then it will use a line height calculated
- * by checking for the height of the "1", "T", or "L" character (in that order). If
- * those characters are not defined, it will use the height of the first frame of the
- * sprite sheet.
- * @property lineHeight
- * @type Number
- * @default 0
- **/
- this.lineHeight = 0;
-
- /**
- * This spacing (in pixels) will be added after each character in the output.
- * @property letterSpacing
- * @type Number
- * @default 0
- **/
- this.letterSpacing = 0;
-
- /**
- * If a space character is not defined in the sprite sheet, then empty pixels equal to
- * spaceWidth will be inserted instead. If 0, then it will use a value calculated
- * by checking for the width of the "1", "l", "E", or "A" character (in that order). If
- * those characters are not defined, it will use the width of the first frame of the
- * sprite sheet.
- * @property spaceWidth
- * @type Number
- * @default 0
- **/
- this.spaceWidth = 0;
-
-
- // private properties:
- /**
- * @property _oldProps
- * @type Object
- * @protected
- **/
- this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0};
-
- /**
- * Used to track the object which this class attached listeners to, helps optimize listener attachment.
- * @property _oldStage
- * @type Stage
- * @protected
- */
- this._oldStage = null;
- /**
- * The event listener proxy triggered drawing draw for special circumstances.
- * @property _drawAction
- * @type function
- * @protected
- */
- this._drawAction = null;
- }
- var p = createjs.extend(BitmapText, createjs.Container);
-
-// static properties:
- /**
- * BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains
- * an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be
- * retained, slightly increasing memory use, but reducing instantiation.
- * @property maxPoolSize
- * @type Number
- * @static
- * @default 100
- **/
- BitmapText.maxPoolSize = 100;
-
- /**
- * Sprite object pool.
- * @type {Array}
- * @static
- * @private
- */
- BitmapText._spritePool = [];
-
-
-// public methods:
- /**
- * Docced in superclass.
- **/
- p.draw = function(ctx, ignoreCache) {
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return; }
- this._updateState();
- this.Container_draw(ctx, ignoreCache);
- };
-
- /**
- * Docced in superclass.
- **/
- p.getBounds = function() {
- this._updateText();
- return this.Container_getBounds();
- };
-
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- var hasContent = this.cacheCanvas || (this.spriteSheet && this.spriteSheet.complete && this.text);
- return !!(this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0 && hasContent);
- };
-
- p.clone = function() {
- return this._cloneProps(new BitmapText(this.text, this.spriteSheet));
- };
-
- /**
- * Disabled in BitmapText.
- * @method addChild
- **/
- /**
- * Disabled in BitmapText.
- * @method addChildAt
- **/
- /**
- * Disabled in BitmapText.
- * @method removeChild
- **/
- /**
- * Disabled in BitmapText.
- * @method removeChildAt
- **/
- /**
- * Disabled in BitmapText.
- * @method removeAllChildren
- **/
- p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function() {};
-
-
-// private methods:
- /**
- * Docced in superclass.
- **/
- p._updateState = function() {
- this._updateText();
- };
-
- /**
- * @method _cloneProps
- * @param {BitmapText} o
- * @return {BitmapText} o
- * @protected
- **/
- p._cloneProps = function(o) {
- this.Container__cloneProps(o);
- o.lineHeight = this.lineHeight;
- o.letterSpacing = this.letterSpacing;
- o.spaceWidth = this.spaceWidth;
- return o;
- };
-
- /**
- * @method _getFrameIndex
- * @param {String} character
- * @param {SpriteSheet} spriteSheet
- * @return {Number}
- * @protected
- **/
- p._getFrameIndex = function(character, spriteSheet) {
- var c, o = spriteSheet.getAnimation(character);
- if (!o) {
- (character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null);
- if (c) { o = spriteSheet.getAnimation(c); }
- }
- return o && o.frames[0];
- };
-
- /**
- * @method _getFrame
- * @param {String} character
- * @param {SpriteSheet} spriteSheet
- * @return {Object}
- * @protected
- **/
- p._getFrame = function(character, spriteSheet) {
- var index = this._getFrameIndex(character, spriteSheet);
- return index == null ? index : spriteSheet.getFrame(index);
- };
-
- /**
- * @method _getLineHeight
- * @param {SpriteSheet} ss
- * @return {Number}
- * @protected
- **/
- p._getLineHeight = function(ss) {
- var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0);
- return frame ? frame.rect.height : 1;
- };
-
- /**
- * @method _getSpaceWidth
- * @param {SpriteSheet} ss
- * @return {Number}
- * @protected
- **/
- p._getSpaceWidth = function(ss) {
- var frame = this._getFrame("1",ss) || this._getFrame("l",ss) || this._getFrame("e",ss) || this._getFrame("a",ss) || ss.getFrame(0);
- return frame ? frame.rect.width : 1;
- };
-
- /**
- * @method _updateText
- * @protected
- **/
- p._updateText = function() {
- var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet;
- var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite;
-
- for (var n in o) {
- if (o[n] != this[n]) {
- o[n] = this[n];
- change = true;
- }
- }
- if (!change) { return; }
-
- var hasSpace = !!this._getFrame(" ", ss);
- if (!hasSpace && !spaceW) { spaceW = this._getSpaceWidth(ss); }
- if (!lineH) { lineH = this._getLineHeight(ss); }
-
- for(var i=0, l=this.text.length; i childIndex) {
- // faster than removeChild.
- pool.push(sprite = kids.pop());
- sprite.parent = null;
- numKids--;
- }
- if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; }
- };
-
-
- createjs.BitmapText = createjs.promote(BitmapText, "Container");
-}());
-
-//##############################################################################
-// MovieClip.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * The MovieClip class associates a TweenJS Timeline with an EaselJS {{#crossLink "Container"}}{{/crossLink}}. It allows
- * you to create objects which encapsulate timeline animations, state changes, and synched actions. The MovieClip
- * class has been included in the EaselJS minified file since 0.7.0.
- *
- * Currently MovieClip only works properly if it is tick based (as opposed to time based) though some concessions have
- * been made to support time-based timelines in the future.
- *
- * Example
- * This example animates two shapes back and forth. The grey shape starts on the left, but we jump to a mid-point in
- * the animation using {{#crossLink "MovieClip/gotoAndPlay"}}{{/crossLink}}.
- *
- * var stage = new createjs.Stage("canvas");
- * createjs.Ticker.addEventListener("tick", stage);
- *
- * var mc = new createjs.MovieClip({loop:-1, labels:{myLabel:20}});
- * stage.addChild(mc);
- *
- * var child1 = new createjs.Shape(
- * new createjs.Graphics().beginFill("#999999")
- * .drawCircle(30,30,30));
- * var child2 = new createjs.Shape(
- * new createjs.Graphics().beginFill("#5a9cfb")
- * .drawCircle(30,30,30));
- *
- * mc.timeline.addTween(
- * createjs.Tween.get(child1)
- * .to({x:0}).to({x:60}, 50).to({x:0}, 50));
- * mc.timeline.addTween(
- * createjs.Tween.get(child2)
- * .to({x:60}).to({x:0}, 50).to({x:60}, 50));
- *
- * mc.gotoAndPlay("start");
- *
- * It is recommended to use tween.to()
to animate and set properties (use no duration to have it set
- * immediately), and the tween.wait()
method to create delays between animations. Note that using the
- * tween.set()
method to affect properties will likely not provide the desired result.
- *
- * @class MovieClip
- * @main MovieClip
- * @param {Object} [props] The configuration properties to apply to this instance (ex. `{mode:MovieClip.SYNCHED}`).
- * Supported props for the MovieClip are listed below. These props are set on the corresponding instance properties except where
- * specified.
- * - `mode`
- * - `startPosition`
- * - `frameBounds`
- *
- *
- * This object will also be passed into the Timeline instance associated with this MovieClip. See the documentation
- * for Timeline for a list of supported props (ex. `paused`, `labels`, `loop`, `reversed`, etc.)
- * @extends Container
- * @constructor
- **/
- function MovieClip(props) {
- this.Container_constructor();
- !MovieClip.inited&&MovieClip.init(); // static init
-
- var mode, startPosition, loop, labels;
-
- // handle old params (mode, startPosition, loop, labels):
- // TODO: deprecated param handling:
- if (props instanceof String || arguments.length > 1) {
- mode = props;
- startPosition = arguments[1];
- loop = arguments[2];
- labels = arguments[3];
- if (loop == null) { loop = -1; }
- props = null;
- } else if (props) {
- mode = props.mode;
- startPosition = props.startPosition;
- loop = props.loop;
- labels = props.labels;
- }
- if (!props) { props = {labels:labels}; }
-
-
- // public properties:
- /**
- * Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED).
- * See each constant for a description of the behaviour.
- * @property mode
- * @type String
- * @default null
- **/
- this.mode = mode||MovieClip.INDEPENDENT;
-
- /**
- * Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME.
- * @property startPosition
- * @type Number
- * @default 0
- */
- this.startPosition = startPosition||0;
-
- /**
- * Specifies how many times this MovieClip should loop. A value of -1 indicates it should loop indefinitely. A value of
- * 1 would cause it to loop once (ie. play a total of twice).
- * @property loop
- * @type Number
- * @default -1
- */
- this.loop = loop === true ? -1 : (loop || 0);
-
- /**
- * The current frame of the movieclip.
- * @property currentFrame
- * @type Number
- * @default 0
- * @readonly
- */
- this.currentFrame = 0;
-
- /**
- * If true, the MovieClip's position will not advance when ticked.
- * @property paused
- * @type Boolean
- * @default false
- */
- this.paused = props.paused||false;
-
- /**
- * If true, actions in this MovieClip's tweens will be run when the playhead advances.
- * @property actionsEnabled
- * @type Boolean
- * @default true
- */
- this.actionsEnabled = true;
-
- /**
- * If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds
- * it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT.
- *
- * For example, if you had a character animation with a "body" child MovieClip instance
- * with different costumes on each frame, you could set body.autoReset = false, so that
- * you can manually change the frame it is on, without worrying that it will be reset
- * automatically.
- * @property autoReset
- * @type Boolean
- * @default true
- */
- this.autoReset = true;
-
- /**
- * An array of bounds for each frame in the MovieClip. This is mainly intended for tool output.
- * @property frameBounds
- * @type Array
- * @default null
- */
- this.frameBounds = this.frameBounds||props.frameBounds; // frameBounds are set on the prototype in Animate.
-
- /**
- * By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip
- * will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
- * framerate.
- *
- * For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will
- * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
- * vary slightly between frames.
- *
- * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
- * passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
- * @property framerate
- * @type {Number}
- * @default null
- **/
- this.framerate = null;
-
- // set up the needed props for Timeline:
- props.useTicks = props.paused = true;
-
- /**
- * The TweenJS Timeline that is associated with this MovieClip. This is created automatically when the MovieClip
- * instance is initialized. Animations are created by adding TweenJS Tween
- * instances to the timeline.
- *
- * Example
- *
- * var tween = createjs.Tween.get(target).to({x:0}).to({x:100}, 30);
- * var mc = new createjs.MovieClip();
- * mc.timeline.addTween(tween);
- *
- * Elements can be added and removed from the timeline by toggling an "_off" property
- * using the tweenInstance.to()
method. Note that using Tween.set
is not recommended to
- * create MovieClip animations. The following example will toggle the target off on frame 0, and then back on for
- * frame 1. You can use the "visible" property to achieve the same effect.
- *
- * var tween = createjs.Tween.get(target).to({_off:false})
- * .wait(1).to({_off:true})
- * .wait(1).to({_off:false});
- *
- * @property timeline
- * @type Timeline
- * @default null
- */
- this.timeline = new createjs.Timeline(props);
-
-
- // private properties:
- /**
- * @property _synchOffset
- * @type Number
- * @default 0
- * @private
- */
- this._synchOffset = 0;
-
- /**
- * @property _rawPosition
- * @type Number
- * @default -1
- * @private
- */
- this._rawPosition = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1.
-
- /**
- * @property _bound_resolveState
- * @type Function
- * @private
- */
- this._bound_resolveState = this._resolveState.bind(this);
-
-
- /**
- * The time remaining from the previous tick, only applicable when .framerate is set.
- * @property _t
- * @type Number
- * @private
- */
- this._t = 0;
-
- /**
- * List of display objects that are actively being managed by the MovieClip.
- * @property _managed
- * @type Object
- * @private
- */
- this._managed = {};
- }
- var p = createjs.extend(MovieClip, createjs.Container);
-
-
-// constants:
- /**
- * The MovieClip will advance independently of its parent, even if its parent is paused.
- * This is the default mode.
- * @property INDEPENDENT
- * @static
- * @type String
- * @default "independent"
- * @readonly
- **/
- MovieClip.INDEPENDENT = "independent";
-
- /**
- * The MovieClip will only display a single frame (as determined by the startPosition property).
- * @property SINGLE_FRAME
- * @static
- * @type String
- * @default "single"
- * @readonly
- **/
- MovieClip.SINGLE_FRAME = "single";
-
- /**
- * The MovieClip will be advanced only when its parent advances and will be synched to the position of
- * the parent MovieClip.
- * @property SYNCHED
- * @static
- * @type String
- * @default "synched"
- * @readonly
- **/
- MovieClip.SYNCHED = "synched";
-
-
-// static properties:
- MovieClip.inited = false;
-
-
-// static methods:
- MovieClip.init = function() {
- if (MovieClip.inited) { return; }
- // plugins introduce some overhead to Tween, so we only install this if an MC is instantiated.
- MovieClipPlugin.install();
- MovieClip.inited = true;
- };
-
-
-// getter / setters:
- /**
- * Use the {{#crossLink "MovieClip/labels:property"}}{{/crossLink}} property instead.
- * @method _getLabels
- * @protected
- * @return {Array}
- **/
- p._getLabels = function() {
- return this.timeline.getLabels();
- };
- // MovieClip.getLabels is @deprecated. Remove for 1.1+
- p.getLabels = createjs.deprecate(p._getLabels, "MovieClip.getLabels");
-
- /**
- * Use the {{#crossLink "MovieClip/currentLabel:property"}}{{/crossLink}} property instead.
- * @method _getCurrentLabel
- * @protected
- * @return {String}
- **/
- p._getCurrentLabel = function() {
- return this.timeline.currentLabel;
- };
- // MovieClip.getCurrentLabel is @deprecated. Remove for 1.1+
- p.getCurrentLabel = createjs.deprecate(p._getCurrentLabel, "MovieClip.getCurrentLabel");
-
- /**
- * Use the {{#crossLink "MovieClip/duration:property"}}{{/crossLink}} property instead.
- * @method _getDuration
- * @protected
- * @return {Number}
- **/
- p._getDuration = function() {
- return this.timeline.duration;
- };
- // MovieClip.getDuration is @deprecated. Remove for 1.1+
- p.getDuration = createjs.deprecate(p._getDuration, "MovieClip.getDuration");
-
- /**
- * Returns an array of objects with label and position (aka frame) properties, sorted by position.
- * @property labels
- * @type {Array}
- * @readonly
- **/
-
- /**
- * Returns the name of the label on or immediately before the current frame.
- * @property currentLabel
- * @type {String}
- * @readonly
- **/
-
- /**
- * Returns the duration of this MovieClip in seconds or ticks.
- * @property totalFrames
- * @type {Number}
- * @readonly
- **/
-
- /**
- * Returns the duration of this MovieClip in seconds or ticks.
- * @property duration
- * @type {Number}
- * @readonly
- **/
- try {
- Object.defineProperties(p, {
- labels: { get: p._getLabels },
- currentLabel: { get: p._getCurrentLabel },
- totalFrames: { get: p._getDuration },
- duration: { get: p._getDuration }
- // TODO: can we just proxy .currentFrame to tl.position as well? Ditto for .loop (or just remove entirely).
- });
- } catch (e) {}
-
-
-// public methods:
- /**
- * Constructor alias for backwards compatibility. This method will be removed in future versions.
- * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
- * @method initialize
- * @deprecated in favour of `createjs.promote()`
- **/
- p.initialize = MovieClip; // TODO: Deprecated. This is for backwards support of Adobe Flash/Animate
-
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- **/
- p.isVisible = function() {
- // children are placed in draw, so we can't determine if we have content.
- return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
- };
-
- /**
- * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
- * into itself).
- **/
- p.draw = function(ctx, ignoreCache) {
- // draw to cache first:
- if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
- this._updateState();
- this.Container_draw(ctx, ignoreCache);
- return true;
- };
-
- /**
- * Sets paused to false.
- * @method play
- **/
- p.play = function() {
- this.paused = false;
- };
-
- /**
- * Sets paused to true.
- * @method stop
- **/
- p.stop = function() {
- this.paused = true;
- };
-
- /**
- * Advances this movie clip to the specified position or label and sets paused to false.
- * @method gotoAndPlay
- * @param {String|Number} positionOrLabel The animation name or frame number to go to.
- **/
- p.gotoAndPlay = function(positionOrLabel) {
- this.paused = false;
- this._goto(positionOrLabel);
- };
-
- /**
- * Advances this movie clip to the specified position or label and sets paused to true.
- * @method gotoAndStop
- * @param {String|Number} positionOrLabel The animation or frame name to go to.
- **/
- p.gotoAndStop = function(positionOrLabel) {
- this.paused = true;
- this._goto(positionOrLabel);
- };
-
- /**
- * Advances the playhead. This occurs automatically each tick by default.
- * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set.
- * @method advance
- */
- p.advance = function(time) {
- var independent = MovieClip.INDEPENDENT;
- if (this.mode !== independent) { return; } // update happens in draw for synched clips
-
- // if this MC doesn't have a framerate, hunt ancestors for one:
- var o=this, fps = o.framerate;
- while ((o = o.parent) && fps === null) { if (o.mode === independent) { fps = o._framerate; } }
- this._framerate = fps;
-
- if (this.paused) { return; }
-
- // calculate how many frames to advance:
- var t = (fps !== null && fps !== -1 && time !== null) ? time/(1000/fps) + this._t : 1;
- var frames = t|0;
- this._t = t-frames; // leftover time, save to add to next advance.
-
- while (frames--) { this._updateTimeline(this._rawPosition+1, false); }
- };
-
- /**
- * MovieClip instances cannot be cloned.
- * @method clone
- **/
- p.clone = function() {
- // TODO: add support for this? Need to clone the Timeline & retarget tweens - pretty complex.
- throw("MovieClip cannot be cloned.");
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[MovieClip (name="+ this.name +")]";
- };
-
-
-// private methods:
- /**
- * Docced in superclass.
- **/
- p._updateState = function() {
- if (this._rawPosition === -1 || this.mode !== MovieClip.INDEPENDENT) { this._updateTimeline(-1); }
- };
-
- /**
- * @method _tick
- * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
- * function.
- * @protected
- **/
- p._tick = function(evtObj) {
- this.advance(evtObj&&evtObj.delta);
- this.Container__tick(evtObj);
- };
-
- /**
- * @method _goto
- * @param {String|Number} positionOrLabel The animation name or frame number to go to.
- * @protected
- **/
- p._goto = function(positionOrLabel) {
- var pos = this.timeline.resolve(positionOrLabel);
- if (pos == null) { return; }
- this._t = 0;
- this._updateTimeline(pos, true);
- };
-
- /**
- * @method _reset
- * @private
- **/
- p._reset = function() {
- this._rawPosition = -1;
- this._t = this.currentFrame = 0;
- this.paused = false;
- };
-
- /**
- * @method _updateTimeline
- * @param {Boolean} jump Indicates whether this update is due to jumping (via gotoAndXX) to a new position.
- * @protected
- **/
- p._updateTimeline = function(rawPosition, jump) {
- var synced = this.mode !== MovieClip.INDEPENDENT, tl = this.timeline;
- if (synced) { rawPosition = this.startPosition + (this.mode===MovieClip.SINGLE_FRAME?0:this._synchOffset); }
- if (rawPosition < 0) { rawPosition = 0; }
- if (this._rawPosition === rawPosition && !synced) { return; }
- this._rawPosition = rawPosition;
-
- // update timeline position, ignoring actions if this is a graphic.
- tl.loop = this.loop; // TODO: should we maintain this on MovieClip, or just have it on timeline?
- tl.setPosition(rawPosition, synced || !this.actionsEnabled, jump, this._bound_resolveState);
- };
-
- /**
- * Renders position 0 without running actions or updating _rawPosition.
- * Primarily used by Animate CC to build out the first frame in the constructor of MC symbols.
- * NOTE: not tested when run after the MC advances past the first frame.
- * @method _renderFirstFrame
- * @protected
- **/
- p._renderFirstFrame = function() {
- var tl = this.timeline, pos = tl.rawPosition;
- tl.setPosition(0, true, true, this._bound_resolveState);
- tl.rawPosition = pos;
- };
-
- /**
- * Runs via a callback after timeline property updates and before actions.
- * @method _resolveState
- * @protected
- **/
- p._resolveState = function() {
- var tl = this.timeline;
- this.currentFrame = tl.position;
-
- for (var n in this._managed) { this._managed[n] = 1; }
-
- var tweens = tl.tweens;
- for (var i=0, l=tweens.length; i=0; i--) {
- var id = kids[i].id;
- if (this._managed[id] === 1) {
- this.removeChildAt(i);
- delete(this._managed[id]);
- }
- }
- };
-
- /**
- * @method _setState
- * @param {Array} state
- * @param {Number} offset
- * @protected
- **/
- p._setState = function(state, offset) {
- if (!state) { return; }
- for (var i=state.length-1;i>=0;i--) {
- var o = state[i];
- var target = o.t;
- var props = o.p;
- for (var n in props) { target[n] = props[n]; }
- this._addManagedChild(target, offset);
- }
- };
-
- /**
- * Adds a child to the timeline, and sets it up as a managed child.
- * @method _addManagedChild
- * @param {MovieClip} child The child MovieClip to manage
- * @param {Number} offset
- * @private
- **/
- p._addManagedChild = function(child, offset) {
- if (child._off) { return; }
- this.addChildAt(child,0);
-
- if (child instanceof MovieClip) {
- child._synchOffset = offset;
- // TODO: this does not precisely match Adobe Flash/Animate, which loses track of the clip if it is renamed or removed from the timeline, which causes it to reset.
- // TODO: should also reset when MovieClip loops, though that will be a bit tricky to detect.
- if (child.mode === MovieClip.INDEPENDENT && child.autoReset && (!this._managed[child.id])) { child._reset(); }
- }
- this._managed[child.id] = 2;
- };
-
- /**
- * @method _getBounds
- * @param {Matrix2D} matrix
- * @param {Boolean} ignoreTransform
- * @return {Rectangle}
- * @protected
- **/
- p._getBounds = function(matrix, ignoreTransform) {
- var bounds = this.DisplayObject_getBounds();
- if (!bounds) {
- if (this.frameBounds) { bounds = this._rectangle.copy(this.frameBounds[this.currentFrame]); }
- }
- if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
- return this.Container__getBounds(matrix, ignoreTransform);
- };
-
-
- createjs.MovieClip = createjs.promote(MovieClip, "Container");
-
-
-
-// MovieClipPlugin for TweenJS:
- /**
- * This plugin works with TweenJS to prevent the startPosition
- * property from tweening.
- * @private
- * @class MovieClipPlugin
- * @constructor
- **/
- function MovieClipPlugin() {
- throw("MovieClipPlugin cannot be instantiated.")
- }
-
- /**
- * @property priority
- * @type {Number}
- * @static
- * @readonly
- **/
- MovieClipPlugin.priority = 100; // very high priority, should run first
-
- /**
- * @property ID
- * @type {String}
- * @static
- * @readonly
- **/
- MovieClipPlugin.ID = "MovieClip";
-
- /**
- * @method install
- * @static
- **/
- MovieClipPlugin.install = function() {
- createjs.Tween._installPlugin(MovieClipPlugin);
- };
-
- /**
- * @method init
- * @param {Tween} tween
- * @param {String} prop
- * @param {*} value
- * @static
- **/
- MovieClipPlugin.init = function(tween, prop, value) {
- if (prop === "startPosition" && tween.target instanceof MovieClip) { tween._addPlugin(MovieClipPlugin); }
- };
-
- /**
- * @method step
- * @param {Tween} tween
- * @param {TweenStep} step
- * @param {Object} props
- * @static
- **/
- MovieClipPlugin.step = function(tween, step, props) {};
-
- /**
- * @method change
- * @param {Tween} tween
- * @param {TweenStep} step
- * @param {*} value
- * @param {Number} ratio
- * @param {Object} end
- * @return {*}
- * @static
- */
- MovieClipPlugin.change = function(tween, step, prop, value, ratio, end) {
- if (prop === "startPosition") { return (ratio === 1 ? step.props[prop] : step.prev.props[prop]); }
- };
-
-}());
-
-//##############################################################################
-// SpriteSheetUtils.js
-//##############################################################################
-
+ createjs.ColorMatrix = ColorMatrix;
+}());
+
+//##############################################################################
+// ColorMatrixFilter.js
+//##############################################################################
+
this.createjs = this.createjs||{};
(function() {
"use strict";
-
-
+
+
// constructor:
/**
- * The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s.
- * A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For
- * example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across
- * by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated.
- * @class SpriteSheetUtils
- * @static
+ * Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the
+ * {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform,
+ * consider the {{#crossLink "ColorFilter"}}{{/crossLink}}.
+ *
+ * Example
+ * This example creates a red circle, inverts its hue, and then saturates it to brighten it up.
+ *
+ * var shape = new createjs.Shape().set({x:100,y:100});
+ * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
+ *
+ * var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100);
+ * shape.filters = [
+ * new createjs.ColorMatrixFilter(matrix)
+ * ];
+ *
+ * shape.cache(-50, -50, 100, 100);
+ *
+ * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
+ * @class ColorMatrixFilter
+ * @constructor
+ * @extends Filter
+ * @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
+ * class.
**/
- function SpriteSheetUtils() {
- throw "SpriteSheetUtils cannot be instantiated";
+ function ColorMatrixFilter(matrix) {
+ this.Filter_constructor();
+
+ // public properties:
+ /**
+ * A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
+ * @property matrix
+ * @type Array | ColorMatrix
+ **/
+ this.matrix = matrix;
+
+ this.FRAG_SHADER_BODY = (
+ "uniform mat4 uColorMatrix;" +
+ "uniform vec4 uColorMatrixOffset;" +
+
+ "void main(void) {" +
+ "vec4 color = texture2D(uSampler, vRenderCoord);" +
+
+ "mat4 m = uColorMatrix;" +
+ "vec4 newColor = vec4(0,0,0,0);" +
+ "newColor.r = color.r*m[0][0] + color.g*m[0][1] + color.b*m[0][2] + color.a*m[0][3];" +
+ "newColor.g = color.r*m[1][0] + color.g*m[1][1] + color.b*m[1][2] + color.a*m[1][3];" +
+ "newColor.b = color.r*m[2][0] + color.g*m[2][1] + color.b*m[2][2] + color.a*m[2][3];" +
+ "newColor.a = color.r*m[3][0] + color.g*m[3][1] + color.b*m[3][2] + color.a*m[3][3];" +
+
+ "gl_FragColor = newColor + uColorMatrixOffset;" +
+ "}"
+ );
}
+ var p = createjs.extend(ColorMatrixFilter, createjs.Filter);
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-// private static properties:
+ /** docced in super class **/
+ p.shaderParamSetup = function(gl, stage, shaderProgram) {
+ var mat = this.matrix;
+ var colorMatrix = new Float32Array([
+ mat[0],mat[1],mat[2],mat[3],
+ mat[5],mat[6],mat[7],mat[8],
+ mat[10],mat[11],mat[12],mat[13],
+ mat[15],mat[16],mat[17],mat[18]
+ ]);
+
+ gl.uniformMatrix4fv(
+ gl.getUniformLocation(shaderProgram, "uColorMatrix"),
+ false, colorMatrix
+ );
+ gl.uniform4f(
+ gl.getUniformLocation(shaderProgram, "uColorMatrixOffset"),
+ mat[4]/255, mat[9]/255, mat[14]/255, mat[19]/255
+ );
+ };
+
+// public methods:
+ /** docced in super class **/
+ p.toString = function() {
+ return "[ColorMatrixFilter]";
+ };
+
+ /** docced in super class **/
+ p.clone = function() {
+ return new ColorMatrixFilter(this.matrix);
+ };
+
+// private methods:
+ /** docced in super class **/
+ p._applyFilter = function(imageData) {
+ var data = imageData.data;
+ var l = data.length;
+ var r,g,b,a;
+ var mtx = this.matrix;
+ var m0 = mtx[0], m1 = mtx[1], m2 = mtx[2], m3 = mtx[3], m4 = mtx[4];
+ var m5 = mtx[5], m6 = mtx[6], m7 = mtx[7], m8 = mtx[8], m9 = mtx[9];
+ var m10 = mtx[10], m11 = mtx[11], m12 = mtx[12], m13 = mtx[13], m14 = mtx[14];
+ var m15 = mtx[15], m16 = mtx[16], m17 = mtx[17], m18 = mtx[18], m19 = mtx[19];
+
+ for (var i=0; iExample
+ *
+ * var stage = new createjs.Stage("canvasId");
+ * createjs.Touch.enable(stage);
+ *
+ * Note: It is important to disable Touch on a stage that you are no longer using:
+ *
+ * createjs.Touch.disable(stage);
+ *
+ * @class Touch
+ * @static
+ **/
+ function Touch() {
+ throw "Touch cannot be instantiated";
+ }
+
+
+// public static methods:
+ /**
+ * Returns `true` if touch is supported in the current browser.
+ * @method isSupported
+ * @return {Boolean} Indicates whether touch is supported in the current browser.
* @static
- * @type HTMLCanvasElement | Object
- * @protected
- */
+ **/
+ Touch.isSupported = function() {
+ return !!(('ontouchstart' in window) // iOS & Android
+ || window.TouchEvent
+ || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10
+ || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+
+ };
+
/**
- * @property _workingContext
+ * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
+ * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
+ * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
+ * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
+ * for more information.
+ * @method enable
+ * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
+ * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
+ * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
+ * allowed when the user is interacting with the target canvas.
+ * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
* @static
- * @type CanvasRenderingContext2D
+ **/
+ Touch.enable = function(stage, singleTouch, allowDefault) {
+ if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }
+ if (stage.__touch) { return true; }
+
+ // inject required properties on stage:
+ stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};
+
+ // note that in the future we may need to disable the standard mouse event model before adding
+ // these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
+ if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
+ else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); }
+ return true;
+ };
+
+ /**
+ * Removes all listeners that were set up when calling `Touch.enable()` on a stage.
+ * @method disable
+ * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on.
+ * @static
+ **/
+ Touch.disable = function(stage) {
+ if (!stage||!stage.__touch) { return; }
+ if ('ontouchstart' in window) { Touch._IOS_disable(stage); }
+ else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); }
+
+ delete stage.__touch;
+ };
+
+
+// Private static methods:
+ /**
+ * @method _IOS_enable
* @protected
- */
- var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
- if (canvas.getContext) {
- SpriteSheetUtils._workingCanvas = canvas;
- SpriteSheetUtils._workingContext = canvas.getContext("2d");
- canvas.width = canvas.height = 1;
- }
+ * @param {Stage} stage
+ * @static
+ **/
+ Touch._IOS_enable = function(stage) {
+ var canvas = stage.canvas;
+ var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); };
+ canvas.addEventListener("touchstart", f, false);
+ canvas.addEventListener("touchmove", f, false);
+ canvas.addEventListener("touchend", f, false);
+ canvas.addEventListener("touchcancel", f, false);
+ };
+ /**
+ * @method _IOS_disable
+ * @protected
+ * @param {Stage} stage
+ * @static
+ **/
+ Touch._IOS_disable = function(stage) {
+ var canvas = stage.canvas;
+ if (!canvas) { return; }
+ var f = stage.__touch.f;
+ canvas.removeEventListener("touchstart", f, false);
+ canvas.removeEventListener("touchmove", f, false);
+ canvas.removeEventListener("touchend", f, false);
+ canvas.removeEventListener("touchcancel", f, false);
+ };
-// public static methods:
/**
- * Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is
- * to use a spritesheet frame as the source for a bitmap fill.
- *
- * WARNING: In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}}
- * with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this
- * method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}}
- * property of {{#crossLink "Bitmap"}}{{/crossLink}}.
- *
- * The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas.
- * @method extractFrame
+ * @method _IOS_handleEvent
+ * @param {Stage} stage
+ * @param {Object} e The event to handle
+ * @protected
* @static
- * @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from.
- * @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation
- * name is specified, only the first frame of the animation will be extracted.
- * @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image.
- */
- SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) {
- if (isNaN(frameOrAnimation)) {
- frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0];
+ **/
+ Touch._IOS_handleEvent = function(stage, e) {
+ if (!stage) { return; }
+ if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
+ var touches = e.changedTouches;
+ var type = e.type;
+ for (var i= 0,l=touches.length; i this.maxHeight) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
- var y=0, x=0;
- var img = 0;
- while (frames.length) {
- var o = this._fillRow(frames, y, img, dataFrames, pad);
- if (o.w > x) { x = o.w; }
- y += o.h;
- if (!o.h || !frames.length) {
- var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
- canvas.width = this._getSize(x,this.maxWidth);
- canvas.height = this._getSize(y,this.maxHeight);
- this._data.images[img] = canvas;
- if (!o.h) {
- x=y=0;
- img++;
- }
- }
- }
- };
-
- /**
- * @method _setupMovieClipFrame
- * @protected
- * @return {Number} The width & height of the row.
- **/
- p._setupMovieClipFrame = function(source, data) {
- var ae = source.actionsEnabled;
- source.actionsEnabled = false;
- source.gotoAndStop(data.i);
- source.actionsEnabled = ae;
- data.f&&data.f(source, data.d, data.i);
- };
-
- /**
- * @method _getSize
- * @protected
- * @return {Number} The width & height of the row.
- **/
- p._getSize = function(size,max) {
- var pow = 4;
- while (Math.pow(2,++pow) < size){}
- return Math.min(max,Math.pow(2,pow));
- };
-
- /**
- * @method _fillRow
- * @param {Array} frames
- * @param {Number} y
- * @param {HTMLImageElement} img
- * @param {Object} dataFrames
- * @param {Number} pad
- * @protected
- * @return {Number} The width & height of the row.
- **/
- p._fillRow = function(frames, y, img, dataFrames, pad) {
- var w = this.maxWidth;
- var maxH = this.maxHeight;
- y += pad;
- var h = maxH-y;
- var x = pad;
- var height = 0;
- for (var i=frames.length-1; i>=0; i--) {
- var frame = frames[i];
- var sc = this._scale*frame.scale;
- var rect = frame.sourceRect;
- var source = frame.source;
- var rx = Math.floor(sc*rect.x-pad);
- var ry = Math.floor(sc*rect.y-pad);
- var rh = Math.ceil(sc*rect.height+pad*2);
- var rw = Math.ceil(sc*rect.width+pad*2);
- if (rw > w) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
- if (rh > h || x+rw > w) { continue; }
- frame.img = img;
- frame.rect = new createjs.Rectangle(x,y,rw,rh);
- height = height || rh;
- frames.splice(i,1);
- dataFrames[frame.index] = [x,y,rw,rh,img,Math.round(-rx+sc*source.regX-pad),Math.round(-ry+sc*source.regY-pad)];
- x += rw;
- }
- return {w:x, h:height};
- };
-
- /**
- * @method _endBuild
- * @protected
- **/
- p._endBuild = function() {
- this.spriteSheet = new createjs.SpriteSheet(this._data);
- this._data = null;
- this.progress = 1;
- this.dispatchEvent("complete");
- };
-
- /**
- * @method _run
- * @protected
- **/
- p._run = function() {
- var ts = Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50;
- var t = (new Date()).getTime()+ts;
- var complete = false;
- while (t > (new Date()).getTime()) {
- if (!this._drawNext()) { complete = true; break; }
- }
- if (complete) {
- this._endBuild();
- } else {
- var _this = this;
- this._timerID = setTimeout(function() { _this._run(); }, 50-ts);
- }
- var p = this.progress = this._index/this._frames.length;
- if (this.hasEventListener("progress")) {
- var evt = new createjs.Event("progress");
- evt.progress = p;
- this.dispatchEvent(evt);
- }
- };
-
- /**
- * @method _drawNext
- * @protected
- * @return Boolean Returns false if this is the last draw.
- **/
- p._drawNext = function() {
- var frame = this._frames[this._index];
- var sc = frame.scale*this._scale;
- var rect = frame.rect;
- var sourceRect = frame.sourceRect;
- var canvas = this._data.images[frame.img];
- var ctx = canvas.getContext("2d");
- frame.funct&&frame.funct(frame.source, frame.data);
- ctx.save();
- ctx.beginPath();
- ctx.rect(rect.x, rect.y, rect.width, rect.height);
- ctx.clip();
- ctx.translate(Math.ceil(rect.x-sourceRect.x*sc), Math.ceil(rect.y-sourceRect.y*sc));
- ctx.scale(sc,sc);
- frame.source.draw(ctx); // display object will draw itself.
- ctx.restore();
- return (++this._index) < this._frames.length;
- };
-
-
- createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher");
-}());
-
-//##############################################################################
-// DOMElement.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * This class is still experimental, and more advanced use is likely to be buggy. Please report bugs.
- *
- * A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed
- * within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is
- * not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be
- * drawn in front of or behind the canvas).
- *
- * The position of a DOMElement is relative to their parent node in the DOM. It is recommended that
- * the DOM Object be added to a div that also contains the canvas so that they share the same position
- * on the page.
- *
- * DOMElement is useful for positioning HTML elements over top of canvas content, and for elements
- * that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML
- * content.
- *
- * Mouse Interaction
- *
- * DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse
- * events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to
- * the htmlElement (note, this does not support EventDispatcher)
- *
- * var domElement = new createjs.DOMElement(htmlElement);
- * domElement.htmlElement.onclick = function() {
- * console.log("clicked");
- * }
- *
- * Important: This class needs to be notified it is about to be drawn, this will happen automatically
- * if you call stage.update, calling stage.draw or disabling tickEnabled will miss important steps and it will render
- * stale information.
- *
- * @class DOMElement
- * @extends DisplayObject
- * @constructor
- * @param {HTMLElement} htmlElement A reference or id for the DOM element to manage.
- */
- function DOMElement(htmlElement) {
- this.DisplayObject_constructor();
-
- if (typeof(htmlElement)=="string") { htmlElement = document.getElementById(htmlElement); }
- this.mouseEnabled = false;
-
- var style = htmlElement.style;
- style.position = "absolute";
- style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%";
-
-
- // public properties:
- /**
- * The DOM object to manage.
- * @property htmlElement
- * @type HTMLElement
- */
- this.htmlElement = htmlElement;
-
-
- // private properties:
- /**
- * @property _oldMtx
- * @type Matrix2D
- * @protected
- */
- this._oldProps = null;
-
- /**
- * Used to track the object which this class attached listeners to, helps optimize listener attachment.
- * @property _oldStage
- * @type Stage
- * @protected
- */
- this._oldStage = null;
- /**
- * The event listener proxy triggered drawing draw for special circumstances.
- * @property _drawAction
- * @type function
- * @protected
- */
- this._drawAction = null;
- }
- var p = createjs.extend(DOMElement, createjs.DisplayObject);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// public methods:
- /**
- * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
- * This does not account for whether it would be visible within the boundaries of the stage.
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method isVisible
- * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
- */
- p.isVisible = function() {
- return this.htmlElement != null;
- };
-
- /**
- * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
- * Returns true if the draw was handled (useful for overriding functionality).
- * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
- * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
- * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
- * into itself).
- * @return {Boolean}
- */
- p.draw = function(ctx, ignoreCache) {
- // this relies on the _tick method because draw isn't called if the parent is not visible.
- // the actual update happens in _handleDrawEnd
- return true;
- };
-
- /**
- * Not applicable to DOMElement.
- * @method cache
- */
- p.cache = function() {};
-
- /**
- * Not applicable to DOMElement.
- * @method uncache
- */
- p.uncache = function() {};
-
- /**
- * Not applicable to DOMElement.
- * @method updateCache
- */
- p.updateCache = function() {};
-
- /**
- * Not applicable to DOMElement.
- * @method hitTest
- */
- p.hitTest = function() {};
-
- /**
- * Not applicable to DOMElement.
- * @method localToGlobal
- */
- p.localToGlobal = function() {};
-
- /**
- * Not applicable to DOMElement.
- * @method globalToLocal
- */
- p.globalToLocal = function() {};
-
- /**
- * Not applicable to DOMElement.
- * @method localToLocal
- */
- p.localToLocal = function() {};
-
- /**
- * DOMElement cannot be cloned. Throws an error.
- * @method clone
- */
- p.clone = function() {
- throw("DOMElement cannot be cloned.")
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- */
- p.toString = function() {
- return "[DOMElement (name="+ this.name +")]";
- };
-
- /**
- * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
- * are not full EaselJS display objects and do not participate in EaselJS mouse events.
- * @event click
- */
-
- /**
- * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
- * are not full EaselJS display objects and do not participate in EaselJS mouse events.
- * @event dblClick
- */
-
- /**
- * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
- * are not full EaselJS display objects and do not participate in EaselJS mouse events.
- * @event mousedown
- */
-
- /**
- * The HTMLElement can listen for the mouseover event, not the DOMElement instance.
- * Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events.
- * @event mouseover
- */
-
- /**
- * Not applicable to DOMElement.
- * @event tick
- */
-
-
-// private methods:
- /**
- * @method _tick
- * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
- * function.
- * @protected
- */
- p._tick = function(evtObj) {
- var stage = this.stage;
- if(stage && stage !== this._oldStage) {
- this._drawAction && stage.off("drawend", this._drawAction);
- this._drawAction = stage.on("drawend", this._handleDrawEnd, this);
- this._oldStage = stage;
- }
- this.DisplayObject__tick(evtObj);
- };
-
- /**
- * @method _handleDrawEnd
- * @param {Event} evt
- * @protected
- */
- p._handleDrawEnd = function(evt) {
- var o = this.htmlElement;
- if (!o) { return; }
- var style = o.style;
-
- var props = this.getConcatenatedDisplayProps(this._props), mtx = props.matrix;
-
- var visibility = props.visible ? "visible" : "hidden";
- if (visibility != style.visibility) { style.visibility = visibility; }
- if (!props.visible) { return; }
-
- var oldProps = this._oldProps, oldMtx = oldProps&&oldProps.matrix;
- var n = 10000; // precision
-
- if (!oldMtx || !oldMtx.equals(mtx)) {
- var str = "matrix(" + (mtx.a*n|0)/n +","+ (mtx.b*n|0)/n +","+ (mtx.c*n|0)/n +","+ (mtx.d*n|0)/n +","+ (mtx.tx+0.5|0);
- style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str +","+ (mtx.ty+0.5|0) +")";
- style.MozTransform = str +"px,"+ (mtx.ty+0.5|0) +"px)";
- if (!oldProps) { oldProps = this._oldProps = new createjs.DisplayProps(true, null); }
- oldProps.matrix.copy(mtx);
- }
-
- if (oldProps.alpha != props.alpha) {
- style.opacity = ""+(props.alpha*n|0)/n;
- oldProps.alpha = props.alpha;
- }
- };
-
-
- createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject");
-}());
-
-//##############################################################################
-// Filter.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Base class that all filters should inherit from. Filters need to be applied to objects that have been cached using
- * the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method. If an object changes, please cache it again, or use
- * {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
- *
- * Example
- *
- * myInstance.filters = [
- * new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0),
- * new createjs.BlurFilter(5, 5, 10)
- * ];
- * myInstance.cache(0,0, 100, 100);
- *
- * Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the
- * margins that need to be applied in order to fully display the filter. For example, the {{#crossLink "BlurFilter"}}{{/crossLink}}
- * will cause an object to feather outwards, resulting in a margin around the shape.
- *
- * EaselJS Filters
- * EaselJS comes with a number of pre-built filters:
- * - {{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object
- * - {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object
- * - {{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object
- * - {{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object
- * - {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}
- *
- *
- * @class Filter
- * @constructor
- **/
- function Filter() {
- /**
- * A flag stating that this filter uses a context draw mode and cannot be batched into imageData processing.
- * @property usesContext
- * @type {boolean}
- * @default false
- */
- this.usesContext = false;
-
- /**
- * Another filter that is required to act as part of this filter and created and managed under the hood.
- * @private
- * @property _multiPass
- * @type {Filter}
- * @default null
- */
- this._multiPass = null;
-
- /**
- * Pre-processed template shader code. It will be parsed before being fed in into the shader compiler.
- * This should be based upon StageGL.SHADER_VERTEX_BODY_REGULAR
- * @property VTX_SHADER
- * @virtual
- * @type {String}
- * @readonly
- */
- this.VTX_SHADER_BODY = null;
-
- /**
- * Pre-processed template shader code. It will be parsed before being fed in into the shader compiler.
- * This should be based upon StageGL.SHADER_FRAGMENT_BODY_REGULAR
- * @property FRAG_SHADER
- * @virtual
- * @type {String}
- * @readonly
- */
- this.FRAG_SHADER_BODY = null;
- }
- var p = Filter.prototype;
-
-// public methods:
- /**
- * Provides padding values for this filter. That is, how much the filter will extend the visual bounds of an object it is applied to.
- * @method getBounds
- * @param {Rectangle} [rect] If specified, the provided Rectangle instance will be expanded by the padding amounts and returned.
- * @return {Rectangle} If a `rect` param was provided, it is returned. If not, either a new rectangle with the padding values, or null if no padding is required for this filter.
- **/
- p.getBounds = function(rect) {
- return rect;
- };
-
- /**
- * Assign any unique uniforms or other setup functionality here.
- * @method shaderParamSetup
- * @virtual
- * @param {WebGLContext} gl The context associated with the stage performing the render.
- * @param {StageGL} stage The stage instance that will be rendering.
- * @param {ShaderProgram} shaderProgram The compiled shader that is going to be used to perform the render.
- */
- p.shaderParamSetup = function(gl, stage, shaderProgram) {};
-
- /**
- * Applies the filter to the specified context.
- * @method applyFilter
- * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
- * @param {Number} x The x position to use for the source rect.
- * @param {Number} y The y position to use for the source rect.
- * @param {Number} width The width to use for the source rect.
- * @param {Number} height The height to use for the source rect.
- * @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
- * @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
- * @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
- * @return {Boolean} If the filter was applied successfully.
- **/
- p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) {
- // this is the default behaviour because most filters access pixel data. It is overridden when not needed.
- targetCtx = targetCtx || ctx;
- if (targetX == null) { targetX = x; }
- if (targetY == null) { targetY = y; }
- try {
- var imageData = ctx.getImageData(x, y, width, height);
- } catch (e) {
- return false;
- }
- if (this._applyFilter(imageData)) {
- targetCtx.putImageData(imageData, targetX, targetY);
- return true;
- }
- return false;
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Filter]";
- };
-
- /**
- * Returns a clone of this Filter instance.
- * @method clone
- * @return {Filter} A clone of the current Filter instance.
- **/
- p.clone = function() {
- return new Filter();
- };
-
-// private methods:
- /**
- * @method _applyFilter
- * @param {ImageData} imageData Target ImageData instance.
- * @return {Boolean}
- **/
- p._applyFilter = function(imageData) { return true; };
-
-
- createjs.Filter = Filter;
-}());
-
-//##############################################################################
-// BitmapCache.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * The BitmapCache is an internal representation of all the cache properties and logic required in order to "cache"
- * an object. This information and functionality used to be located on a {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
- * method in {{#crossLink "DisplayObject"}}{{/crossLink}}, but was moved to its own class.
- *
- * Caching in this context is purely visual, and will render the DisplayObject out into an image to be used instead
- * of the object. The actual cache itself is still stored on the target with the {{#crossLink "DisplayObject/cacheCanvas:property"}}{{/crossLink}}.
- * Working with a singular image like a {{#crossLink "Bitmap"}}{{/crossLink}} there is little benefit to performing
- * a cache as it is already a single image. Caching is best done on containers containing multiple complex parts that
- * do not move often, so that rendering the image instead will improve overall rendering speed. A cached object will
- * not visually update until explicitly told to do so with a call to update, much like a Stage. If a cache is being
- * updated every frame it is likely not improving rendering performance. Cache are best used when updates will be sparse.
- *
- * Caching is also a co-requisite for applying filters to prevent expensive filters running constantly without need,
- * and to physically enable some effects. The BitmapCache is also responsible for applying filters to objects and
- * reads each {{#crossLink "Filter"}}{{/crossLink}} due to this relationship. Real-time Filters are not recommended
- * performance wise when dealing with a Context2D canvas. For best performance and to still allow for some visual
- * effects use a compositeOperation when possible.
- * @class BitmapCache
- * @constructor
- **/
- function BitmapCache() {
-
- // public:
- /**
- * Width of the cache relative to the target object.
- * @property width
- * @protected
- * @type {Number}
- * @default undefined
- **/
- this.width = undefined;
-
- /**
- * Height of the cache relative to the target object.
- * @property height
- * @protected
- * @type {Number}
- * @default undefined
- * @todo Should the width and height be protected?
- **/
- this.height = undefined;
-
- /**
- * Horizontal position of the cache relative to the target's origin.
- * @property x
- * @protected
- * @type {Number}
- * @default undefined
- **/
- this.x = undefined;
-
- /**
- * Vertical position of the cache relative to target's origin.
- * @property y
- * @protected
- * @type {Number}
- * @default undefined
- **/
- this.y = undefined;
-
- /**
- * The internal scale of the cache image, does not affects display size. This is useful to both increase and
- * decrease render quality. Objects with increased scales are more likely to look good when scaled up or rotated.
- * Objects with decreased scales can save on rendering performance.
- * @property scale
- * @protected
- * @type {Number}
- * @default 1
- **/
- this.scale = 1;
-
- /**
- * The x offset used for drawing into the cache itself, accounts for both transforms applied.
- * @property offX
- * @protected
- * @type {Number}
- * @default 0
- **/
- this.offX = 0;
-
- /**
- * The y offset used for drawing into the cache itself, accounts for both transforms applied.
- * @property offY
- * @protected
- * @type {Number}
- * @default 0
- **/
- this.offY = 0;
-
- /**
- * Track how many times the cache has been updated, mostly used for preventing duplicate cacheURLs.
- * This can be useful to see if a cache has been updated.
- * @property cacheID
- * @type {Number}
- * @default 0
- **/
- this.cacheID = 0;
-
- // protected:
- /**
- * The relative offset of the filter's x position, used for drawing the cache onto its container.
- * Re-calculated every update call before drawing.
- * @property _filterOffY
- * @protected
- * @type {Number}
- * @default 0
- **/
- this._filterOffX = 0;
-
- /**
- * The relative offset of the filter's y position, used for drawing the cache onto its container.
- * Re-calculated every update call before drawing.
- * @property _filterOffY
- * @protected
- * @type {Number}
- * @default 0
- **/
- this._filterOffY = 0;
-
- /**
- * The cacheID when a DataURL was requested.
- * @property _cacheDataURLID
- * @protected
- * @type {Number}
- * @default 0
- **/
- this._cacheDataURLID = 0;
-
- /**
- * The cache's DataURL, generated on-demand using the getter.
- * @property _cacheDataURL
- * @protected
- * @type {String}
- * @default null
- **/
- this._cacheDataURL = null;
-
- /**
- * Internal tracking of final bounding width, approximately width*scale; however, filters can complicate the actual value.
- * @property _drawWidth
- * @protected
- * @type {Number}
- * @default 0
- **/
- this._drawWidth = 0;
-
- /**
- * Internal tracking of final bounding height, approximately height*scale; however, filters can complicate the actual value.
- * @property _drawHeight
- * @protected
- * @type {Number}
- * @default 0
- **/
- this._drawHeight = 0;
- }
- var p = BitmapCache.prototype;
-
- /**
- * Returns the bounds that surround all applied filters, relies on each filter to describe how it changes bounds.
- * @method getFilterBounds
- * @param {DisplayObject} target The object to check the filter bounds for.
- * @param {Rectangle} [output=null] Optional parameter, if provided then calculated bounds will be applied to that object.
- * @return {Rectangle} bounds object representing the bounds with filters.
- * @static
- **/
- BitmapCache.getFilterBounds = function(target, output) {
- if(!output){ output = new createjs.Rectangle(); }
- var filters = target.filters;
- var filterCount = filters && filters.length;
- if (!!filterCount <= 0) { return output; }
-
- for(var i=0; iWebGL cache with a 2D context
- *
- * var stage = new createjs.Stage();
- * var bmp = new createjs.Bitmap(src);
- * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "new"}); // no StageGL to use, so make one
- *
- * var shape = new createjs.Shape();
- * shape.graphics.clear().fill("red").drawRect(0,0,20,20);
- * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache
- *
- * WebGL cache with a WebGL context
- *
- * var stageGL = new createjs.StageGL();
- * var bmp = new createjs.Bitmap(src);
- * bmp.cache(0, 0, bmp.width, bmp.height, 1, {gl: "stage"}); // use our StageGL to cache
- *
- * var shape = new createjs.Shape();
- * shape.graphics.clear().fill("red").drawRect(0,0,20,20);
- * shape.cache(0, 0, 20, 20, 1); // cannot use WebGL cache
- *
- * You may wish to create your own StageGL instance to control factors like clear color, transparency, AA, and
- * others. If you do, pass a new instance in instead of "true", the library will automatically set the
- * {{#crossLink "StageGL/isCacheControlled"}}{{/crossLink}} to true on your instance. This will trigger it to behave
- * correctly, and not assume your main context is WebGL.
- *
- * @public
- * @method BitmapCache.cache
- * @param {Number} x The x coordinate origin for the cache region.
- * @param {Number} y The y coordinate origin for the cache region.
- * @param {Number} width The width of the cache region.
- * @param {Number} height The height of the cache region.
- * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape
- * using myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and
- * rotate cached elements with greater fidelity. Default is 1.
- * @param {Object} [options=undefined] Specify additional parameters for the cache logic
- * @param {undefined|"new"|"stage"|StageGL} [options.useGL=undefined] Select whether to use context 2D, or WebGL rendering, and
- * whether to make a new stage instance or use an existing one. See above for extensive details on use.
- * @for BitmapCache
- */
- p.define = function(target, x, y, width, height, scale, options) {
- if(!target){ throw "No symbol to cache"; }
- this._options = options;
- this.target = target;
-
- this.width = width >= 1 ? width : 1;
- this.height = height >= 1 ? height : 1;
- this.x = x || 0;
- this.y = y || 0;
- this.scale = scale || 1;
-
- this.update();
- };
-
- /**
- * Directly called via {{#crossLink "DisplayObject/updateCache:method"}}{{/crossLink}}, but also internally. This
- * has the dual responsibility of making sure the surface is ready to be drawn to, and performing the draw. For
- * full details of each behaviour, check the protected functions {{#crossLink "BitmapCache/_updateSurface"}}{{/crossLink}}
- * and {{#crossLink "BitmapCache/_drawToCache"}}{{/crossLink}} respectively.
- * @method update
- * @param {String} [compositeOperation=null] The DisplayObject this cache is linked to.
- **/
- p.update = function(compositeOperation) {
- if(!this.target) { throw "define() must be called before update()"; }
-
- var filterBounds = BitmapCache.getFilterBounds(this.target);
- var surface = this.target.cacheCanvas;
-
- this._drawWidth = Math.ceil(this.width*this.scale) + filterBounds.width;
- this._drawHeight = Math.ceil(this.height*this.scale) + filterBounds.height;
-
- if(!surface || this._drawWidth != surface.width || this._drawHeight != surface.height) {
- this._updateSurface();
- }
-
- this._filterOffX = filterBounds.x;
- this._filterOffY = filterBounds.y;
- this.offX = this.x*this.scale + this._filterOffX;
- this.offY = this.y*this.scale + this._filterOffY;
-
- this._drawToCache(compositeOperation);
-
- this.cacheID = this.cacheID?this.cacheID+1:1;
- };
-
- /**
- * Reset and release all the properties and memory associated with this cache.
- * @method release
- **/
- p.release = function() {
- if (this._webGLCache) {
- // if it isn't cache controlled clean up after yourself
- if (!this._webGLCache.isCacheControlled) {
- if (this.__lastRT){ this.__lastRT = undefined; }
- if (this.__rtA){ this._webGLCache._killTextureObject(this.__rtA); }
- if (this.__rtB){ this._webGLCache._killTextureObject(this.__rtB); }
- if (this.target && this.target.cacheCanvas){ this._webGLCache._killTextureObject(this.target.cacheCanvas); }
- }
- // set the context to none and let the garbage collector get the rest when the canvas itself gets removed
- this._webGLCache = false;
- } else {
- var stage = this.target.stage;
- if (stage instanceof createjs.StageGL){
- stage.releaseTexture(this.target.cacheCanvas);
- }
- }
-
- this.target = this.target.cacheCanvas = null;
- this.cacheID = this._cacheDataURLID = this._cacheDataURL = undefined;
- this.width = this.height = this.x = this.y = this.offX = this.offY = 0;
- this.scale = 1;
- };
-
- /**
- * Returns a data URL for the cache, or `null` if this display object is not cached.
- * Uses {{#crossLink "BitmapCache/cacheID:property"}}{{/crossLink}} to ensure a new data URL is not generated if the
- * cache has not changed.
- * @method getCacheDataURL
- * @return {String} The image data url for the cache.
- **/
- p.getCacheDataURL = function() {
- var cacheCanvas = this.target && this.target.cacheCanvas;
- if (!cacheCanvas) { return null; }
- if (this.cacheID != this._cacheDataURLID) {
- this._cacheDataURLID = this.cacheID;
- this._cacheDataURL = cacheCanvas.toDataURL?cacheCanvas.toDataURL():null; // incase function is
- }
- return this._cacheDataURL;
- };
-
- /**
- * Use context2D drawing commands to display the cache canvas being used.
- * @method draw
- * @param {CanvasRenderingContext2D} ctx The context to draw into.
- * @return {Boolean} Whether the draw was handled successfully.
- **/
- p.draw = function(ctx) {
- if(!this.target) { return false; }
- ctx.drawImage(this.target.cacheCanvas,
- this.x + (this._filterOffX/this.scale), this.y + (this._filterOffY/this.scale),
- this._drawWidth/this.scale, this._drawHeight/this.scale
- );
- return true;
- };
-
-// private methods:
- /**
- * Create or resize the invisible canvas/surface that is needed for the display object(s) to draw to,
- * and in turn be used in their stead when drawing. The surface is resized to the size defined
- * by the width and height, factoring in scaling and filters. Adjust them to adjust the output size.
- * @method _updateSurface
- * @protected
- **/
- p._updateSurface = function() {
- if (!this._options || !this._options.useGL) {
- var surface = this.target.cacheCanvas;
-
- // create it if it's missing
- if(!surface) {
- surface = this.target.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
- }
-
- // now size it
- surface.width = this._drawWidth;
- surface.height = this._drawHeight;
- return;
- }
-
- // create it if it's missing
- if (!this._webGLCache) {
- if (this._options.useGL === "stage") {
- if(!(this.target.stage && this.target.stage.isWebGL)){
- var error = "Cannot use 'stage' for cache because the object's parent stage is ";
- error += this.target.stage ? "non WebGL." : "not set, please addChild to the correct stage.";
- throw error;
- }
- this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks
- this._webGLCache = this.target.stage;
-
- } else if(this._options.useGL === "new") {
- this.target.cacheCanvas = document.createElement("canvas"); // we can turn off autopurge because we wont be making textures here
- this._webGLCache = new createjs.StageGL(this.target.cacheCanvas, {antialias: true, transparent: true, autoPurge: -1});
- this._webGLCache.isCacheControlled = true; // use this flag to control stage sizing and final output
-
- } else if(this._options.useGL instanceof createjs.StageGL) {
- this.target.cacheCanvas = true; // will be replaced with RenderTexture, temporary positive value for old "isCached" checks
- this._webGLCache = this._options.useGL;
- this._webGLCache.isCacheControlled = true; // use this flag to control stage sizing and final output
-
- } else {
- throw "Invalid option provided to useGL, expected ['stage', 'new', StageGL, undefined], got "+ this._options.useGL;
- }
- }
-
- // now size render surfaces
- var surface = this.target.cacheCanvas;
- var stageGL = this._webGLCache;
-
- // if we have a dedicated stage we've gotta size it
- if (stageGL.isCacheControlled) {
- surface.width = this._drawWidth;
- surface.height = this._drawHeight;
- stageGL.updateViewport(this._drawWidth, this._drawHeight);
- }
- if (this.target.filters) {
- // with filters we can't tell how many we'll need but the most we'll ever need is two, so make them now
- stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
- stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
- } else {
- // without filters then we only need one RenderTexture, and that's only if its not a dedicated stage
- if (!stageGL.isCacheControlled) {
- stageGL.getTargetRenderTexture(this.target, this._drawWidth,this._drawHeight);
- }
- }
- };
-
- /**
- * Perform the cache draw out for context 2D now that the setup properties have been performed.
- * @method _drawToCache
- * @protected
- **/
- p._drawToCache = function(compositeOperation) {
- var surface = this.target.cacheCanvas;
- var target = this.target;
- var webGL = this._webGLCache;
-
- if (webGL){
- //TODO: auto split blur into an x/y pass
- webGL.cacheDraw(target, target.filters, this);
-
- // we may of swapped around which element the surface is, so we re-fetch it
- surface = this.target.cacheCanvas;
-
- surface.width = this._drawWidth;
- surface.height = this._drawHeight;
- } else {
- var ctx = surface.getContext("2d");
-
- if (!compositeOperation) {
- ctx.clearRect(0, 0, this._drawWidth+1, this._drawHeight+1);
- }
-
- ctx.save();
- ctx.globalCompositeOperation = compositeOperation;
- ctx.setTransform(this.scale,0,0,this.scale, -this._filterOffX,-this._filterOffY);
- ctx.translate(-this.x, -this.y);
- target.draw(ctx, true);
- ctx.restore();
-
-
- if (target.filters && target.filters.length) {
- this._applyFilters(ctx);
- }
- }
- surface._invalid = true;
- };
-
- /**
- * Work through every filter and apply its individual visual transformation.
- * @method _applyFilters
- * @protected
- **/
- p._applyFilters = function(ctx) {
- var filters = this.target.filters;
-
- var w = this._drawWidth;
- var h = this._drawHeight;
-
- var data;
-
- var i = 0, filter = filters[i];
- do { // this is safe because we wouldn't be in apply filters without a filter count of at least 1
- if(filter.usesContext){
- if(data) {
- ctx.putImageData(data, 0,0);
- data = null;
- }
- filter.applyFilter(ctx, 0,0, w,h);
- } else {
- if(!data) {
- data = ctx.getImageData(0,0, w,h);
- }
- filter._applyFilter(data);
- }
-
- // work through the multipass if it's there, otherwise move on
- filter = filter._multiPass !== null ? filter._multiPass : filters[++i];
- } while (filter);
-
- //done
- if(data) {
- ctx.putImageData(data, 0,0);
- }
- };
-
- createjs.BitmapCache = BitmapCache;
-}());
-
-//##############################################################################
-// BlurFilter.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Applies a box blur to DisplayObjects in context 2D and a Gaussian blur in webgl. Note that this filter is fairly
- * intensive, particularly if the quality is set higher than 1.
- *
- * Example
- * This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}}
- * method to account for the spread that the blur causes.
- *
- * var shape = new createjs.Shape().set({x:100,y:100});
- * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
- *
- * var blurFilter = new createjs.BlurFilter(5, 5, 1);
- * shape.filters = [blurFilter];
- * var bounds = blurFilter.getBounds();
- *
- * shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height);
- *
- * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
- * @class BlurFilter
- * @extends Filter
- * @constructor
- * @param {Number} [blurX=0] The horizontal blur radius in pixels.
- * @param {Number} [blurY=0] The vertical blur radius in pixels.
- * @param {Number} [quality=1] The number of blur iterations.
- **/
- function BlurFilter( blurX, blurY, quality) {
- this.Filter_constructor();
-
- // public properties:
- /**
- * Horizontal blur radius in pixels
- * @property blurX
- * @default 0
- * @type Number
- **/
- this._blurX = blurX;
- this._blurXTable = [];
- this._lastBlurX = null;
-
- /**
- * Vertical blur radius in pixels
- * @property blurY
- * @default 0
- * @type Number
- **/
- this._blurY = blurY;
- this._blurYTable = [];
- this._lastBlurY = null;
-
- /**
- * Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a
- * smoother blur, but take twice as long to run.
- * @property quality
- * @default 1
- * @type Number
- **/
- this._quality;
- this._lastQuality = null;
-
- /**
- * This is a template to generate the shader for {{#crossLink FRAG_SHADER_BODY}}{{/crossLink}}
- */
- this.FRAG_SHADER_TEMPLATE = (
- "uniform float xWeight[{{blurX}}];" +
- "uniform float yWeight[{{blurY}}];" +
- "uniform vec2 textureOffset;" +
- "void main(void) {" +
- "vec4 color = vec4(0.0);" +
-
- "float xAdj = ({{blurX}}.0-1.0)/2.0;" +
- "float yAdj = ({{blurY}}.0-1.0)/2.0;" +
- "vec2 sampleOffset;" +
-
- "for(int i=0; i<{{blurX}}; i++) {" +
- "for(int j=0; j<{{blurY}}; j++) {" +
- "sampleOffset = vRenderCoord + (textureOffset * vec2(float(i)-xAdj, float(j)-yAdj));" +
- "color += texture2D(uSampler, sampleOffset) * (xWeight[i] * yWeight[j]);" +
- "}" +
- "}" +
-
- "gl_FragColor = color.rgba;" +
- "}"
- );
-
- // update the filter using the setters
- if(isNaN(quality) || quality < 1){ quality = 1; }
- this.setQuality(quality|0);
- }
- var p = createjs.extend(BlurFilter, createjs.Filter);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
- p.getBlurX = function() { return this._blurX; };
- p.getBlurY = function() { return this._blurY; };
- p.setBlurX = function(value) {
- if(isNaN(value) || value < 0){ value = 0; }
- this._blurX = value;
- };
- p.setBlurY = function(value) {
- if(isNaN(value) || value < 0){ value = 0; }
- this._blurY = value;
- };
- p.getQuality = function() { return this._quality; };
- p.setQuality = function(value) {
- if(isNaN(value) || value < 0){ value = 0; }
- this._quality = value | 0;
- };
- p._getShader = function() {
- var xChange = this._lastBlurX !== this._blurX;
- var yChange = this._lastBlurY !== this._blurY;
- var qChange = this._lastQuality !== this._quality;
- if(xChange || yChange || qChange) {
- if(xChange || qChange) { this._blurXTable = this._getTable(this._blurX * this._quality); }
- if(yChange || qChange) { this._blurYTable = this._getTable(this._blurY * this._quality); }
- this._updateShader();
- this._lastBlurX = this._blurX;
- this._lastBlurY = this._blurY;
- this._lastQuality = this._quality;
- return undefined; // force a rebuild
- }
- return this._compiledShader;
- };
- p._setShader = function() { this._compiledShader; };
-
- try {
- Object.defineProperties(p, {
- blurX: { get: p.getBlurX, set: p.setBlurX },
- blurY: { get: p.getBlurY, set: p.setBlurY },
- quality: { get: p.getQuality, set: p.setQuality },
- _builtShader: { get: p._getShader, set: p._setShader}
- });
- } catch (e) { console.log(e); }
-
- /**
- * Internal lookup function to create gaussian distribution.
- * @method _getTable
- * @param {Number} spread How many steps in the curve.
- * @return {Array} An array with Math.ceil(spread*2) entries with appropriately distributed weights.
- */
- p._getTable = function(spread) {
- var EDGE = 4.2;
- if(spread<=1) { return [1]; }
-
- var result = [];
- var count = Math.ceil(spread*2);
- count += (count%2)?0:1;
- var adjust = (count/2)|0;
- for(var i = -adjust; i<=adjust; i++) {
- var x = (i/adjust)*EDGE;
- result.push(1/Math.sqrt(2*Math.PI) * Math.pow(Math.E, -(Math.pow(x,2)/4)));
- }
- var factor = result.reduce(function(a, b) { return a + b; });
- return result.map(function(currentValue, index, array) { return currentValue/factor; });
- };
-
- /**
- * Internal update function to create shader properties.
- * @method _updateShader
- */
- p._updateShader = function() {
- if(this._blurX === undefined || this._blurY === undefined){ return; }
- var result = this.FRAG_SHADER_TEMPLATE;
- result = result.replace(/\{\{blurX\}\}/g, (this._blurXTable.length).toFixed(0));
- result = result.replace(/\{\{blurY\}\}/g, (this._blurYTable.length).toFixed(0));
- this.FRAG_SHADER_BODY = result;
- };
-
- /** docced in super class **/
- p.shaderParamSetup = function(gl, stage, shaderProgram) {
- // load the normalized gaussian weight tables
- gl.uniform1fv(
- gl.getUniformLocation(shaderProgram, "xWeight"),
- this._blurXTable
- );
- gl.uniform1fv(
- gl.getUniformLocation(shaderProgram, "yWeight"),
- this._blurYTable
- );
-
- // what is the size of a single pixel in -1, 1 (webGL) space
- gl.uniform2f(
- gl.getUniformLocation(shaderProgram, "textureOffset"),
- 2/(stage._viewportWidth*this._quality), 2/(stage._viewportHeight*this._quality)
- );
- };
-
-// constants:
- /**
- * Array of multiply values for blur calculations.
- * @property MUL_TABLE
- * @type Array
- * @protected
- * @static
- **/
- BlurFilter.MUL_TABLE = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1];
-
- /**
- * Array of shift values for blur calculations.
- * @property SHG_TABLE
- * @type Array
- * @protected
- * @static
- **/
- BlurFilter.SHG_TABLE = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9];
-
-// public methods:
- /** docced in super class **/
- p.getBounds = function (rect) {
- var x = this.blurX|0, y = this.blurY| 0;
- if(x <= 0 && y <= 0) { return rect; }
- var q = Math.pow(this.quality, 0.2);
- return (rect || new createjs.Rectangle()).pad(y*q+1,x*q+1,y*q+1,x*q+1);
- };
-
- /** docced in super class **/
- p.clone = function() {
- return new BlurFilter(this.blurX, this.blurY, this.quality);
- };
-
- /** docced in super class **/
- p.toString = function() {
- return "[BlurFilter]";
- };
-
-
-// private methods:
-
- /** docced in super class **/
- p._applyFilter = function (imageData) {
- var radiusX = this._blurX >> 1;
- if (isNaN(radiusX) || radiusX < 0) return false;
- var radiusY = this._blurY >> 1;
- if (isNaN(radiusY) || radiusY < 0) return false;
- if (radiusX == 0 && radiusY == 0) return false;
-
- var iterations = this.quality;
- if (isNaN(iterations) || iterations < 1) iterations = 1;
- iterations |= 0;
- if (iterations > 3) iterations = 3;
- if (iterations < 1) iterations = 1;
-
- var px = imageData.data;
- var x=0, y=0, i=0, p=0, yp=0, yi=0, yw=0, r=0, g=0, b=0, a=0, pr=0, pg=0, pb=0, pa=0;
-
- var divx = (radiusX + radiusX + 1) | 0;
- var divy = (radiusY + radiusY + 1) | 0;
- var w = imageData.width | 0;
- var h = imageData.height | 0;
-
- var w1 = (w - 1) | 0;
- var h1 = (h - 1) | 0;
- var rxp1 = (radiusX + 1) | 0;
- var ryp1 = (radiusY + 1) | 0;
-
- var ssx = {r:0,b:0,g:0,a:0};
- var sx = ssx;
- for ( i = 1; i < divx; i++ )
- {
- sx = sx.n = {r:0,b:0,g:0,a:0};
- }
- sx.n = ssx;
-
- var ssy = {r:0,b:0,g:0,a:0};
- var sy = ssy;
- for ( i = 1; i < divy; i++ )
- {
- sy = sy.n = {r:0,b:0,g:0,a:0};
- }
- sy.n = ssy;
-
- var si = null;
-
-
- var mtx = BlurFilter.MUL_TABLE[radiusX] | 0;
- var stx = BlurFilter.SHG_TABLE[radiusX] | 0;
- var mty = BlurFilter.MUL_TABLE[radiusY] | 0;
- var sty = BlurFilter.SHG_TABLE[radiusY] | 0;
-
- while (iterations-- > 0) {
-
- yw = yi = 0;
- var ms = mtx;
- var ss = stx;
- for (y = h; --y > -1;) {
- r = rxp1 * (pr = px[(yi) | 0]);
- g = rxp1 * (pg = px[(yi + 1) | 0]);
- b = rxp1 * (pb = px[(yi + 2) | 0]);
- a = rxp1 * (pa = px[(yi + 3) | 0]);
-
- sx = ssx;
-
- for( i = rxp1; --i > -1; )
- {
- sx.r = pr;
- sx.g = pg;
- sx.b = pb;
- sx.a = pa;
- sx = sx.n;
- }
-
- for( i = 1; i < rxp1; i++ )
- {
- p = (yi + ((w1 < i ? w1 : i) << 2)) | 0;
- r += ( sx.r = px[p]);
- g += ( sx.g = px[p+1]);
- b += ( sx.b = px[p+2]);
- a += ( sx.a = px[p+3]);
-
- sx = sx.n;
- }
-
- si = ssx;
- for ( x = 0; x < w; x++ )
- {
- px[yi++] = (r * ms) >>> ss;
- px[yi++] = (g * ms) >>> ss;
- px[yi++] = (b * ms) >>> ss;
- px[yi++] = (a * ms) >>> ss;
-
- p = ((yw + ((p = x + radiusX + 1) < w1 ? p : w1)) << 2);
-
- r -= si.r - ( si.r = px[p]);
- g -= si.g - ( si.g = px[p+1]);
- b -= si.b - ( si.b = px[p+2]);
- a -= si.a - ( si.a = px[p+3]);
-
- si = si.n;
-
- }
- yw += w;
- }
-
- ms = mty;
- ss = sty;
- for (x = 0; x < w; x++) {
- yi = (x << 2) | 0;
-
- r = (ryp1 * (pr = px[yi])) | 0;
- g = (ryp1 * (pg = px[(yi + 1) | 0])) | 0;
- b = (ryp1 * (pb = px[(yi + 2) | 0])) | 0;
- a = (ryp1 * (pa = px[(yi + 3) | 0])) | 0;
-
- sy = ssy;
- for( i = 0; i < ryp1; i++ )
- {
- sy.r = pr;
- sy.g = pg;
- sy.b = pb;
- sy.a = pa;
- sy = sy.n;
- }
-
- yp = w;
-
- for( i = 1; i <= radiusY; i++ )
- {
- yi = ( yp + x ) << 2;
-
- r += ( sy.r = px[yi]);
- g += ( sy.g = px[yi+1]);
- b += ( sy.b = px[yi+2]);
- a += ( sy.a = px[yi+3]);
-
- sy = sy.n;
-
- if( i < h1 )
- {
- yp += w;
- }
- }
-
- yi = x;
- si = ssy;
- if ( iterations > 0 )
- {
- for ( y = 0; y < h; y++ )
- {
- p = yi << 2;
- px[p+3] = pa =(a * ms) >>> ss;
- if ( pa > 0 )
- {
- px[p] = ((r * ms) >>> ss );
- px[p+1] = ((g * ms) >>> ss );
- px[p+2] = ((b * ms) >>> ss );
- } else {
- px[p] = px[p+1] = px[p+2] = 0
- }
-
- p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2;
-
- r -= si.r - ( si.r = px[p]);
- g -= si.g - ( si.g = px[p+1]);
- b -= si.b - ( si.b = px[p+2]);
- a -= si.a - ( si.a = px[p+3]);
-
- si = si.n;
-
- yi += w;
- }
- } else {
- for ( y = 0; y < h; y++ )
- {
- p = yi << 2;
- px[p+3] = pa =(a * ms) >>> ss;
- if ( pa > 0 )
- {
- pa = 255 / pa;
- px[p] = ((r * ms) >>> ss ) * pa;
- px[p+1] = ((g * ms) >>> ss ) * pa;
- px[p+2] = ((b * ms) >>> ss ) * pa;
- } else {
- px[p] = px[p+1] = px[p+2] = 0
- }
-
- p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2;
-
- r -= si.r - ( si.r = px[p]);
- g -= si.g - ( si.g = px[p+1]);
- b -= si.b - ( si.b = px[p+2]);
- a -= si.a - ( si.a = px[p+3]);
-
- si = si.n;
-
- yi += w;
- }
- }
- }
-
- }
- return true;
- };
-
- createjs.BlurFilter = createjs.promote(BlurFilter, "Filter");
-}());
-
-//##############################################################################
-// AlphaMapFilter.js
-//##############################################################################
-
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
-
-// constructor:
- /**
- * Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will
- * be copied from the red channel of the map, and the RGB channels will be copied from the target.
- *
- * Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much
- * better performance.
- *
- * Example
- * This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image.
- *
- * var box = new createjs.Shape();
- * box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100)
- * box.graphics.drawRect(0, 0, 100, 100);
- * box.cache(0, 0, 100, 100);
- *
- * var bmp = new createjs.Bitmap("path/to/image.jpg");
- * bmp.filters = [
- * new createjs.AlphaMapFilter(box.cacheCanvas)
- * ];
- * bmp.cache(0, 0, 100, 100);
- * stage.addChild(bmp);
- *
- * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
- * @class AlphaMapFilter
- * @extends Filter
- * @constructor
- * @param {HTMLImageElement|HTMLCanvasElement} alphaMap The greyscale image (or canvas) to use as the alpha value for the
- * result. This should be exactly the same dimensions as the target.
- **/
- function AlphaMapFilter(alphaMap) {
- this.Filter_constructor();
-
- // public properties:
- /**
- * The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same
- * dimensions as the target.
- * @property alphaMap
- * @type HTMLImageElement|HTMLCanvasElement
- **/
- this.alphaMap = alphaMap;
-
-
- // private properties:
- /**
- * @property _alphaMap
- * @protected
- * @type HTMLImageElement|HTMLCanvasElement
- **/
- this._alphaMap = null;
-
- /**
- * @property _mapData
- * @protected
- * @type Uint8ClampedArray
- **/
- this._mapData = null;
- this._mapTexture = null;
-
- this.FRAG_SHADER_BODY = (
- "uniform sampler2D uAlphaSampler;"+
-
- "void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
- "vec4 alphaMap = texture2D(uAlphaSampler, vTextureCoord);" +
-
- // some image formats can have transparent white rgba(1,1,1, 0) when put on the GPU, this means we need a slight tweak
- // using ceil ensure that the colour will be used so long as it exists but pure transparency will be treated black
- "gl_FragColor = vec4(color.rgb, color.a * (alphaMap.r * ceil(alphaMap.a)));" +
- "}"
- );
- }
- var p = createjs.extend(AlphaMapFilter, createjs.Filter);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
- /** docced in super class **/
- p.shaderParamSetup = function(gl, stage, shaderProgram) {
- if(!this._mapTexture) { this._mapTexture = gl.createTexture(); }
-
- gl.activeTexture(gl.TEXTURE1);
- gl.bindTexture(gl.TEXTURE_2D, this._mapTexture);
- stage.setTextureParams(gl);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.alphaMap);
-
- gl.uniform1i(
- gl.getUniformLocation(shaderProgram, "uAlphaSampler"),
- 1
- );
- };
-
-// public methods:
- /** docced in super class **/
- p.clone = function () {
- var o = new AlphaMapFilter(this.alphaMap);
- o._alphaMap = this._alphaMap;
- o._mapData = this._mapData;
- return o;
- };
-
- /** docced in super class **/
- p.toString = function () {
- return "[AlphaMapFilter]";
- };
-
-
-// private methods:
- /** docced in super class **/
- p._applyFilter = function (imageData) {
- if (!this.alphaMap) { return true; }
- if (!this._prepAlphaMap()) { return false; }
-
- // TODO: update to support scenarios where the target has different dimensions.
- var data = imageData.data;
- var map = this._mapData;
- for(var i=0, l=data.length; iIMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly.
- *
- * Example
- * This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image.
- *
- * var box = new createjs.Shape();
- * box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100)
- * box.graphics.drawRect(0, 0, 100, 100);
- * box.cache(0, 0, 100, 100);
- *
- * var bmp = new createjs.Bitmap("path/to/image.jpg");
- * bmp.filters = [
- * new createjs.AlphaMaskFilter(box.cacheCanvas)
- * ];
- * bmp.cache(0, 0, 100, 100);
- *
- * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
- * @class AlphaMaskFilter
- * @extends Filter
- * @constructor
- * @param {HTMLImageElement|HTMLCanvasElement} mask
- **/
- function AlphaMaskFilter(mask) {
- this.Filter_constructor();
-
- // public properties:
- /**
- * The image (or canvas) to use as the mask.
- * @property mask
- * @type HTMLImageElement|HTMLCanvasElement
- **/
- this.mask = mask;
-
- /** docced in super class **/
- this.usesContext = true;
-
- this.FRAG_SHADER_BODY = (
- "uniform sampler2D uAlphaSampler;"+
-
- "void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
- "vec4 alphaMap = texture2D(uAlphaSampler, vTextureCoord);" +
-
- "gl_FragColor = vec4(color.rgb, color.a * alphaMap.a);" +
- "}"
- );
- }
- var p = createjs.extend(AlphaMaskFilter, createjs.Filter);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
- /** docced in super class **/
- p.shaderParamSetup = function(gl, stage, shaderProgram) {
- if(!this._mapTexture) { this._mapTexture = gl.createTexture(); }
-
- gl.activeTexture(gl.TEXTURE1);
- gl.bindTexture(gl.TEXTURE_2D, this._mapTexture);
- stage.setTextureParams(gl);
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.mask);
-
- gl.uniform1i(
- gl.getUniformLocation(shaderProgram, "uAlphaSampler"),
- 1
- );
- };
-
-// public methods:
- /**
- * Applies the filter to the specified context.
- *
- * IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters
- * correctly.
- * @method applyFilter
- * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
- * @param {Number} x The x position to use for the source rect.
- * @param {Number} y The y position to use for the source rect.
- * @param {Number} width The width to use for the source rect.
- * @param {Number} height The height to use for the source rect.
- * @param {CanvasRenderingContext2D} [targetCtx] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx.
- * @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x.
- * @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y.
- * @return {Boolean} If the filter was applied successfully.
- **/
- p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
- if (!this.mask) { return true; }
- targetCtx = targetCtx || ctx;
- if (targetX == null) { targetX = x; }
- if (targetY == null) { targetY = y; }
-
- targetCtx.save();
- if (ctx != targetCtx) {
- // TODO: support targetCtx and targetX/Y
- // clearRect, then draw the ctx in?
- return false;
- }
-
- targetCtx.globalCompositeOperation = "destination-in";
- targetCtx.drawImage(this.mask, targetX, targetY);
- targetCtx.restore();
- return true;
- };
-
- /** docced in super class **/
- p.clone = function () {
- return new AlphaMaskFilter(this.mask);
- };
-
- /** docced in super class **/
- p.toString = function () {
- return "[AlphaMaskFilter]";
- };
-
-
- createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter");
-}());
-
-//##############################################################################
-// ColorFilter.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Applies a color transform to DisplayObjects.
- *
- * Example
- * This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels
- * to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel.
- *
- * var shape = new createjs.Shape().set({x:100,y:100});
- * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
- *
- * shape.filters = [
- * new createjs.ColorFilter(0,0,0,1, 0,0,255,0)
- * ];
- * shape.cache(-50, -50, 100, 100);
- *
- * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
- * @class ColorFilter
- * @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1.
- * @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1.
- * @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1.
- * @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1.
- * @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range
- * between -255 and 255.
- * @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range
- * between -255 and 255.
- * @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range
- * between -255 and 255.
- * @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range
- * between -255 and 255.
- * @constructor
- * @extends Filter
- **/
- function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) {
- this.Filter_constructor();
-
- // public properties:
- /**
- * Red channel multiplier.
- * @property redMultiplier
- * @type Number
- **/
- this.redMultiplier = redMultiplier != null ? redMultiplier : 1;
-
- /**
- * Green channel multiplier.
- * @property greenMultiplier
- * @type Number
- **/
- this.greenMultiplier = greenMultiplier != null ? greenMultiplier : 1;
-
- /**
- * Blue channel multiplier.
- * @property blueMultiplier
- * @type Number
- **/
- this.blueMultiplier = blueMultiplier != null ? blueMultiplier : 1;
-
- /**
- * Alpha channel multiplier.
- * @property alphaMultiplier
- * @type Number
- **/
- this.alphaMultiplier = alphaMultiplier != null ? alphaMultiplier : 1;
-
- /**
- * Red channel offset (added to value).
- * @property redOffset
- * @type Number
- **/
- this.redOffset = redOffset || 0;
-
- /**
- * Green channel offset (added to value).
- * @property greenOffset
- * @type Number
- **/
- this.greenOffset = greenOffset || 0;
-
- /**
- * Blue channel offset (added to value).
- * @property blueOffset
- * @type Number
- **/
- this.blueOffset = blueOffset || 0;
-
- /**
- * Alpha channel offset (added to value).
- * @property alphaOffset
- * @type Number
- **/
- this.alphaOffset = alphaOffset || 0;
-
- this.FRAG_SHADER_BODY = (
- "uniform vec4 uColorMultiplier;" +
- "uniform vec4 uColorOffset;" +
-
- "void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
-
- "gl_FragColor = (color * uColorMultiplier) + uColorOffset;" +
- "}"
- );
-
- }
- var p = createjs.extend(ColorFilter, createjs.Filter);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// public methods:
- /** docced in super class **/
- p.shaderParamSetup = function(gl, stage, shaderProgram) {
- gl.uniform4f(
- gl.getUniformLocation(shaderProgram, "uColorMultiplier"),
- this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier
- );
-
- gl.uniform4f(
- gl.getUniformLocation(shaderProgram, "uColorOffset"),
- this.redOffset/255, this.greenOffset/255, this.blueOffset/255, this.alphaOffset/255
- );
- };
-
- /** docced in super class **/
- p.toString = function() {
- return "[ColorFilter]";
- };
-
- /** docced in super class **/
- p.clone = function() {
- return new ColorFilter(
- this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier,
- this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset
- );
- };
-
-// private methods:
- /** docced in super class **/
- p._applyFilter = function(imageData) {
- var data = imageData.data;
- var l = data.length;
- for (var i=0; iExample
- *
- * myColorMatrix.adjustHue(20).adjustBrightness(50);
- *
- * See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}
- * for an example of how to use ColorMatrix to change a DisplayObject's color.
- * @class ColorMatrix
- * @param {Number} brightness
- * @param {Number} contrast
- * @param {Number} saturation
- * @param {Number} hue
- * @constructor
- **/
- function ColorMatrix(brightness, contrast, saturation, hue) {
- this.setColor(brightness, contrast, saturation, hue);
- }
- var p = ColorMatrix.prototype;
-
-// constants:
- /**
- * Array of delta values for contrast calculations.
- * @property DELTA_INDEX
- * @type Array
- * @protected
- * @static
- **/
- ColorMatrix.DELTA_INDEX = [
- 0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
- 0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
- 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
- 0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
- 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
- 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
- 1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
- 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
- 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
- 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
- 10.0
- ];
-
- /**
- * Identity matrix values.
- * @property IDENTITY_MATRIX
- * @type Array
- * @protected
- * @static
- **/
- ColorMatrix.IDENTITY_MATRIX = [
- 1,0,0,0,0,
- 0,1,0,0,0,
- 0,0,1,0,0,
- 0,0,0,1,0,
- 0,0,0,0,1
- ];
-
- /**
- * The constant length of a color matrix.
- * @property LENGTH
- * @type Number
- * @protected
- * @static
- **/
- ColorMatrix.LENGTH = ColorMatrix.IDENTITY_MATRIX.length;
-
-
-// public methods:
- /**
- * Resets the instance with the specified values.
- * @method setColor
- * @param {Number} brightness
- * @param {Number} contrast
- * @param {Number} saturation
- * @param {Number} hue
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- */
- p.setColor = function(brightness,contrast,saturation,hue) {
- return this.reset().adjustColor(brightness,contrast,saturation,hue);
- };
-
- /**
- * Resets the matrix to identity values.
- * @method reset
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- */
- p.reset = function() {
- return this.copy(ColorMatrix.IDENTITY_MATRIX);
- };
-
- /**
- * Shortcut method to adjust brightness, contrast, saturation and hue.
- * Equivalent to calling adjustHue(hue), adjustContrast(contrast),
- * adjustBrightness(brightness), adjustSaturation(saturation), in that order.
- * @method adjustColor
- * @param {Number} brightness
- * @param {Number} contrast
- * @param {Number} saturation
- * @param {Number} hue
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.adjustColor = function(brightness,contrast,saturation,hue) {
- this.adjustHue(hue);
- this.adjustContrast(contrast);
- this.adjustBrightness(brightness);
- return this.adjustSaturation(saturation);
- };
-
- /**
- * Adjusts the brightness of pixel color by adding the specified value to the red, green and blue channels.
- * Positive values will make the image brighter, negative values will make it darker.
- * @method adjustBrightness
- * @param {Number} value A value between -255 & 255 that will be added to the RGB channels.
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.adjustBrightness = function(value) {
- if (value == 0 || isNaN(value)) { return this; }
- value = this._cleanValue(value,255);
- this._multiplyMatrix([
- 1,0,0,0,value,
- 0,1,0,0,value,
- 0,0,1,0,value,
- 0,0,0,1,0,
- 0,0,0,0,1
- ]);
- return this;
- };
-
- /**
- * Adjusts the contrast of pixel color.
- * Positive values will increase contrast, negative values will decrease contrast.
- * @method adjustContrast
- * @param {Number} value A value between -100 & 100.
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.adjustContrast = function(value) {
- if (value == 0 || isNaN(value)) { return this; }
- value = this._cleanValue(value,100);
- var x;
- if (value<0) {
- x = 127+value/100*127;
- } else {
- x = value%1;
- if (x == 0) {
- x = ColorMatrix.DELTA_INDEX[value];
- } else {
- x = ColorMatrix.DELTA_INDEX[(value<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(value<<0)+1]*x; // use linear interpolation for more granularity.
- }
- x = x*127+127;
- }
- this._multiplyMatrix([
- x/127,0,0,0,0.5*(127-x),
- 0,x/127,0,0,0.5*(127-x),
- 0,0,x/127,0,0.5*(127-x),
- 0,0,0,1,0,
- 0,0,0,0,1
- ]);
- return this;
- };
-
- /**
- * Adjusts the color saturation of the pixel.
- * Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale).
- * @method adjustSaturation
- * @param {Number} value A value between -100 & 100.
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.adjustSaturation = function(value) {
- if (value == 0 || isNaN(value)) { return this; }
- value = this._cleanValue(value,100);
- var x = 1+((value > 0) ? 3*value/100 : value/100);
- var lumR = 0.3086;
- var lumG = 0.6094;
- var lumB = 0.0820;
- this._multiplyMatrix([
- lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0,
- lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0,
- lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0,
- 0,0,0,1,0,
- 0,0,0,0,1
- ]);
- return this;
- };
-
-
- /**
- * Adjusts the hue of the pixel color.
- * @method adjustHue
- * @param {Number} value A value between -180 & 180.
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.adjustHue = function(value) {
- if (value == 0 || isNaN(value)) { return this; }
- value = this._cleanValue(value,180)/180*Math.PI;
- var cosVal = Math.cos(value);
- var sinVal = Math.sin(value);
- var lumR = 0.213;
- var lumG = 0.715;
- var lumB = 0.072;
- this._multiplyMatrix([
- lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0,
- lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0,
- lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0,
- 0,0,0,1,0,
- 0,0,0,0,1
- ]);
- return this;
- };
-
- /**
- * Concatenates (multiplies) the specified matrix with this one.
- * @method concat
- * @param {Array} matrix An array or ColorMatrix instance.
- * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
- * @chainable
- **/
- p.concat = function(matrix) {
- matrix = this._fixMatrix(matrix);
- if (matrix.length != ColorMatrix.LENGTH) { return this; }
- this._multiplyMatrix(matrix);
- return this;
- };
-
- /**
- * Returns a clone of this ColorMatrix.
- * @method clone
- * @return {ColorMatrix} A clone of this ColorMatrix.
- **/
- p.clone = function() {
- return (new ColorMatrix()).copy(this);
- };
-
- /**
- * Return a length 25 (5x5) array instance containing this matrix's values.
- * @method toArray
- * @return {Array} An array holding this matrix's values.
- **/
- p.toArray = function() {
- var arr = [];
- for (var i= 0, l=ColorMatrix.LENGTH; i ColorMatrix.LENGTH) {
- matrix = matrix.slice(0,ColorMatrix.LENGTH);
- }
- return matrix;
- };
-
-
- createjs.ColorMatrix = ColorMatrix;
-}());
-
-//##############################################################################
-// ColorMatrixFilter.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the
- * {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform,
- * consider the {{#crossLink "ColorFilter"}}{{/crossLink}}.
- *
- * Example
- * This example creates a red circle, inverts its hue, and then saturates it to brighten it up.
- *
- * var shape = new createjs.Shape().set({x:100,y:100});
- * shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
- *
- * var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100);
- * shape.filters = [
- * new createjs.ColorMatrixFilter(matrix)
- * ];
- *
- * shape.cache(-50, -50, 100, 100);
- *
- * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
- * @class ColorMatrixFilter
- * @constructor
- * @extends Filter
- * @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
- * class.
- **/
- function ColorMatrixFilter(matrix) {
- this.Filter_constructor();
-
- // public properties:
- /**
- * A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
- * @property matrix
- * @type Array | ColorMatrix
- **/
- this.matrix = matrix;
-
- this.FRAG_SHADER_BODY = (
- "uniform mat4 uColorMatrix;" +
- "uniform vec4 uColorMatrixOffset;" +
-
- "void main(void) {" +
- "vec4 color = texture2D(uSampler, vRenderCoord);" +
-
- "mat4 m = uColorMatrix;" +
- "vec4 newColor = vec4(0,0,0,0);" +
- "newColor.r = color.r*m[0][0] + color.g*m[0][1] + color.b*m[0][2] + color.a*m[0][3];" +
- "newColor.g = color.r*m[1][0] + color.g*m[1][1] + color.b*m[1][2] + color.a*m[1][3];" +
- "newColor.b = color.r*m[2][0] + color.g*m[2][1] + color.b*m[2][2] + color.a*m[2][3];" +
- "newColor.a = color.r*m[3][0] + color.g*m[3][1] + color.b*m[3][2] + color.a*m[3][3];" +
-
- "gl_FragColor = newColor + uColorMatrixOffset;" +
- "}"
- );
- }
- var p = createjs.extend(ColorMatrixFilter, createjs.Filter);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
- /** docced in super class **/
- p.shaderParamSetup = function(gl, stage, shaderProgram) {
- var mat = this.matrix;
- var colorMatrix = new Float32Array([
- mat[0],mat[1],mat[2],mat[3],
- mat[5],mat[6],mat[7],mat[8],
- mat[10],mat[11],mat[12],mat[13],
- mat[15],mat[16],mat[17],mat[18]
- ]);
-
- gl.uniformMatrix4fv(
- gl.getUniformLocation(shaderProgram, "uColorMatrix"),
- false, colorMatrix
- );
- gl.uniform4f(
- gl.getUniformLocation(shaderProgram, "uColorMatrixOffset"),
- mat[4]/255, mat[9]/255, mat[14]/255, mat[19]/255
- );
- };
-
-// public methods:
- /** docced in super class **/
- p.toString = function() {
- return "[ColorMatrixFilter]";
- };
-
- /** docced in super class **/
- p.clone = function() {
- return new ColorMatrixFilter(this.matrix);
- };
-
-// private methods:
- /** docced in super class **/
- p._applyFilter = function(imageData) {
- var data = imageData.data;
- var l = data.length;
- var r,g,b,a;
- var mtx = this.matrix;
- var m0 = mtx[0], m1 = mtx[1], m2 = mtx[2], m3 = mtx[3], m4 = mtx[4];
- var m5 = mtx[5], m6 = mtx[6], m7 = mtx[7], m8 = mtx[8], m9 = mtx[9];
- var m10 = mtx[10], m11 = mtx[11], m12 = mtx[12], m13 = mtx[13], m14 = mtx[14];
- var m15 = mtx[15], m16 = mtx[16], m17 = mtx[17], m18 = mtx[18], m19 = mtx[19];
-
- for (var i=0; iExample
- *
- * var stage = new createjs.Stage("canvasId");
- * createjs.Touch.enable(stage);
- *
- * Note: It is important to disable Touch on a stage that you are no longer using:
- *
- * createjs.Touch.disable(stage);
- *
- * @class Touch
- * @static
- **/
- function Touch() {
- throw "Touch cannot be instantiated";
- }
-
-
-// public static methods:
- /**
- * Returns `true` if touch is supported in the current browser.
- * @method isSupported
- * @return {Boolean} Indicates whether touch is supported in the current browser.
- * @static
- **/
- Touch.isSupported = function() {
- return !!(('ontouchstart' in window) // iOS & Android
- || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10
- || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+
- };
-
- /**
- * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
- * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
- * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
- * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
- * for more information.
- * @method enable
- * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
- * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
- * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
- * allowed when the user is interacting with the target canvas.
- * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
- * @static
- **/
- Touch.enable = function(stage, singleTouch, allowDefault) {
- if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }
- if (stage.__touch) { return true; }
-
- // inject required properties on stage:
- stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};
-
- // note that in the future we may need to disable the standard mouse event model before adding
- // these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
- if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
- else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); }
- return true;
- };
-
- /**
- * Removes all listeners that were set up when calling `Touch.enable()` on a stage.
- * @method disable
- * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on.
- * @static
- **/
- Touch.disable = function(stage) {
- if (!stage) { return; }
- if ('ontouchstart' in window) { Touch._IOS_disable(stage); }
- else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); }
-
- delete stage.__touch;
- };
-
-
-// Private static methods:
- /**
- * @method _IOS_enable
- * @protected
- * @param {Stage} stage
- * @static
- **/
- Touch._IOS_enable = function(stage) {
- var canvas = stage.canvas;
- var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); };
- canvas.addEventListener("touchstart", f, false);
- canvas.addEventListener("touchmove", f, false);
- canvas.addEventListener("touchend", f, false);
- canvas.addEventListener("touchcancel", f, false);
- };
-
- /**
- * @method _IOS_disable
- * @protected
- * @param {Stage} stage
- * @static
- **/
- Touch._IOS_disable = function(stage) {
- var canvas = stage.canvas;
- if (!canvas) { return; }
- var f = stage.__touch.f;
- canvas.removeEventListener("touchstart", f, false);
- canvas.removeEventListener("touchmove", f, false);
- canvas.removeEventListener("touchend", f, false);
- canvas.removeEventListener("touchcancel", f, false);
- };
-
- /**
- * @method _IOS_handleEvent
- * @param {Stage} stage
- * @param {Object} e The event to handle
- * @protected
- * @static
- **/
- Touch._IOS_handleEvent = function(stage, e) {
- if (!stage) { return; }
- if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
- var touches = e.changedTouches;
- var type = e.type;
- for (var i= 0,l=touches.length; i
Date: Fri, 27 May 2022 14:17:32 +0530
Subject: [PATCH 2/2] Chrome browser - Touch drag fix
---
lib/easeljs.js | 652 ++++++++++++++++++++++++++-----------------------
1 file changed, 341 insertions(+), 311 deletions(-)
diff --git a/lib/easeljs.js b/lib/easeljs.js
index 917b11b41..4abd6738e 100644
--- a/lib/easeljs.js
+++ b/lib/easeljs.js
@@ -7871,8 +7871,6 @@ this.createjs = this.createjs||{};
// Stage.js
//##############################################################################
-this.createjs = this.createjs||{};
-
(function() {
"use strict";
@@ -7903,8 +7901,8 @@ this.createjs = this.createjs||{};
**/
function Stage(canvas) {
this.Container_constructor();
-
-
+
+
// public properties:
/**
* Indicates whether the stage should automatically clear the canvas before each render. You can set this to false
@@ -7921,7 +7919,7 @@ this.createjs = this.createjs||{};
* @default true
**/
this.autoClear = true;
-
+
/**
* The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the
* first stage that will be ticked (or they will clear each other's render).
@@ -7937,7 +7935,7 @@ this.createjs = this.createjs||{};
* @type HTMLCanvasElement | Object
**/
this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas;
-
+
/**
* The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
* position over the canvas, and mouseInBounds will be set to false.
@@ -7946,7 +7944,7 @@ this.createjs = this.createjs||{};
* @readonly
**/
this.mouseX = 0;
-
+
/**
* The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
* position over the canvas, and mouseInBounds will be set to false.
@@ -7955,7 +7953,7 @@ this.createjs = this.createjs||{};
* @readonly
**/
this.mouseY = 0;
-
+
/**
* Specifies the area of the stage to affect when calling update. This can be use to selectively
* re-draw specific regions of the canvas. If null, the whole canvas area is drawn.
@@ -7963,7 +7961,7 @@ this.createjs = this.createjs||{};
* @type {Rectangle}
*/
this.drawRect = null;
-
+
/**
* Indicates whether display objects should be rendered on whole pixels. You can set the
* {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of
@@ -7973,7 +7971,7 @@ this.createjs = this.createjs||{};
* @default false
**/
this.snapToPixelEnabled = false;
-
+
/**
* Indicates whether the mouse is currently within the bounds of the canvas.
* @property mouseInBounds
@@ -7981,7 +7979,7 @@ this.createjs = this.createjs||{};
* @default false
**/
this.mouseInBounds = false;
-
+
/**
* If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas.
* @property tickOnUpdate
@@ -7989,7 +7987,7 @@ this.createjs = this.createjs||{};
* @default true
**/
this.tickOnUpdate = true;
-
+
/**
* If true, mouse move events will continue to be called when the mouse leaves the target canvas. See
* {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}}
@@ -7999,8 +7997,8 @@ this.createjs = this.createjs||{};
* @default false
**/
this.mouseMoveOutside = false;
-
-
+
+
/**
* Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas.
* This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas.
@@ -8009,15 +8007,15 @@ this.createjs = this.createjs||{};
* @default true
**/
this.preventSelection = true;
-
+
/**
* The hitArea property is not supported for Stage.
* @property hitArea
* @type {DisplayObject}
* @default null
*/
-
-
+
+
// private properties:
/**
* Holds objects with data for each active pointer id. Each object has the following properties:
@@ -8027,7 +8025,7 @@ this.createjs = this.createjs||{};
* @private
*/
this._pointerData = {};
-
+
/**
* Number of active pointers.
* @property _pointerCount
@@ -8035,7 +8033,7 @@ this.createjs = this.createjs||{};
* @private
*/
this._pointerCount = 0;
-
+
/**
* The ID of the primary pointer.
* @property _primaryPointerID
@@ -8043,29 +8041,29 @@ this.createjs = this.createjs||{};
* @private
*/
this._primaryPointerID = null;
-
+
/**
* @property _mouseOverIntervalID
* @protected
* @type Number
**/
this._mouseOverIntervalID = null;
-
+
/**
* @property _nextStage
* @protected
* @type Stage
**/
this._nextStage = null;
-
+
/**
* @property _prevStage
* @protected
* @type Stage
**/
this._prevStage = null;
-
-
+
+
// initialize:
this.enableDOMEvents(true);
}
@@ -8109,35 +8107,35 @@ this.createjs = this.createjs||{};
* @event mouseenter
* @since 0.7.0
*/
-
+
/**
* Dispatched each update immediately before the tick event is propagated through the display list.
* You can call preventDefault on the event object to cancel propagating the tick event.
* @event tickstart
* @since 0.7.0
*/
-
+
/**
* Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if
* tickOnUpdate is false. Precedes the "drawstart" event.
* @event tickend
* @since 0.7.0
*/
-
+
/**
* Dispatched each update immediately before the canvas is cleared and the display list is drawn to it.
* You can call preventDefault on the event object to cancel the draw.
* @event drawstart
* @since 0.7.0
*/
-
+
/**
* Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
* @event drawend
* @since 0.7.0
*/
-
+
// getter / setters:
/**
* Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them.
@@ -8147,7 +8145,7 @@ this.createjs = this.createjs||{};
* topStage.nextStage = bottomStage;
*
* To disable relaying, set nextStage to null.
- *
+ *
* MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings
* of the top-most stage, but are only processed if the target stage has mouse over interactions enabled.
* Considerations when using roll over in relay targets:
@@ -8160,12 +8158,12 @@ this.createjs = this.createjs||{};
* topStage.nextStage = targetStage;
* topStage.enableMouseOver(10);
* targetStage.enableMouseOver(30);
- *
+ *
* If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its
* DOM events using:
- *
+ *
* targetStage.enableDOMEvents(false);
- *
+ *
* @property nextStage
* @type {Stage}
**/
@@ -8177,7 +8175,7 @@ this.createjs = this.createjs||{};
if (value) { value._prevStage = this; }
this._nextStage = value;
};
-
+
try {
Object.defineProperties(p, {
nextStage: { get: p._get_nextStage, set: p._set_nextStage }
@@ -8216,7 +8214,7 @@ this.createjs = this.createjs||{};
ctx.restore();
this.dispatchEvent("drawend");
};
-
+
/**
* Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
* unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
@@ -8232,18 +8230,18 @@ this.createjs = this.createjs||{};
* function handleTick(evtObj) {
* // clone the event object from Ticker, and add some custom data to it:
* var evt = evtObj.clone().set({greeting:"hello", name:"world"});
- *
+ *
* // pass it to stage.update():
* myStage.update(evt); // subsequently calls tick() with the same param
* }
- *
+ *
* // ...
* myDisplayObject.on("tick", handleDisplayObjectTick);
* function handleDisplayObjectTick(evt) {
* console.log(evt.delta); // the delta property from the Ticker tick event object
* console.log(evt.greeting, evt.name); // custom data: "hello world"
* }
- *
+ *
* @method tick
* @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property.
**/
@@ -8303,7 +8301,7 @@ this.createjs = this.createjs||{};
data = ctx.getImageData(0, 0, w, h);
var compositeOperation = ctx.globalCompositeOperation;
ctx.globalCompositeOperation = "destination-over";
-
+
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, w, h);
}
@@ -8364,40 +8362,33 @@ this.createjs = this.createjs||{};
* @method enableDOMEvents
* @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true.
**/
-
p.enableDOMEvents = function(enable) {
if (enable == null) { enable = true; }
var n, o, ls = this._eventListeners;
if (!enable && ls) {
for (n in ls) {
o = ls[n];
- if(n!=="mousemove"){
- o.t.removeEventListener(n, o.f, false);
- delete this._eventListeners[n];
- }
+ o.t.removeEventListener(n, o.f, false);
}
- } else if (enable && this.canvas) {
+ this._eventListeners = null;
+ } else if (enable && !ls && this.canvas) {
var t = window.addEventListener ? window : document;
var _this = this;
- if(this._eventListeners===undefined)ls=this._eventListeners={};
- if(ls["mousemove"]===undefined){
- ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} };
- ls["mousemove"].t.addEventListener("mousemove",ls["mousemove"].f, false);
- }
- if(ls["mouseup"]===undefined){
- ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} };
- ls["mouseup"].t.addEventListener("mouseup",ls["mouseup"].f, false);
- }
- if(ls["dblclick"]===undefined){
- ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} };
- ls["dblclick"].t.addEventListener("dblclick",ls["dblclick"].f, false);
- }
- if(ls["mousedown"]===undefined){
- ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} };
- ls["mousedown"].t.addEventListener("mousedown",ls["mousedown"].f, false);
+ ls = this._eventListeners = {};
+
+ // Dan Zen adjust October 28 2019 - added pointerCheck so these do not fire if pointer events have fired
+ ls["mousedown"] = {t:this.canvas, f:function(e) {if (!pointerCheck) _this._handleMouseDown(e)} };
+ ls["mouseup"] = {t:t, f:function(e) {if (!pointerCheck) _this._handleMouseUp(e)} };
+
+ ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} };
+ ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} };
+
+ for (n in ls) {
+ o = ls[n];
+ o.t.addEventListener(n, o.f, false);
}
}
- };
+ };
/**
* Stage instances cannot be cloned.
@@ -8487,11 +8478,11 @@ this.createjs = this.createjs||{};
if (id === -1 && o.inBounds == !inBounds) {
this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e);
}
-
+
this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e);
this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e);
}
-
+
nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null);
};
@@ -8552,20 +8543,20 @@ this.createjs = this.createjs||{};
p._handlePointerUp = function(id, e, clear, owner) {
var nextStage = this._nextStage, o = this._getPointerData(id);
if (this._prevStage && owner === undefined) { return; } // redundant listener.
-
+
var target=null, oTarget = o.target;
if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
-
+
if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; }
-
+
if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); }
this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e);
-
+
if (clear) {
if (id==this._primaryPointerID) { this._primaryPointerID = null; }
delete(this._pointerData[id]);
} else { o.target = null; }
-
+
nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this);
};
@@ -8590,14 +8581,14 @@ this.createjs = this.createjs||{};
p._handlePointerDown = function(id, e, pageX, pageY, owner) {
if (this.preventSelection) { e.preventDefault(); }
if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over.
-
+
if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); }
var target = null, nextStage = this._nextStage, o = this._getPointerData(id);
if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; }
this._dispatchMouseEvent(target, "mousedown", true, id, o, e);
-
+
nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this);
};
@@ -8610,7 +8601,7 @@ this.createjs = this.createjs||{};
**/
p._testMouseOver = function(clear, owner, eventTarget) {
if (this._prevStage && owner === undefined) { return; } // redundant listener.
-
+
var nextStage = this._nextStage;
if (!this._mouseOverIntervalID) {
// not enabled for mouseover, but should still relay the event.
@@ -8620,11 +8611,11 @@ this.createjs = this.createjs||{};
var o = this._getPointerData(-1);
// only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs.
if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; }
-
+
var e = o.posEvtObj;
var isEventTarget = eventTarget || e&&(e.target == this.canvas);
var target=null, common = -1, cursor="", t, i, l;
-
+
if (!owner && (clear || this.mouseInBounds && isEventTarget)) {
target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true);
this._mouseOverX = this.mouseX;
@@ -8666,7 +8657,7 @@ this.createjs = this.createjs||{};
if (oldTarget != target) {
this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget);
}
-
+
nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this);
};
@@ -8713,6 +8704,7 @@ this.createjs = this.createjs||{};
createjs.Stage = createjs.promote(Stage, "Container");
}());
+
//##############################################################################
// StageGL.js
//##############################################################################
@@ -16341,9 +16333,12 @@ this.createjs = this.createjs||{};
//##############################################################################
// Touch.js
+// updated to https://github.com/CreateJS/EaselJS/commit/848dcbf765abd00234569fd348e4402e329f5708
+// Sep 6, 2018
//##############################################################################
-this.createjs = this.createjs||{};
+// Dan Zen change October 28 2019 - used in a couple places below
+var pointerCheck = false;
(function() {
"use strict";
@@ -16373,263 +16368,298 @@ this.createjs = this.createjs||{};
throw "Touch cannot be instantiated";
}
+ // public static methods:
+ /**
+ * Returns `true` if touch is supported in the current browser.
+ * @method isSupported
+ * @return {Boolean} Indicates whether touch is supported in the current browser.
+ * @static
+ **/
+ Touch.isSupported = function() {
+ return !!(
+ ('ontouchstart' in window) // iOS & Android
+
+ // https://github.com/owendwyer addition June 10 2019
+ // to include new Firefox which now turns off some touch events in the desktop browser to avoid being seen as mobile
+ // https://developer.mozilla.org/en-US/docs/Web/API/Touch_events#Browser_compatibility
+ || window.TouchEvent
+
+ || (window.MSPointerEvent && window.navigator.msMaxTouchPoints > 0) // IE10
+ || (window.PointerEvent && window.navigator.maxTouchPoints > 0)
+ ); // IE11+
+ };
-// public static methods:
- /**
- * Returns `true` if touch is supported in the current browser.
- * @method isSupported
- * @return {Boolean} Indicates whether touch is supported in the current browser.
- * @static
- **/
- Touch.isSupported = function() {
- return !!(('ontouchstart' in window) // iOS & Android
- || window.TouchEvent
- || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10
- || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+
- };
-
- /**
- * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
- * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
- * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
- * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
- * for more information.
- * @method enable
- * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
- * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
- * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
- * allowed when the user is interacting with the target canvas.
- * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
- * @static
- **/
- Touch.enable = function(stage, singleTouch, allowDefault) {
- if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }
- if (stage.__touch) { return true; }
-
- // inject required properties on stage:
- stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};
-
- // note that in the future we may need to disable the standard mouse event model before adding
- // these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
- if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
- else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); }
- return true;
- };
-
- /**
- * Removes all listeners that were set up when calling `Touch.enable()` on a stage.
- * @method disable
- * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on.
- * @static
- **/
- Touch.disable = function(stage) {
- if (!stage||!stage.__touch) { return; }
- if ('ontouchstart' in window) { Touch._IOS_disable(stage); }
- else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); }
-
- delete stage.__touch;
- };
-
-
-// Private static methods:
- /**
- * @method _IOS_enable
- * @protected
- * @param {Stage} stage
- * @static
- **/
- Touch._IOS_enable = function(stage) {
- var canvas = stage.canvas;
- var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); };
- canvas.addEventListener("touchstart", f, false);
- canvas.addEventListener("touchmove", f, false);
- canvas.addEventListener("touchend", f, false);
- canvas.addEventListener("touchcancel", f, false);
- };
+ /**
+ * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
+ * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
+ * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
+ * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
+ * for more information.
+ * @method enable
+ * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
+ * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
+ * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
+ * allowed when the user is interacting with the target canvas.
+ * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
+ * @static
+ **/
+ Touch.enable = function(stage, singleTouch, allowDefault) {
+ if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }
+ if (stage.__touch) { return true; }
- /**
- * @method _IOS_disable
- * @protected
- * @param {Stage} stage
- * @static
- **/
- Touch._IOS_disable = function(stage) {
- var canvas = stage.canvas;
- if (!canvas) { return; }
- var f = stage.__touch.f;
- canvas.removeEventListener("touchstart", f, false);
- canvas.removeEventListener("touchmove", f, false);
- canvas.removeEventListener("touchend", f, false);
- canvas.removeEventListener("touchcancel", f, false);
- };
+ // inject required properties on stage:
+ stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};
- /**
- * @method _IOS_handleEvent
- * @param {Stage} stage
- * @param {Object} e The event to handle
- * @protected
- * @static
- **/
- Touch._IOS_handleEvent = function(stage, e) {
- if (!stage) { return; }
- if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
- var touches = e.changedTouches;
- var type = e.type;
- for (var i= 0,l=touches.length; i