diff --git a/bower.json b/bower.json
index aa5e8161..12bf6c41 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "SoundJS",
- "version": "1.0.0",
+ "version": "1.1.1",
"homepage": "https://github.com/CreateJS/SoundJS",
"authors": [
"lannymcnie",
diff --git a/build/package.json b/build/package.json
index a88c8a75..9f7e310c 100644
--- a/build/package.json
+++ b/build/package.json
@@ -1,6 +1,6 @@
{
"name": "SoundJS",
- "version": "1.0.0",
+ "version": "1.1.1",
"description": "SoundJS Docs",
"url": "http://www.createjs.com/soundjs",
"logo": "assets/docs-icon-SoundJS.png",
diff --git a/lib/soundjs-NEXT.js b/lib/soundjs-NEXT.js
index bc535e3e..7263b43e 100644
--- a/lib/soundjs-NEXT.js
+++ b/lib/soundjs-NEXT.js
@@ -49,7 +49,7 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.version = /*=version*/"NEXT"; // injected by build process
+ s.version = /*=version*/""; // injected by build process
/**
* The build date for this release in UTC format.
@@ -57,7 +57,7 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.buildDate = /*=date*/"Fri, 07 Feb 2025 16:53:29 GMT"; // injected by build process
+ s.buildDate = /*=date*/""; // injected by build process
})();
@@ -338,381 +338,381 @@ this.createjs = this.createjs||{};
// EventDispatcher.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * EventDispatcher provides methods for managing queues of event listeners and dispatching events.
- *
- * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the
- * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method.
- *
- * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the
- * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports
- * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent.
- *
- * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier
- * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The
- * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to
- * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}.
- *
- * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}}
- * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also
- * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener.
- *
- *
Example
- * Add EventDispatcher capabilities to the "MyClass" class.
- *
- * EventDispatcher.initialize(MyClass.prototype);
- *
- * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
- *
- * instance.addEventListener("eventName", handlerMethod);
- * function handlerMethod(event) {
- * console.log(event.target + " Was Clicked");
- * }
- *
- * Maintaining proper scope
- * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
- * method to subscribe to events simplifies this.
- *
- * instance.addEventListener("click", function(event) {
- * console.log(instance == this); // false, scope is ambiguous.
- * });
- *
- * instance.on("click", function(event) {
- * console.log(instance == this); // true, "on" uses dispatcher scope by default.
- * });
- *
- * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
- * scope.
- *
- * Browser support
- * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
- * requires modern browsers (IE9+).
- *
- *
- * @class EventDispatcher
- * @constructor
- **/
- function EventDispatcher() {
-
-
- // private properties:
- /**
- * @protected
- * @property _listeners
- * @type Object
- **/
- this._listeners = null;
-
- /**
- * @protected
- * @property _captureListeners
- * @type Object
- **/
- this._captureListeners = null;
- }
- var p = EventDispatcher.prototype;
-
-// static public methods:
- /**
- * Static initializer to mix EventDispatcher methods into a target object or prototype.
- *
- * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
- * EventDispatcher.initialize(myObject); // add to a specific instance
- *
- * @method initialize
- * @static
- * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
- * prototype.
- **/
- EventDispatcher.initialize = function(target) {
- target.addEventListener = p.addEventListener;
- target.on = p.on;
- target.removeEventListener = target.off = p.removeEventListener;
- target.removeAllEventListeners = p.removeAllEventListeners;
- target.hasEventListener = p.hasEventListener;
- target.dispatchEvent = p.dispatchEvent;
- target._dispatchEvent = p._dispatchEvent;
- target.willTrigger = p.willTrigger;
- };
-
-
-// public methods:
- /**
- * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
- * multiple callbacks getting fired.
- *
- * Example
- *
- * displayObject.addEventListener("click", handleClick);
- * function handleClick(event) {
- * // Click happened.
- * }
- *
- * @method addEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function | Object} Returns the listener for chaining or assignment.
- **/
- p.addEventListener = function(type, listener, useCapture) {
- var listeners;
- if (useCapture) {
- listeners = this._captureListeners = this._captureListeners||{};
- } else {
- listeners = this._listeners = this._listeners||{};
- }
- var arr = listeners[type];
- if (arr) { this.removeEventListener(type, listener, useCapture); }
- arr = listeners[type]; // remove may have deleted the array
- if (!arr) { listeners[type] = [listener]; }
- else { arr.push(listener); }
- return listener;
- };
-
- /**
- * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
- * only run once, associate arbitrary data with the listener, and remove the listener.
- *
- * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
- * The wrapper function is returned for use with `removeEventListener` (or `off`).
- *
- * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
- * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
- * to `on` with the same params will create multiple listeners.
- *
- * Example
- *
- * var listener = myBtn.on("click", handleClick, null, false, {count:3});
- * function handleClick(evt, data) {
- * data.count -= 1;
- * console.log(this == myBtn); // true - scope defaults to the dispatcher
- * if (data.count == 0) {
- * alert("clicked 3 times!");
- * myBtn.off("click", listener);
- * // alternately: evt.remove();
- * }
- * }
- *
- * @method on
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
- * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
- * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
- * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
- **/
- p.on = function(type, listener, scope, once, data, useCapture) {
- if (listener.handleEvent) {
- scope = scope||listener;
- listener = listener.handleEvent;
- }
- scope = scope||this;
- return this.addEventListener(type, function(evt) {
- listener.call(scope, evt, data);
- once&&evt.remove();
- }, useCapture);
- };
-
- /**
- * Removes the specified event listener.
- *
- * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
- * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
- * closure will not work.
- *
- * Example
- *
- * displayObject.removeEventListener("click", handleClick);
- *
- * @method removeEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener The listener function or object.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- **/
- p.removeEventListener = function(type, listener, useCapture) {
- var listeners = useCapture ? this._captureListeners : this._listeners;
- if (!listeners) { return; }
- var arr = listeners[type];
- if (!arr) { return; }
- for (var i=0,l=arr.length; iIMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
- * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
- *
- * @method off
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener The listener function or object.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- **/
- p.off = p.removeEventListener;
-
- /**
- * Removes all listeners for the specified type, or all listeners of all types.
- *
- * Example
- *
- * // Remove all listeners
- * displayObject.removeAllEventListeners();
- *
- * // Remove all click listeners
- * displayObject.removeAllEventListeners("click");
- *
- * @method removeAllEventListeners
- * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
- **/
- p.removeAllEventListeners = function(type) {
- if (!type) { this._listeners = this._captureListeners = null; }
- else {
- if (this._listeners) { delete(this._listeners[type]); }
- if (this._captureListeners) { delete(this._captureListeners[type]); }
- }
- };
-
- /**
- * Dispatches the specified event to all listeners.
- *
- * Example
- *
- * // Use a string event
- * this.dispatchEvent("complete");
- *
- * // Use an Event instance
- * var event = new createjs.Event("progress");
- * this.dispatchEvent(event);
- *
- * @method dispatchEvent
- * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
- * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
- * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
- * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
- * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
- * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
- * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
- **/
- p.dispatchEvent = function(eventObj, bubbles, cancelable) {
- if (typeof eventObj == "string") {
- // skip everything if there's no listeners and it doesn't bubble:
- var listeners = this._listeners;
- if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
- eventObj = new createjs.Event(eventObj, bubbles, cancelable);
- } else if (eventObj.target && eventObj.clone) {
- // redispatching an active event object, so clone it:
- eventObj = eventObj.clone();
- }
-
- // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
- try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
-
- if (!eventObj.bubbles || !this.parent) {
- this._dispatchEvent(eventObj, 2);
- } else {
- var top=this, list=[top];
- while (top.parent) { list.push(top = top.parent); }
- var i, l=list.length;
-
- // capture & atTarget
- for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
- list[i]._dispatchEvent(eventObj, 1+(i==0));
- }
- // bubbling
- for (i=1; iExample
+ * Add EventDispatcher capabilities to the "MyClass" class.
+ *
+ * EventDispatcher.initialize(MyClass.prototype);
+ *
+ * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
+ *
+ * instance.addEventListener("eventName", handlerMethod);
+ * function handlerMethod(event) {
+ * console.log(event.target + " Was Clicked");
+ * }
+ *
+ * Maintaining proper scope
+ * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
+ * method to subscribe to events simplifies this.
+ *
+ * instance.addEventListener("click", function(event) {
+ * console.log(instance == this); // false, scope is ambiguous.
+ * });
+ *
+ * instance.on("click", function(event) {
+ * console.log(instance == this); // true, "on" uses dispatcher scope by default.
+ * });
+ *
+ * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
+ * scope.
+ *
+ * Browser support
+ * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
+ * requires modern browsers (IE9+).
+ *
+ *
+ * @class EventDispatcher
+ * @constructor
+ **/
+ function EventDispatcher() {
+
+
+ // private properties:
+ /**
+ * @protected
+ * @property _listeners
+ * @type Object
+ **/
+ this._listeners = null;
+
+ /**
+ * @protected
+ * @property _captureListeners
+ * @type Object
+ **/
+ this._captureListeners = null;
+ }
+ var p = EventDispatcher.prototype;
+
+// static public methods:
+ /**
+ * Static initializer to mix EventDispatcher methods into a target object or prototype.
+ *
+ * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
+ * EventDispatcher.initialize(myObject); // add to a specific instance
+ *
+ * @method initialize
+ * @static
+ * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
+ * prototype.
+ **/
+ EventDispatcher.initialize = function(target) {
+ target.addEventListener = p.addEventListener;
+ target.on = p.on;
+ target.removeEventListener = target.off = p.removeEventListener;
+ target.removeAllEventListeners = p.removeAllEventListeners;
+ target.hasEventListener = p.hasEventListener;
+ target.dispatchEvent = p.dispatchEvent;
+ target._dispatchEvent = p._dispatchEvent;
+ target.willTrigger = p.willTrigger;
+ };
+
+
+// public methods:
+ /**
+ * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
+ * multiple callbacks getting fired.
+ *
+ * Example
+ *
+ * displayObject.addEventListener("click", handleClick);
+ * function handleClick(event) {
+ * // Click happened.
+ * }
+ *
+ * @method addEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function | Object} Returns the listener for chaining or assignment.
+ **/
+ p.addEventListener = function(type, listener, useCapture) {
+ var listeners;
+ if (useCapture) {
+ listeners = this._captureListeners = this._captureListeners||{};
+ } else {
+ listeners = this._listeners = this._listeners||{};
+ }
+ var arr = listeners[type];
+ if (arr) { this.removeEventListener(type, listener, useCapture); }
+ arr = listeners[type]; // remove may have deleted the array
+ if (!arr) { listeners[type] = [listener]; }
+ else { arr.push(listener); }
+ return listener;
+ };
+
+ /**
+ * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
+ * only run once, associate arbitrary data with the listener, and remove the listener.
+ *
+ * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
+ * The wrapper function is returned for use with `removeEventListener` (or `off`).
+ *
+ * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
+ * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
+ * to `on` with the same params will create multiple listeners.
+ *
+ * Example
+ *
+ * var listener = myBtn.on("click", handleClick, null, false, {count:3});
+ * function handleClick(evt, data) {
+ * data.count -= 1;
+ * console.log(this == myBtn); // true - scope defaults to the dispatcher
+ * if (data.count == 0) {
+ * alert("clicked 3 times!");
+ * myBtn.off("click", listener);
+ * // alternately: evt.remove();
+ * }
+ * }
+ *
+ * @method on
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
+ * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
+ * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
+ * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
+ **/
+ p.on = function(type, listener, scope, once, data, useCapture) {
+ if (listener.handleEvent) {
+ scope = scope||listener;
+ listener = listener.handleEvent;
+ }
+ scope = scope||this;
+ return this.addEventListener(type, function(evt) {
+ listener.call(scope, evt, data);
+ once&&evt.remove();
+ }, useCapture);
+ };
+
+ /**
+ * Removes the specified event listener.
+ *
+ * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
+ * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
+ * closure will not work.
+ *
+ * Example
+ *
+ * displayObject.removeEventListener("click", handleClick);
+ *
+ * @method removeEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.removeEventListener = function(type, listener, useCapture) {
+ var listeners = useCapture ? this._captureListeners : this._listeners;
+ if (!listeners) { return; }
+ var arr = listeners[type];
+ if (!arr) { return; }
+ for (var i=0,l=arr.length; iIMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
+ * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
+ *
+ * @method off
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.off = p.removeEventListener;
+
+ /**
+ * Removes all listeners for the specified type, or all listeners of all types.
+ *
+ * Example
+ *
+ * // Remove all listeners
+ * displayObject.removeAllEventListeners();
+ *
+ * // Remove all click listeners
+ * displayObject.removeAllEventListeners("click");
+ *
+ * @method removeAllEventListeners
+ * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
+ **/
+ p.removeAllEventListeners = function(type) {
+ if (!type) { this._listeners = this._captureListeners = null; }
+ else {
+ if (this._listeners) { delete(this._listeners[type]); }
+ if (this._captureListeners) { delete(this._captureListeners[type]); }
+ }
+ };
+
+ /**
+ * Dispatches the specified event to all listeners.
+ *
+ * Example
+ *
+ * // Use a string event
+ * this.dispatchEvent("complete");
+ *
+ * // Use an Event instance
+ * var event = new createjs.Event("progress");
+ * this.dispatchEvent(event);
+ *
+ * @method dispatchEvent
+ * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
+ * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
+ * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
+ * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
+ * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
+ * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
+ * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
+ **/
+ p.dispatchEvent = function(eventObj, bubbles, cancelable) {
+ if (typeof eventObj == "string") {
+ // skip everything if there's no listeners and it doesn't bubble:
+ var listeners = this._listeners;
+ if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
+ eventObj = new createjs.Event(eventObj, bubbles, cancelable);
+ } else if (eventObj.target && eventObj.clone) {
+ // redispatching an active event object, so clone it:
+ eventObj = eventObj.clone();
+ }
+
+ // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
+ try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
+
+ if (!eventObj.bubbles || !this.parent) {
+ this._dispatchEvent(eventObj, 2);
+ } else {
+ var top=this, list=[top];
+ while (top.parent) { list.push(top = top.parent); }
+ var i, l=list.length;
+
+ // capture & atTarget
+ for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
+ list[i]._dispatchEvent(eventObj, 1+(i==0));
+ }
+ // bubbling
+ for (i=1; iExample
- * Add EventDispatcher capabilities to the "MyClass" class.
- *
- * EventDispatcher.initialize(MyClass.prototype);
- *
- * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
- *
- * instance.addEventListener("eventName", handlerMethod);
- * function handlerMethod(event) {
- * console.log(event.target + " Was Clicked");
- * }
- *
- * Maintaining proper scope
- * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
- * method to subscribe to events simplifies this.
- *
- * instance.addEventListener("click", function(event) {
- * console.log(instance == this); // false, scope is ambiguous.
- * });
- *
- * instance.on("click", function(event) {
- * console.log(instance == this); // true, "on" uses dispatcher scope by default.
- * });
- *
- * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
- * scope.
- *
- * Browser support
- * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
- * requires modern browsers (IE9+).
- *
- *
- * @class EventDispatcher
- * @constructor
- **/
- function EventDispatcher() {
-
-
- // private properties:
- /**
- * @protected
- * @property _listeners
- * @type Object
- **/
- this._listeners = null;
-
- /**
- * @protected
- * @property _captureListeners
- * @type Object
- **/
- this._captureListeners = null;
- }
- var p = EventDispatcher.prototype;
-
-// static public methods:
- /**
- * Static initializer to mix EventDispatcher methods into a target object or prototype.
- *
- * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
- * EventDispatcher.initialize(myObject); // add to a specific instance
- *
- * @method initialize
- * @static
- * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
- * prototype.
- **/
- EventDispatcher.initialize = function(target) {
- target.addEventListener = p.addEventListener;
- target.on = p.on;
- target.removeEventListener = target.off = p.removeEventListener;
- target.removeAllEventListeners = p.removeAllEventListeners;
- target.hasEventListener = p.hasEventListener;
- target.dispatchEvent = p.dispatchEvent;
- target._dispatchEvent = p._dispatchEvent;
- target.willTrigger = p.willTrigger;
- };
-
-
-// public methods:
- /**
- * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
- * multiple callbacks getting fired.
- *
- * Example
- *
- * displayObject.addEventListener("click", handleClick);
- * function handleClick(event) {
- * // Click happened.
- * }
- *
- * @method addEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function | Object} Returns the listener for chaining or assignment.
- **/
- p.addEventListener = function(type, listener, useCapture) {
- var listeners;
- if (useCapture) {
- listeners = this._captureListeners = this._captureListeners||{};
- } else {
- listeners = this._listeners = this._listeners||{};
- }
- var arr = listeners[type];
- if (arr) { this.removeEventListener(type, listener, useCapture); }
- arr = listeners[type]; // remove may have deleted the array
- if (!arr) { listeners[type] = [listener]; }
- else { arr.push(listener); }
- return listener;
- };
-
- /**
- * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
- * only run once, associate arbitrary data with the listener, and remove the listener.
- *
- * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
- * The wrapper function is returned for use with `removeEventListener` (or `off`).
- *
- * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
- * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
- * to `on` with the same params will create multiple listeners.
- *
- * Example
- *
- * var listener = myBtn.on("click", handleClick, null, false, {count:3});
- * function handleClick(evt, data) {
- * data.count -= 1;
- * console.log(this == myBtn); // true - scope defaults to the dispatcher
- * if (data.count == 0) {
- * alert("clicked 3 times!");
- * myBtn.off("click", listener);
- * // alternately: evt.remove();
- * }
- * }
- *
- * @method on
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
- * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
- * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
- * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
- **/
- p.on = function(type, listener, scope, once, data, useCapture) {
- if (listener.handleEvent) {
- scope = scope||listener;
- listener = listener.handleEvent;
- }
- scope = scope||this;
- return this.addEventListener(type, function(evt) {
- listener.call(scope, evt, data);
- once&&evt.remove();
- }, useCapture);
- };
-
- /**
- * Removes the specified event listener.
- *
- * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
- * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
- * closure will not work.
- *
- * Example
- *
- * displayObject.removeEventListener("click", handleClick);
- *
- * @method removeEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener The listener function or object.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- **/
- p.removeEventListener = function(type, listener, useCapture) {
- var listeners = useCapture ? this._captureListeners : this._listeners;
- if (!listeners) { return; }
- var arr = listeners[type];
- if (!arr) { return; }
- for (var i=0,l=arr.length; iIMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
- * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
- *
- * @method off
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener The listener function or object.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- **/
- p.off = p.removeEventListener;
-
- /**
- * Removes all listeners for the specified type, or all listeners of all types.
- *
- * Example
- *
- * // Remove all listeners
- * displayObject.removeAllEventListeners();
- *
- * // Remove all click listeners
- * displayObject.removeAllEventListeners("click");
- *
- * @method removeAllEventListeners
- * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
- **/
- p.removeAllEventListeners = function(type) {
- if (!type) { this._listeners = this._captureListeners = null; }
- else {
- if (this._listeners) { delete(this._listeners[type]); }
- if (this._captureListeners) { delete(this._captureListeners[type]); }
- }
- };
-
- /**
- * Dispatches the specified event to all listeners.
- *
- * Example
- *
- * // Use a string event
- * this.dispatchEvent("complete");
- *
- * // Use an Event instance
- * var event = new createjs.Event("progress");
- * this.dispatchEvent(event);
- *
- * @method dispatchEvent
- * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
- * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
- * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
- * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
- * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
- * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
- * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
- **/
- p.dispatchEvent = function(eventObj, bubbles, cancelable) {
- if (typeof eventObj == "string") {
- // skip everything if there's no listeners and it doesn't bubble:
- var listeners = this._listeners;
- if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
- eventObj = new createjs.Event(eventObj, bubbles, cancelable);
- } else if (eventObj.target && eventObj.clone) {
- // redispatching an active event object, so clone it:
- eventObj = eventObj.clone();
- }
-
- // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
- try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
-
- if (!eventObj.bubbles || !this.parent) {
- this._dispatchEvent(eventObj, 2);
- } else {
- var top=this, list=[top];
- while (top.parent) { list.push(top = top.parent); }
- var i, l=list.length;
-
- // capture & atTarget
- for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
- list[i]._dispatchEvent(eventObj, 1+(i==0));
- }
- // bubbling
- for (i=1; iExample
+ * Add EventDispatcher capabilities to the "MyClass" class.
+ *
+ * EventDispatcher.initialize(MyClass.prototype);
+ *
+ * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
+ *
+ * instance.addEventListener("eventName", handlerMethod);
+ * function handlerMethod(event) {
+ * console.log(event.target + " Was Clicked");
+ * }
+ *
+ * Maintaining proper scope
+ * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
+ * method to subscribe to events simplifies this.
+ *
+ * instance.addEventListener("click", function(event) {
+ * console.log(instance == this); // false, scope is ambiguous.
+ * });
+ *
+ * instance.on("click", function(event) {
+ * console.log(instance == this); // true, "on" uses dispatcher scope by default.
+ * });
+ *
+ * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
+ * scope.
+ *
+ * Browser support
+ * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
+ * requires modern browsers (IE9+).
+ *
+ *
+ * @class EventDispatcher
+ * @constructor
+ **/
+ function EventDispatcher() {
+
+
+ // private properties:
+ /**
+ * @protected
+ * @property _listeners
+ * @type Object
+ **/
+ this._listeners = null;
+
+ /**
+ * @protected
+ * @property _captureListeners
+ * @type Object
+ **/
+ this._captureListeners = null;
+ }
+ var p = EventDispatcher.prototype;
+
+// static public methods:
+ /**
+ * Static initializer to mix EventDispatcher methods into a target object or prototype.
+ *
+ * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
+ * EventDispatcher.initialize(myObject); // add to a specific instance
+ *
+ * @method initialize
+ * @static
+ * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
+ * prototype.
+ **/
+ EventDispatcher.initialize = function(target) {
+ target.addEventListener = p.addEventListener;
+ target.on = p.on;
+ target.removeEventListener = target.off = p.removeEventListener;
+ target.removeAllEventListeners = p.removeAllEventListeners;
+ target.hasEventListener = p.hasEventListener;
+ target.dispatchEvent = p.dispatchEvent;
+ target._dispatchEvent = p._dispatchEvent;
+ target.willTrigger = p.willTrigger;
+ };
+
+
+// public methods:
+ /**
+ * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
+ * multiple callbacks getting fired.
+ *
+ * Example
+ *
+ * displayObject.addEventListener("click", handleClick);
+ * function handleClick(event) {
+ * // Click happened.
+ * }
+ *
+ * @method addEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function | Object} Returns the listener for chaining or assignment.
+ **/
+ p.addEventListener = function(type, listener, useCapture) {
+ var listeners;
+ if (useCapture) {
+ listeners = this._captureListeners = this._captureListeners||{};
+ } else {
+ listeners = this._listeners = this._listeners||{};
+ }
+ var arr = listeners[type];
+ if (arr) { this.removeEventListener(type, listener, useCapture); }
+ arr = listeners[type]; // remove may have deleted the array
+ if (!arr) { listeners[type] = [listener]; }
+ else { arr.push(listener); }
+ return listener;
+ };
+
+ /**
+ * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
+ * only run once, associate arbitrary data with the listener, and remove the listener.
+ *
+ * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
+ * The wrapper function is returned for use with `removeEventListener` (or `off`).
+ *
+ * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
+ * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
+ * to `on` with the same params will create multiple listeners.
+ *
+ * Example
+ *
+ * var listener = myBtn.on("click", handleClick, null, false, {count:3});
+ * function handleClick(evt, data) {
+ * data.count -= 1;
+ * console.log(this == myBtn); // true - scope defaults to the dispatcher
+ * if (data.count == 0) {
+ * alert("clicked 3 times!");
+ * myBtn.off("click", listener);
+ * // alternately: evt.remove();
+ * }
+ * }
+ *
+ * @method on
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
+ * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
+ * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
+ * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
+ **/
+ p.on = function(type, listener, scope, once, data, useCapture) {
+ if (listener.handleEvent) {
+ scope = scope||listener;
+ listener = listener.handleEvent;
+ }
+ scope = scope||this;
+ return this.addEventListener(type, function(evt) {
+ listener.call(scope, evt, data);
+ once&&evt.remove();
+ }, useCapture);
+ };
+
+ /**
+ * Removes the specified event listener.
+ *
+ * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
+ * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
+ * closure will not work.
+ *
+ * Example
+ *
+ * displayObject.removeEventListener("click", handleClick);
+ *
+ * @method removeEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.removeEventListener = function(type, listener, useCapture) {
+ var listeners = useCapture ? this._captureListeners : this._listeners;
+ if (!listeners) { return; }
+ var arr = listeners[type];
+ if (!arr) { return; }
+ for (var i=0,l=arr.length; iIMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
+ * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
+ *
+ * @method off
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.off = p.removeEventListener;
+
+ /**
+ * Removes all listeners for the specified type, or all listeners of all types.
+ *
+ * Example
+ *
+ * // Remove all listeners
+ * displayObject.removeAllEventListeners();
+ *
+ * // Remove all click listeners
+ * displayObject.removeAllEventListeners("click");
+ *
+ * @method removeAllEventListeners
+ * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
+ **/
+ p.removeAllEventListeners = function(type) {
+ if (!type) { this._listeners = this._captureListeners = null; }
+ else {
+ if (this._listeners) { delete(this._listeners[type]); }
+ if (this._captureListeners) { delete(this._captureListeners[type]); }
+ }
+ };
+
+ /**
+ * Dispatches the specified event to all listeners.
+ *
+ * Example
+ *
+ * // Use a string event
+ * this.dispatchEvent("complete");
+ *
+ * // Use an Event instance
+ * var event = new createjs.Event("progress");
+ * this.dispatchEvent(event);
+ *
+ * @method dispatchEvent
+ * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
+ * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
+ * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
+ * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
+ * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
+ * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
+ * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
+ **/
+ p.dispatchEvent = function(eventObj, bubbles, cancelable) {
+ if (typeof eventObj == "string") {
+ // skip everything if there's no listeners and it doesn't bubble:
+ var listeners = this._listeners;
+ if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
+ eventObj = new createjs.Event(eventObj, bubbles, cancelable);
+ } else if (eventObj.target && eventObj.clone) {
+ // redispatching an active event object, so clone it:
+ eventObj = eventObj.clone();
+ }
+
+ // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
+ try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
+
+ if (!eventObj.bubbles || !this.parent) {
+ this._dispatchEvent(eventObj, 2);
+ } else {
+ var top=this, list=[top];
+ while (top.parent) { list.push(top = top.parent); }
+ var i, l=list.length;
+
+ // capture & atTarget
+ for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
+ list[i]._dispatchEvent(eventObj, 1+(i==0));
+ }
+ // bubbling
+ for (i=1; ipan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
+ * startAt - Web Audio only: the AudioContext time in seconds to start playback at (null or past = now).
*
*
* Example
@@ -3708,6 +3709,17 @@ this.createjs = this.createjs || {};
* @default null
*/
this.duration = null;
+
+ /**
+ * Web Audio only: the AudioContext time (in seconds) at which playback should start. A value in the
+ * past, or null, starts playback immediately. Starting in the future is sample-accurate, which a
+ * "now" start is not on devices with a coarse render quantum.
+ * @property startAt
+ * @type {number}
+ * @default null
+ * @since 1.1.0
+ */
+ this.startAt = null;
};
var p = PlayPropsConfig.prototype = {};
var s = PlayPropsConfig;
@@ -5590,6 +5602,25 @@ this.createjs = this.createjs || {};
* @since 0.4.0
*/
this.delayTimeoutId = null;
+
+ /**
+ * Web Audio only: the AudioContext time (seconds) the next play should start at. Consumed by the
+ * play; null or a time in the past means "now". See {{#crossLink "PlayPropsConfig/startAt:property"}}{{/crossLink}}.
+ * @property startAt
+ * @type {Number}
+ * @default null
+ * @since 1.1.0
+ */
+ this.startAt = null;
+
+ /**
+ * Web Audio only: the AudioContext time (seconds) the current play started, or will start, at.
+ * @property scheduledAt
+ * @type {Number}
+ * @default null
+ * @since 1.1.0
+ */
+ this.scheduledAt = null;
// TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins
@@ -5874,6 +5905,7 @@ this.createjs = this.createjs || {};
this._setStartTime(playProps.startTime);
this._setDuration(playProps.duration);
}
+ if (playProps.startAt != null) { this.startAt = playProps.startAt; }
return this;
};
@@ -6180,6 +6212,7 @@ this.createjs = this.createjs || {};
this._setStartTime(playProps.startTime);
this._setDuration(playProps.duration);
}
+ if (playProps.startAt != null) { this.startAt = playProps.startAt; }
if (this._playbackResource != null && this._position < this._duration) {
this._paused = false;
@@ -6806,6 +6839,16 @@ this.createjs = this.createjs || {};
*/
this.sourceNode = null;
+ /**
+ * Whether the current play is an infinite loop on a single, natively looping source node
+ * (see {{#crossLink "WebAudioSoundInstance/nativeLoops:property"}}{{/crossLink}}).
+ * @property nativeLoop
+ * @type {Boolean}
+ * @default false
+ * @since 1.1.0
+ */
+ this.nativeLoop = false;
+
// private properties
/**
@@ -6878,6 +6921,21 @@ this.createjs = this.createjs || {};
*/
s.destinationNode = null;
+ /**
+ * Play infinite loops (loop = -1) on ONE source node with loop = true instead of a chain of
+ * one node per repetition. The chain is refilled from a timer armed relative to when it last ran, so
+ * main-thread stalls accumulate and once they add up to a loop length the next repetition is created
+ * after it should have started: an audible gap. A native loop involves no JS while it plays. It
+ * dispatches no "loop" event, and its {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}}
+ * wraps to the repetition in progress. Finite loop counts always use the chain.
+ * @property nativeLoops
+ * @type {Boolean}
+ * @default true
+ * @static
+ * @since 1.1.0
+ */
+ s.nativeLoops = true;
+
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
@@ -6911,6 +6969,15 @@ this.createjs = this.createjs || {};
};
p._removeLooping = function(value) {
+ if (this.nativeLoop && this.sourceNode) {
+ // Let the repetition in progress finish, then complete.
+ this.sourceNode.loop = false;
+ var remainingMs = this._duration - this._calculateCurrentPosition();
+ clearTimeout(this._soundCompleteTimeout);
+ this._soundCompleteTimeout = setTimeout(this._endedHandler, Math.max(0, remainingMs));
+ this.nativeLoop = false;
+ return;
+ }
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
@@ -6935,6 +7002,7 @@ this.createjs = this.createjs || {};
clearTimeout(this._soundCompleteTimeout);
this._playbackStartTime = 0; // This is used by _getPosition
+ this.nativeLoop = false;
};
/**
@@ -6962,18 +7030,61 @@ this.createjs = this.createjs || {};
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
+ // A future startAt is honoured sample-accurately; null or past = now.
+ var now = s.context.currentTime;
+ var startAt = this.startAt;
+ this.startAt = null;
+ var at = (typeof startAt === "number" && startAt > now) ? startAt : now;
+ this.scheduledAt = at;
+ this.nativeLoop = false;
+
var dur = this._duration * 0.001,
pos = Math.min(Math.max(0, this._position) * 0.001, dur);
- this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
+
+ if (this._loop < 0 && s.nativeLoops) {
+ this._startNativeLoop(at, pos, dur);
+ return;
+ }
+
+ this.sourceNode = this._createAndPlayAudioNode((at - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
- this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
+ this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos + (at - now)) * 1000);
if(this._loop != 0) {
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
}
};
+ /**
+ * Start an infinite loop on one natively looping source node (see
+ * {{#crossLink "WebAudioSoundInstance/nativeLoops:property"}}{{/crossLink}}).
+ * @method _startNativeLoop
+ * @param {Number} at The context time to start at, in seconds.
+ * @param {Number} pos The position in the sound to start at, in seconds.
+ * @param {Number} dur The duration of the sound, in seconds.
+ * @protected
+ * @since 1.1.0
+ */
+ p._startNativeLoop = function (at, pos, dur) {
+ if (pos >= dur) { pos = 0; }
+ var spriteStart = this._startTime * 0.001;
+
+ var audioNode = s.context.createBufferSource();
+ audioNode.buffer = this.playbackResource;
+ audioNode.connect(this.panNode);
+ audioNode.loop = true;
+ audioNode.loopStart = spriteStart;
+ audioNode.loopEnd = spriteStart + dur;
+ audioNode.startTime = at;
+ audioNode.start(at, spriteStart + pos);
+
+ this.sourceNode = audioNode;
+ this._sourceNodeNext = null;
+ this._playbackStartTime = at - pos;
+ this.nativeLoop = true;
+ };
+
/**
* Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
* @method _createAndPlayAudioNode
@@ -6994,7 +7105,7 @@ this.createjs = this.createjs || {};
};
p._pause = function () {
- this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
+ this._position = this._calculateCurrentPosition(); // lets us restart at same point (wrapped for a native loop)
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
@@ -7021,7 +7132,11 @@ this.createjs = this.createjs || {};
};
p._calculateCurrentPosition = function () {
- return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
+ var ms = (s.context.currentTime - this._playbackStartTime) * 1000; // pos in seconds * 1000 to give milliseconds
+ if (this.nativeLoop && this._duration > 0 && ms >= this._duration) {
+ ms = ms % this._duration; // the repetition in progress
+ }
+ return ms;
};
p._updatePosition = function () {
diff --git a/lib/soundjs.min.js b/lib/soundjs.min.js
index 2f71ecd2..d62a2634 100644
--- a/lib/soundjs.min.js
+++ b/lib/soundjs.min.js
@@ -14,5 +14,6 @@
* SoundJS FlashAudioPlugin also includes swfobject (http://code.google.com/p/swfobject/)
*/
-this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="1.0.0",a.buildDate="Fri, 07 Feb 2025 16:37:31 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.deprecate=function(a,b){"use strict";return function(){var c="Deprecated property or method '"+b+"'. See docs for info.";return console&&(console.warn?console.warn(c):console.log(c)),a&&a.apply(this,arguments)}},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;c-1||a.indexOf("Windows Phone")>-1,BrowserDetect.isFirefox=a.indexOf("Firefox")>-1,BrowserDetect.isOpera=null!=window.opera,BrowserDetect.isChrome=a.indexOf("Chrome")>-1,BrowserDetect.isIOS=(a.indexOf("iPod")>-1||a.indexOf("iPhone")>-1||a.indexOf("iPad")>-1)&&!BrowserDetect.isWindowPhone,BrowserDetect.isAndroid=a.indexOf("Android")>-1&&!BrowserDetect.isWindowPhone,BrowserDetect.isBlackberry=a.indexOf("Blackberry")>-1,createjs.BrowserDetect=BrowserDetect}(),this.createjs=this.createjs||{},function(){"use strict";function EventDispatcher(){this._listeners=null,this._captureListeners=null}var a=EventDispatcher.prototype;EventDispatcher.initialize=function(b){b.addEventListener=a.addEventListener,b.on=a.on,b.removeEventListener=b.off=a.removeEventListener,b.removeAllEventListeners=a.removeAllEventListeners,b.hasEventListener=a.hasEventListener,b.dispatchEvent=a.dispatchEvent,b._dispatchEvent=a._dispatchEvent,b.willTrigger=a.willTrigger},a.addEventListener=function(a,b,c){var d;d=c?this._captureListeners=this._captureListeners||{}:this._listeners=this._listeners||{};var e=d[a];return e&&this.removeEventListener(a,b,c),e=d[a],e?e.push(b):d[a]=[b],b},a.on=function(a,b,c,d,e,f){return b.handleEvent&&(c=c||b,b=b.handleEvent),c=c||this,this.addEventListener(a,function(a){b.call(c,a,e),d&&a.remove()},f)},a.removeEventListener=function(a,b,c){var d=c?this._captureListeners:this._listeners;if(d){var e=d[a];if(e)for(var f=0,g=e.length;f=0&&!a.propagationStopped;g--)f[g]._dispatchEvent(a,1+(0==g));for(g=1;g-1&&(b=b.substr(0,f));var g;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(g=b.match(a.EXTENSION_PATT))&&(c.extension=g[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildURI=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=createjs.Elements.a();b.href=a.src;var c=createjs.Elements.a();return c.href=location.href,""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname)},a.isLocal=function(a){var b=createjs.Elements.a();return b.href=a.src,""==b.hostname&&"file:"==b.protocol},createjs.URLUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;try{Object.defineProperties(b,{POST:{get:createjs.deprecate(function(){return createjs.Methods.POST},"AbstractLoader.POST")},GET:{get:createjs.deprecate(function(){return createjs.Methods.GET},"AbstractLoader.GET")},BINARY:{get:createjs.deprecate(function(){return createjs.Types.BINARY},"AbstractLoader.BINARY")},CSS:{get:createjs.deprecate(function(){return createjs.Types.CSS},"AbstractLoader.CSS")},FONT:{get:createjs.deprecate(function(){return createjs.Types.FONT},"AbstractLoader.FONT")},FONTCSS:{get:createjs.deprecate(function(){return createjs.Types.FONTCSS},"AbstractLoader.FONTCSS")},IMAGE:{get:createjs.deprecate(function(){return createjs.Types.IMAGE},"AbstractLoader.IMAGE")},JAVASCRIPT:{get:createjs.deprecate(function(){return createjs.Types.JAVASCRIPT},"AbstractLoader.JAVASCRIPT")},JSON:{get:createjs.deprecate(function(){return createjs.Types.JSON},"AbstractLoader.JSON")},JSONP:{get:createjs.deprecate(function(){return createjs.Types.JSONP},"AbstractLoader.JSONP")},MANIFEST:{get:createjs.deprecate(function(){return createjs.Types.MANIFEST},"AbstractLoader.MANIFEST")},SOUND:{get:createjs.deprecate(function(){return createjs.Types.SOUND},"AbstractLoader.SOUND")},VIDEO:{get:createjs.deprecate(function(){return createjs.Types.VIDEO},"AbstractLoader.VIDEO")},SPRITESHEET:{get:createjs.deprecate(function(){return createjs.Types.SPRITESHEET},"AbstractLoader.SPRITESHEET")},SVG:{get:createjs.deprecate(function(){return createjs.Types.SVG},"AbstractLoader.SVG")},TEXT:{get:createjs.deprecate(function(){return createjs.Types.TEXT},"AbstractLoader.TEXT")},XML:{get:createjs.deprecate(function(){return createjs.Types.XML},"AbstractLoader.XML")}})}catch(a){}a.getItem=function(){return this._item},a.getResult=function(a){return a?this._rawResult:this._result},a.getTag=function(){return this._tag},a.setTag=function(a){this._tag=a},a.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},a.cancel=function(){this.canceled=!0,this.destroy()},a.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},a.getLoadedItems=function(){return this._loadedItems},a._createRequest=function(){this._preferXHR?this._request=new createjs.XHRRequest(this._item):this._request=new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._createTag=function(a){return null},a._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},a._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||this.progress==1/0)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},a._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},a._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a?a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"):void 0===a.type&&(a=new createjs.ErrorEvent(a.message)),this.dispatchEvent(a))},a._isCanceled=function(){return this.canceled},a.resultFormatter=null,a.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this);b instanceof Function?b.call(this,createjs.proxy(this._resultFormatSuccess,this),createjs.proxy(this._resultFormatFailed,this)):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_"+a.type.toUpperCase()+"_ERROR"))}},a._resultFormatSuccess=function(a){this._result=a,this._sendComplete()},a._resultFormatFailed=function(a){this._sendError(a)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",this.on("initialize",this._updateXHR,this)}var a=createjs.extend(AbstractMediaLoader,createjs.AbstractLoader);a.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},a._createTag=function(){},a._createRequest=function(){this._preferXHR?this._request=new createjs.XHRRequest(this._item):this._request=new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._updateXHR=function(a){a.loader.setResponseType&&a.loader.setResponseType("blob")},a._formatResult=function(a){if(this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR){var b=window.URL||window.webkitURL,c=a.getResult(!0);a.getTag().src=b.createObjectURL(c)}return a.getTag()},createjs.AbstractMediaLoader=createjs.promote(AbstractMediaLoader,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractRequest=function(a){this._item=a},a=createjs.extend(AbstractRequest,createjs.EventDispatcher);a.load=function(){},a.destroy=function(){},a.cancel=function(){},createjs.AbstractRequest=createjs.promote(AbstractRequest,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function TagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(createjs.DomUtils.appendToBody(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;"loaded"!=a.readyState&&"complete"!=a.readyState||this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;"loaded"!=a.readyState&&"complete"!=a.readyState||this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();null!=this._request.addEventListener?(this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1)):(this._request.onloadstart=this._handleLoadStartProxy,this._request.onprogress=this._handleProgressProxy,this._request.onabort=this._handleAbortProxy,this._request.onerror=this._handleErrorProxy,this._request.ontimeout=this._handleTimeoutProxy,this._request.onload=this._handleLoadProxy,this._request.onreadystatechange=this._handleReadyStateChangeProxy),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values?this._request.send(createjs.URLUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){"blob"===a&&(a=window.URL?"blob":"arraybuffer",this._responseType=a),this._request.responseType=a},a.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},a.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._handleLoadStart=function(a){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},a._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},a._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},a._handleReadyStateChange=function(a){4==this._request.readyState&&this._handleLoad()},a._handleLoad=function(a){if(!this.loaded){this.loaded=!0;var b=this._checkError();if(b)return void this._handleError(b);if(this._response=this._getResponse(),"arraybuffer"===this._responseType)try{this._response=new Blob([this._response])}catch(a){if(window.BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,"TypeError"===a.name&&window.BlobBuilder){var c=new BlobBuilder;c.append(this._response),this._response=c.getBlob()}}this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},a._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},a._checkError=function(){var a=parseInt(this._request.status);return a>=400&&a<=599?new Error(a):0==a&&/^https?:/.test(location.protocol)?new Error(0):null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.URLUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;eb.alternateExtensions.length)return null;return a=a.replace("."+c[5],"."+e),{name:d,src:a,extension:e}},b._parseSrc=function(a){var c={name:void 0,src:void 0,extension:void 0},d=b.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){c.src=a[e],c.extension=e;break}if(!c.src)return!1;var f=c.src.lastIndexOf("/");return c.name=-1!=f?c.src.slice(f+1):c.src,c},b.play=function(a,c){var d=createjs.PlayPropsConfig.create(c),e=b.createInstance(a,d.startTime,d.duration);return b._playInstance(e,d)||e._playFailed(),e},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),(f=f||b._defaultPlayPropsHash[g.src])&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(c,d,e),h.uniqueId=b._lastID++,h},b.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},b.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.position),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){if(!b._beginPlaying(a,c))return!1}else{var e=setTimeout(function(){b._beginPlaying(a,c)},c.delay);a.delayTimeoutId=e}return this._instances.push(a),!0},b._beginPlaying=function(b,c){if(!a.add(b,c.interrupt))return!1;if(!b._beginPlaying(c)){var d=createjs.indexOf(this._instances,b);return d>-1&&this._instances.splice(d,1),!1}return!0},b._getSrcById=function(a){return b._idHash[a]||{src:a}},b._playFinished=function(b){a.remove(b);var c=createjs.indexOf(this._instances,b);c>-1&&this._instances.splice(c,1)},createjs.Sound=Sound,a.channels={},a.create=function(b,c){return null==a.get(b)&&(a.channels[b]=new a(b,c),!0)},a.removeSrc=function(b){var c=a.get(b);return null!=c&&(c._removeAll(),delete a.channels[b],!0)},a.removeAll=function(){for(var b in a.channels)a.channels[b]._removeAll();a.channels={}},a.add=function(b,c){var d=a.get(b.src);return null!=d&&d._add(b,c)},a.remove=function(b){var c=a.get(b.src);return null!=c&&(c._remove(b),!0)},a.maxPerChannel=function(){return c.maxDefault},a.get=function(b){return a.channels[b]};var c=a.prototype;c.constructor=a,c.src=null,c.max=null,c.maxDefault=100,c.length=0,c.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},c._get=function(a){return this._instances[a]},c._add=function(a,b){return!!this._getSlot(b,a)&&(this._instances.push(a),this.length++,!0)},c._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1!=b&&(this._instances.splice(b,1),this.length--,!0)},c._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},c._getSlot=function(a,b){var c,d;if(a!=Sound.INTERRUPT_NONE&&null==(d=this._get(0)))return!0;for(var e=0,f=this.max;ed.position)&&(d=c))}return null!=d&&(d._interrupt(),this._remove(d),!0)},c.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractSoundInstance=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._volume=1,Object.defineProperty(this,"volume",{get:this._getVolume,set:this._setVolume}),this.getVolume=createjs.deprecate(this._getVolume,"AbstractSoundInstance.getVolume"),this.setVolume=createjs.deprecate(this._setVolume,"AbstractSoundInstance.setVolume"),this._pan=0,Object.defineProperty(this,"pan",{get:this._getPan,set:this._setPan}),this.getPan=createjs.deprecate(this._getPan,"AbstractSoundInstance.getPan"),this.setPan=createjs.deprecate(this._setPan,"AbstractSoundInstance.setPan"),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this._getStartTime,set:this._setStartTime}),this.getStartTime=createjs.deprecate(this._getStartTime,"AbstractSoundInstance.getStartTime"),this.setStartTime=createjs.deprecate(this._setStartTime,"AbstractSoundInstance.setStartTime"),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this._getDuration,set:this._setDuration}),this.getDuration=createjs.deprecate(this._getDuration,"AbstractSoundInstance.getDuration"),this.setDuration=createjs.deprecate(this._setDuration,"AbstractSoundInstance.setDuration"),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this._getPlaybackResource,set:this._setPlaybackResource}),!1!==d&&!0!==d&&this._setPlaybackResource(d),this.getPlaybackResource=createjs.deprecate(this._getPlaybackResource,"AbstractSoundInstance.getPlaybackResource"),this.setPlaybackResource=createjs.deprecate(this._setPlaybackResource,"AbstractSoundInstance.setPlaybackResource"),this._position=0,Object.defineProperty(this,"position",{get:this._getPosition,set:this._setPosition}),this.getPosition=createjs.deprecate(this._getPosition,"AbstractSoundInstance.getPosition"),this.setPosition=createjs.deprecate(this._setPosition,"AbstractSoundInstance.setPosition"),this._loop=0,Object.defineProperty(this,"loop",{get:this._getLoop,set:this._setLoop}),this.getLoop=createjs.deprecate(this._getLoop,"AbstractSoundInstance.getLoop"),this.setLoop=createjs.deprecate(this._setLoop,"AbstractSoundInstance.setLoop"),this._muted=!1,Object.defineProperty(this,"muted",{get:this._getMuted,set:this._setMuted}),this.getMuted=createjs.deprecate(this._getMuted,"AbstractSoundInstance.getMuted"),this.setMuted=createjs.deprecate(this._setMuted,"AbstractSoundInstance.setMuted"),this._paused=!1,Object.defineProperty(this,"paused",{get:this._getPaused,set:this._setPaused}),this.getPaused=createjs.deprecate(this._getPaused,"AbstractSoundInstance.getPaused"),this.setPaused=createjs.deprecate(this._setPaused,"AbstractSoundInstance.setPaused")},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a){var b=createjs.PlayPropsConfig.create(a);return this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(b),void(this._paused&&this._setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,b),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this._setPosition(a.offset),null!=a.loop&&this._setLoop(a.loop),null!=a.volume&&this._setVolume(a.volume),null!=a.pan&&this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),this},a.toString=function(){return"[AbstractSoundInstance]"},a._getPaused=function(){return this._paused},a._setPaused=function(a){if(!(!0!==a&&!1!==a||this._paused==a||1==a&&this.playState!=createjs.Sound.PLAY_SUCCEEDED))return this._paused=a,a?this._pause():this._resume(),clearTimeout(this.delayTimeoutId),this},a._setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},a._getVolume=function(){return this._volume},a._setMuted=function(a){if(!0===a||!1===a)return this._muted=a,this._updateVolume(),this},a._getMuted=function(){return this._muted},a._setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},a._getPan=function(){return this._pan},a._getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED||(this._position=this._calculateCurrentPosition()),this._position},a._setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},a._getStartTime=function(){return this._startTime},a._setStartTime=function(a){return a==this._startTime?this:(this._startTime=Math.max(0,a||0),this._updateStartTime(),this)},a._getDuration=function(){return this._duration},a._setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},a._setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._playbackResource&&this._setDurationFromSource(),this},a._getPlaybackResource=function(){return this._playbackResource},a._getLoop=function(){return this._loop},a._setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this._setPosition(a.offset),this._setLoop(a.loop),this._setVolume(a.volume),this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),null!=this._playbackResource&&this._position0)for(var e=0,f=d.length;e-1||a.indexOf("Windows Phone")>-1,BrowserDetect.isFirefox=a.indexOf("Firefox")>-1,BrowserDetect.isOpera=null!=window.opera,BrowserDetect.isChrome=a.indexOf("Chrome")>-1,BrowserDetect.isIOS=(a.indexOf("iPod")>-1||a.indexOf("iPhone")>-1||a.indexOf("iPad")>-1)&&!BrowserDetect.isWindowPhone,BrowserDetect.isAndroid=a.indexOf("Android")>-1&&!BrowserDetect.isWindowPhone,BrowserDetect.isBlackberry=a.indexOf("Blackberry")>-1,createjs.BrowserDetect=BrowserDetect}(),this.createjs=this.createjs||{},function(){"use strict";function EventDispatcher(){this._listeners=null,this._captureListeners=null}var a=EventDispatcher.prototype;EventDispatcher.initialize=function(b){b.addEventListener=a.addEventListener,b.on=a.on,b.removeEventListener=b.off=a.removeEventListener,b.removeAllEventListeners=a.removeAllEventListeners,b.hasEventListener=a.hasEventListener,b.dispatchEvent=a.dispatchEvent,b._dispatchEvent=a._dispatchEvent,b.willTrigger=a.willTrigger},a.addEventListener=function(a,b,c){var d;d=c?this._captureListeners=this._captureListeners||{}:this._listeners=this._listeners||{};var e=d[a];return e&&this.removeEventListener(a,b,c),e=d[a],e?e.push(b):d[a]=[b],b},a.on=function(a,b,c,d,e,f){return b.handleEvent&&(c=c||b,b=b.handleEvent),c=c||this,this.addEventListener(a,function(a){b.call(c,a,e),d&&a.remove()},f)},a.removeEventListener=function(a,b,c){var d=c?this._captureListeners:this._listeners;if(d){var e=d[a];if(e)for(var f=0,g=e.length;f=0&&!a.propagationStopped;g--)f[g]._dispatchEvent(a,1+(0==g));for(g=1;g-1&&(b=b.substr(0,f));var g;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(g=b.match(a.EXTENSION_PATT))&&(c.extension=g[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildURI=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=createjs.Elements.a();b.href=a.src;var c=createjs.Elements.a();return c.href=location.href,""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname)},a.isLocal=function(a){var b=createjs.Elements.a();return b.href=a.src,""==b.hostname&&"file:"==b.protocol},createjs.URLUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;try{Object.defineProperties(b,{POST:{get:createjs.deprecate(function(){return createjs.Methods.POST},"AbstractLoader.POST")},GET:{get:createjs.deprecate(function(){return createjs.Methods.GET},"AbstractLoader.GET")},BINARY:{get:createjs.deprecate(function(){return createjs.Types.BINARY},"AbstractLoader.BINARY")},CSS:{get:createjs.deprecate(function(){return createjs.Types.CSS},"AbstractLoader.CSS")},FONT:{get:createjs.deprecate(function(){return createjs.Types.FONT},"AbstractLoader.FONT")},FONTCSS:{get:createjs.deprecate(function(){return createjs.Types.FONTCSS},"AbstractLoader.FONTCSS")},IMAGE:{get:createjs.deprecate(function(){return createjs.Types.IMAGE},"AbstractLoader.IMAGE")},JAVASCRIPT:{get:createjs.deprecate(function(){return createjs.Types.JAVASCRIPT},"AbstractLoader.JAVASCRIPT")},JSON:{get:createjs.deprecate(function(){return createjs.Types.JSON},"AbstractLoader.JSON")},JSONP:{get:createjs.deprecate(function(){return createjs.Types.JSONP},"AbstractLoader.JSONP")},MANIFEST:{get:createjs.deprecate(function(){return createjs.Types.MANIFEST},"AbstractLoader.MANIFEST")},SOUND:{get:createjs.deprecate(function(){return createjs.Types.SOUND},"AbstractLoader.SOUND")},VIDEO:{get:createjs.deprecate(function(){return createjs.Types.VIDEO},"AbstractLoader.VIDEO")},SPRITESHEET:{get:createjs.deprecate(function(){return createjs.Types.SPRITESHEET},"AbstractLoader.SPRITESHEET")},SVG:{get:createjs.deprecate(function(){return createjs.Types.SVG},"AbstractLoader.SVG")},TEXT:{get:createjs.deprecate(function(){return createjs.Types.TEXT},"AbstractLoader.TEXT")},XML:{get:createjs.deprecate(function(){return createjs.Types.XML},"AbstractLoader.XML")}})}catch(a){}a.getItem=function(){return this._item},a.getResult=function(a){return a?this._rawResult:this._result},a.getTag=function(){return this._tag},a.setTag=function(a){this._tag=a},a.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},a.cancel=function(){this.canceled=!0,this.destroy()},a.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},a.getLoadedItems=function(){return this._loadedItems},a._createRequest=function(){this._preferXHR?this._request=new createjs.XHRRequest(this._item):this._request=new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._createTag=function(a){return null},a._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},a._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||this.progress==1/0)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},a._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},a._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a?a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"):void 0===a.type&&(a=new createjs.ErrorEvent(a.message)),this.dispatchEvent(a))},a._isCanceled=function(){return this.canceled},a.resultFormatter=null,a.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this);b instanceof Function?b.call(this,createjs.proxy(this._resultFormatSuccess,this),createjs.proxy(this._resultFormatFailed,this)):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_"+a.type.toUpperCase()+"_ERROR"))}},a._resultFormatSuccess=function(a){this._result=a,this._sendComplete()},a._resultFormatFailed=function(a){this._sendError(a)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",this.on("initialize",this._updateXHR,this)}var a=createjs.extend(AbstractMediaLoader,createjs.AbstractLoader);a.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},a._createTag=function(){},a._createRequest=function(){this._preferXHR?this._request=new createjs.XHRRequest(this._item):this._request=new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._updateXHR=function(a){a.loader.setResponseType&&a.loader.setResponseType("blob")},a._formatResult=function(a){if(this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR){var b=window.URL||window.webkitURL,c=a.getResult(!0);a.getTag().src=b.createObjectURL(c)}return a.getTag()},createjs.AbstractMediaLoader=createjs.promote(AbstractMediaLoader,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractRequest=function(a){this._item=a},a=createjs.extend(AbstractRequest,createjs.EventDispatcher);a.load=function(){},a.destroy=function(){},a.cancel=function(){},createjs.AbstractRequest=createjs.promote(AbstractRequest,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function TagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(createjs.DomUtils.appendToBody(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;"loaded"!=a.readyState&&"complete"!=a.readyState||this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;"loaded"!=a.readyState&&"complete"!=a.readyState||this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();null!=this._request.addEventListener?(this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1)):(this._request.onloadstart=this._handleLoadStartProxy,this._request.onprogress=this._handleProgressProxy,this._request.onabort=this._handleAbortProxy,this._request.onerror=this._handleErrorProxy,this._request.ontimeout=this._handleTimeoutProxy,this._request.onload=this._handleLoadProxy,this._request.onreadystatechange=this._handleReadyStateChangeProxy),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values?this._request.send(createjs.URLUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){"blob"===a&&(a=window.URL?"blob":"arraybuffer",this._responseType=a),this._request.responseType=a},a.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},a.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._handleLoadStart=function(a){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},a._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},a._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},a._handleReadyStateChange=function(a){4==this._request.readyState&&this._handleLoad()},a._handleLoad=function(a){if(!this.loaded){this.loaded=!0;var b=this._checkError();if(b)return void this._handleError(b);if(this._response=this._getResponse(),"arraybuffer"===this._responseType)try{this._response=new Blob([this._response])}catch(a){if(window.BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,"TypeError"===a.name&&window.BlobBuilder){var c=new BlobBuilder;c.append(this._response),this._response=c.getBlob()}}this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},a._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},a._checkError=function(){var a=parseInt(this._request.status);return a>=400&&a<=599?new Error(a):0==a&&/^https?:/.test(location.protocol)?new Error(0):null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.URLUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;eb.alternateExtensions.length)return null;return a=a.replace("."+c[5],"."+e),{name:d,src:a,extension:e}},b._parseSrc=function(a){var c={name:void 0,src:void 0,extension:void 0},d=b.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){c.src=a[e],c.extension=e;break}if(!c.src)return!1;var f=c.src.lastIndexOf("/");return c.name=-1!=f?c.src.slice(f+1):c.src,c},b.play=function(a,c){var d=createjs.PlayPropsConfig.create(c),e=b.createInstance(a,d.startTime,d.duration);return b._playInstance(e,d)||e._playFailed(),e},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),(f=f||b._defaultPlayPropsHash[g.src])&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(c,d,e),h.uniqueId=b._lastID++,h},b.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},b.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.position),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){if(!b._beginPlaying(a,c))return!1}else{var e=setTimeout(function(){b._beginPlaying(a,c)},c.delay);a.delayTimeoutId=e}return this._instances.push(a),!0},b._beginPlaying=function(b,c){if(!a.add(b,c.interrupt))return!1;if(!b._beginPlaying(c)){var d=createjs.indexOf(this._instances,b);return d>-1&&this._instances.splice(d,1),!1}return!0},b._getSrcById=function(a){return b._idHash[a]||{src:a}},b._playFinished=function(b){a.remove(b);var c=createjs.indexOf(this._instances,b);c>-1&&this._instances.splice(c,1)},createjs.Sound=Sound,a.channels={},a.create=function(b,c){return null==a.get(b)&&(a.channels[b]=new a(b,c),!0)},a.removeSrc=function(b){var c=a.get(b);return null!=c&&(c._removeAll(),delete a.channels[b],!0)},a.removeAll=function(){for(var b in a.channels)a.channels[b]._removeAll();a.channels={}},a.add=function(b,c){var d=a.get(b.src);return null!=d&&d._add(b,c)},a.remove=function(b){var c=a.get(b.src);return null!=c&&(c._remove(b),!0)},a.maxPerChannel=function(){return c.maxDefault},a.get=function(b){return a.channels[b]};var c=a.prototype;c.constructor=a,c.src=null,c.max=null,c.maxDefault=100,c.length=0,c.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},c._get=function(a){return this._instances[a]},c._add=function(a,b){return!!this._getSlot(b,a)&&(this._instances.push(a),this.length++,!0)},c._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1!=b&&(this._instances.splice(b,1),this.length--,!0)},c._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},c._getSlot=function(a,b){var c,d;if(a!=Sound.INTERRUPT_NONE&&null==(d=this._get(0)))return!0;for(var e=0,f=this.max;ed.position)&&(d=c))}return null!=d&&(d._interrupt(),this._remove(d),!0)},c.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractSoundInstance=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this.startAt=null,this.scheduledAt=null,this._volume=1,Object.defineProperty(this,"volume",{get:this._getVolume,set:this._setVolume}),this.getVolume=createjs.deprecate(this._getVolume,"AbstractSoundInstance.getVolume"),this.setVolume=createjs.deprecate(this._setVolume,"AbstractSoundInstance.setVolume"),this._pan=0,Object.defineProperty(this,"pan",{get:this._getPan,set:this._setPan}),this.getPan=createjs.deprecate(this._getPan,"AbstractSoundInstance.getPan"),this.setPan=createjs.deprecate(this._setPan,"AbstractSoundInstance.setPan"),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this._getStartTime,set:this._setStartTime}),this.getStartTime=createjs.deprecate(this._getStartTime,"AbstractSoundInstance.getStartTime"),this.setStartTime=createjs.deprecate(this._setStartTime,"AbstractSoundInstance.setStartTime"),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this._getDuration,set:this._setDuration}),this.getDuration=createjs.deprecate(this._getDuration,"AbstractSoundInstance.getDuration"),this.setDuration=createjs.deprecate(this._setDuration,"AbstractSoundInstance.setDuration"),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this._getPlaybackResource,set:this._setPlaybackResource}),!1!==d&&!0!==d&&this._setPlaybackResource(d),this.getPlaybackResource=createjs.deprecate(this._getPlaybackResource,"AbstractSoundInstance.getPlaybackResource"),this.setPlaybackResource=createjs.deprecate(this._setPlaybackResource,"AbstractSoundInstance.setPlaybackResource"),this._position=0,Object.defineProperty(this,"position",{get:this._getPosition,set:this._setPosition}),this.getPosition=createjs.deprecate(this._getPosition,"AbstractSoundInstance.getPosition"),this.setPosition=createjs.deprecate(this._setPosition,"AbstractSoundInstance.setPosition"),this._loop=0,Object.defineProperty(this,"loop",{get:this._getLoop,set:this._setLoop}),this.getLoop=createjs.deprecate(this._getLoop,"AbstractSoundInstance.getLoop"),this.setLoop=createjs.deprecate(this._setLoop,"AbstractSoundInstance.setLoop"),this._muted=!1,Object.defineProperty(this,"muted",{get:this._getMuted,set:this._setMuted}),this.getMuted=createjs.deprecate(this._getMuted,"AbstractSoundInstance.getMuted"),this.setMuted=createjs.deprecate(this._setMuted,"AbstractSoundInstance.setMuted"),this._paused=!1,Object.defineProperty(this,"paused",{get:this._getPaused,set:this._setPaused}),this.getPaused=createjs.deprecate(this._getPaused,"AbstractSoundInstance.getPaused"),this.setPaused=createjs.deprecate(this._setPaused,"AbstractSoundInstance.setPaused")},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a){var b=createjs.PlayPropsConfig.create(a);return this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(b),void(this._paused&&this._setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,b),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this._setPosition(a.offset),null!=a.loop&&this._setLoop(a.loop),null!=a.volume&&this._setVolume(a.volume),null!=a.pan&&this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),null!=a.startAt&&(this.startAt=a.startAt),this},a.toString=function(){return"[AbstractSoundInstance]"},a._getPaused=function(){return this._paused},a._setPaused=function(a){if(!(!0!==a&&!1!==a||this._paused==a||1==a&&this.playState!=createjs.Sound.PLAY_SUCCEEDED))return this._paused=a,a?this._pause():this._resume(),clearTimeout(this.delayTimeoutId),this},a._setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},a._getVolume=function(){return this._volume},a._setMuted=function(a){if(!0===a||!1===a)return this._muted=a,this._updateVolume(),this},a._getMuted=function(){return this._muted},a._setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},a._getPan=function(){return this._pan},a._getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED||(this._position=this._calculateCurrentPosition()),this._position},a._setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},a._getStartTime=function(){return this._startTime},a._setStartTime=function(a){return a==this._startTime?this:(this._startTime=Math.max(0,a||0),this._updateStartTime(),this)},a._getDuration=function(){return this._duration},a._setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},a._setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._playbackResource&&this._setDurationFromSource(),this},a._getPlaybackResource=function(){return this._playbackResource},a._getLoop=function(){return this._loop},a._setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this._setPosition(a.offset),this._setLoop(a.loop),this._setVolume(a.volume),this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),null!=a.startAt&&(this.startAt=a.startAt),null!=this._playbackResource&&this._position0)for(var e=0,f=d.length;ec?d:c;this.scheduledAt=e,this.nativeLoop=!1;var f=.001*this._duration,g=Math.min(.001*Math.max(0,this._position),f);if(this._loop<0&&b.nativeLoops)return void this._startNativeLoop(e,g,f);this.sourceNode=this._createAndPlayAudioNode(e-f,g),this._playbackStartTime=this.sourceNode.startTime-g,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(f-g+(e-c))),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},a._startNativeLoop=function(a,c,d){c>=d&&(c=0);var e=.001*this._startTime,f=b.context.createBufferSource();f.buffer=this.playbackResource,f.connect(this.panNode),f.loop=!0,f.loopStart=e,f.loopEnd=e+d,f.startTime=a,f.start(a,e+c),this.sourceNode=f,this._sourceNodeNext=null,this._playbackStartTime=a-c,this.nativeLoop=!0},a._createAndPlayAudioNode=function(a,c){var d=b.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,c+.001*this._startTime,e-c),d},a._pause=function(){this._position=this._calculateCurrentPosition(),this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout)},a._resume=function(){this._handleSoundReady()},a._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},a._calculateCurrentPosition=function(){var a=1e3*(b.context.currentTime-this._playbackStartTime);return this.nativeLoop&&this._duration>0&&a>=this._duration&&(a%=this._duration),a},a._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},a._handleLoop=function(){this._cleanUpAudioNode(this.sourceNode),this.sourceNode=this._sourceNodeNext,this._playbackStartTime=this.sourceNode.startTime,this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0),this._soundCompleteTimeout=setTimeout(this._endedHandler,this._duration)},a._updateDuration=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._pause(),this._resume())},createjs.WebAudioSoundInstance=createjs.promote(WebAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioPlugin(){this.AbstractPlugin_constructor(),this._panningModel=b._panningModel,this.context=b.context,this.dynamicsCompressorNode=this.context.createDynamicsCompressor(),this.dynamicsCompressorNode.connect(this.context.destination),this.gainNode=this.context.createGain(),this.gainNode.connect(this.dynamicsCompressorNode),createjs.WebAudioSoundInstance.destinationNode=this.gainNode,this._capabilities=b._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var a=createjs.extend(WebAudioPlugin,createjs.AbstractPlugin),b=WebAudioPlugin;b._capabilities=null,b._panningModel="equalpower",b.context=null,b._scratchBuffer=null,b._unlocked=!1,b.DEFAULT_SAMPLE_RATE=44100,b.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return!("file:"==location.protocol&&!a&&!this._isFileXHRSupported())&&(b._generateCapabilities(),null!=b.context)},b.playEmptySound=function(){if(null!=b.context){var a=b.context.createBufferSource();a.buffer=b._scratchBuffer,a.connect(b.context.destination),a.start(0,0,0)}},b._isFileXHRSupported=function(){return document.location.host},b._generateCapabilities=function(){if(null==b._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;if(null==b.context&&(b.context=b._createAudioContext(),null==b.context))return null;null==b._scratchBuffer&&(b._scratchBuffer=b.context.createBuffer(1,1,22050)),b._compatibilitySetUp(),"ontouchstart"in window&&"running"!=b.context.state&&(b._unlock(),document.addEventListener("mousedown",b._unlock,!0),document.addEventListener("touchstart",b._unlock,!0),document.addEventListener("touchend",b._unlock,!0)),b._capabilities={panning:!0,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;epan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
+ * startAt - Web Audio only: the AudioContext time in seconds to start playback at (null or past = now).
*
*
* Example
@@ -138,6 +139,17 @@ this.createjs = this.createjs || {};
* @default null
*/
this.duration = null;
+
+ /**
+ * Web Audio only: the AudioContext time (in seconds) at which playback should start. A value in the
+ * past, or null, starts playback immediately. Starting in the future is sample-accurate, which a
+ * "now" start is not on devices with a coarse render quantum.
+ * @property startAt
+ * @type {number}
+ * @default null
+ * @since 1.1.0
+ */
+ this.startAt = null;
};
var p = PlayPropsConfig.prototype = {};
var s = PlayPropsConfig;
diff --git a/src/soundjs/webaudio/WebAudioSoundInstance.js b/src/soundjs/webaudio/WebAudioSoundInstance.js
index 29d30b5d..84142644 100644
--- a/src/soundjs/webaudio/WebAudioSoundInstance.js
+++ b/src/soundjs/webaudio/WebAudioSoundInstance.js
@@ -88,6 +88,16 @@ this.createjs = this.createjs || {};
*/
this.sourceNode = null;
+ /**
+ * Whether the current play is an infinite loop on a single, natively looping source node
+ * (see {{#crossLink "WebAudioSoundInstance/nativeLoops:property"}}{{/crossLink}}).
+ * @property nativeLoop
+ * @type {Boolean}
+ * @default false
+ * @since 1.1.0
+ */
+ this.nativeLoop = false;
+
// private properties
/**
@@ -160,6 +170,21 @@ this.createjs = this.createjs || {};
*/
s.destinationNode = null;
+ /**
+ * Play infinite loops (loop = -1) on ONE source node with loop = true instead of a chain of
+ * one node per repetition. The chain is refilled from a timer armed relative to when it last ran, so
+ * main-thread stalls accumulate and once they add up to a loop length the next repetition is created
+ * after it should have started: an audible gap. A native loop involves no JS while it plays. It
+ * dispatches no "loop" event, and its {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}}
+ * wraps to the repetition in progress. Finite loop counts always use the chain.
+ * @property nativeLoops
+ * @type {Boolean}
+ * @default true
+ * @static
+ * @since 1.1.0
+ */
+ s.nativeLoops = true;
+
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
@@ -193,6 +218,15 @@ this.createjs = this.createjs || {};
};
p._removeLooping = function(value) {
+ if (this.nativeLoop && this.sourceNode) {
+ // Let the repetition in progress finish, then complete.
+ this.sourceNode.loop = false;
+ var remainingMs = this._duration - this._calculateCurrentPosition();
+ clearTimeout(this._soundCompleteTimeout);
+ this._soundCompleteTimeout = setTimeout(this._endedHandler, Math.max(0, remainingMs));
+ this.nativeLoop = false;
+ return;
+ }
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
@@ -217,6 +251,7 @@ this.createjs = this.createjs || {};
clearTimeout(this._soundCompleteTimeout);
this._playbackStartTime = 0; // This is used by _getPosition
+ this.nativeLoop = false;
};
/**
@@ -244,18 +279,61 @@ this.createjs = this.createjs || {};
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
+ // A future startAt is honoured sample-accurately; null or past = now.
+ var now = s.context.currentTime;
+ var startAt = this.startAt;
+ this.startAt = null;
+ var at = (typeof startAt === "number" && startAt > now) ? startAt : now;
+ this.scheduledAt = at;
+ this.nativeLoop = false;
+
var dur = this._duration * 0.001,
pos = Math.min(Math.max(0, this._position) * 0.001, dur);
- this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
+
+ if (this._loop < 0 && s.nativeLoops) {
+ this._startNativeLoop(at, pos, dur);
+ return;
+ }
+
+ this.sourceNode = this._createAndPlayAudioNode((at - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
- this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
+ this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos + (at - now)) * 1000);
if(this._loop != 0) {
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
}
};
+ /**
+ * Start an infinite loop on one natively looping source node (see
+ * {{#crossLink "WebAudioSoundInstance/nativeLoops:property"}}{{/crossLink}}).
+ * @method _startNativeLoop
+ * @param {Number} at The context time to start at, in seconds.
+ * @param {Number} pos The position in the sound to start at, in seconds.
+ * @param {Number} dur The duration of the sound, in seconds.
+ * @protected
+ * @since 1.1.0
+ */
+ p._startNativeLoop = function (at, pos, dur) {
+ if (pos >= dur) { pos = 0; }
+ var spriteStart = this._startTime * 0.001;
+
+ var audioNode = s.context.createBufferSource();
+ audioNode.buffer = this.playbackResource;
+ audioNode.connect(this.panNode);
+ audioNode.loop = true;
+ audioNode.loopStart = spriteStart;
+ audioNode.loopEnd = spriteStart + dur;
+ audioNode.startTime = at;
+ audioNode.start(at, spriteStart + pos);
+
+ this.sourceNode = audioNode;
+ this._sourceNodeNext = null;
+ this._playbackStartTime = at - pos;
+ this.nativeLoop = true;
+ };
+
/**
* Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
* @method _createAndPlayAudioNode
@@ -276,7 +354,7 @@ this.createjs = this.createjs || {};
};
p._pause = function () {
- this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
+ this._position = this._calculateCurrentPosition(); // lets us restart at same point (wrapped for a native loop)
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
@@ -303,7 +381,11 @@ this.createjs = this.createjs || {};
};
p._calculateCurrentPosition = function () {
- return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
+ var ms = (s.context.currentTime - this._playbackStartTime) * 1000; // pos in seconds * 1000 to give milliseconds
+ if (this.nativeLoop && this._duration > 0 && ms >= this._duration) {
+ ms = ms % this._duration; // the repetition in progress
+ }
+ return ms;
};
p._updatePosition = function () {