diff --git a/vendor/scripts/easeljs-NEXT.combined.js b/vendor/scripts/easeljs-NEXT.combined.js index a6b16da20..f25f23e63 100644 --- a/vendor/scripts/easeljs-NEXT.combined.js +++ b/vendor/scripts/easeljs-NEXT.combined.js @@ -364,6 +364,7 @@ var p = EventDispatcher.prototype; target.hasEventListener = p.hasEventListener; target.dispatchEvent = p.dispatchEvent; target._dispatchEvent = p._dispatchEvent; + target.willTrigger = p.willTrigger; }; // constructor: @@ -558,7 +559,7 @@ var p = EventDispatcher.prototype; eventObj = new createjs.Event(eventObj); } // TODO: deprecated. Target param is deprecated, only use case is MouseEvent/mousemove, remove. - eventObj.target = target||this; + try { eventObj.target = target||this; } catch (e) {} // allows redispatching of native events if (!eventObj.bubbles || !this.parent) { this._dispatchEvent(eventObj, 2); @@ -580,7 +581,7 @@ var p = EventDispatcher.prototype; }; /** - * Indicates whether there is at least one listener for the specified event type and `useCapture` value. + * Indicates whether there is at least one listener for the specified event type. * @method hasEventListener * @param {String} type The string type of the event. * @return {Boolean} Returns true if there is at least one listener for the specified event. @@ -589,6 +590,26 @@ var p = EventDispatcher.prototype; var listeners = this._listeners, captureListeners = this._captureListeners; return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type])); }; + + /** + * Indicates whether there is at least one listener for the specified event type on this object or any of its + * ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the + * specified type is dispatched from this object, it will trigger at least one listener. + * + * This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire + * event flow for a listener, not just this object. + * @method willTrigger + * @param {String} type The string type of the event. + * @return {Boolean} Returns `true` if there is at least one listener for the specified event. + **/ + p.willTrigger = function(type) { + var o = this; + while (o) { + if (o.hasEventListener(type)) { return true; } + o = o.parent; + } + return false; + }; /** * @method toString @@ -610,8 +631,8 @@ var p = EventDispatcher.prototype; if (eventObj && listeners) { var arr = listeners[eventObj.type]; if (!arr||!(l=arr.length)) { return; } - eventObj.currentTarget = this; - eventObj.eventPhase = eventPhase; + try { eventObj.currentTarget = this; } catch (e) {} + try { eventObj.eventPhase = eventPhase; } catch (e) {} eventObj.removed = false; arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) { @@ -1425,7 +1446,6 @@ this.createjs = this.createjs||{}; (function() { "use strict"; -// TODO: deprecated. @uses EventDispatcher /** * Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties, * see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings. @@ -1441,7 +1461,6 @@ this.createjs = this.createjs||{}; * @param {Number} rawX The raw x position relative to the stage. * @param {Number} rawY The raw y position relative to the stage. * @extends Event - * @uses EventDispatcher * @constructor **/ var MouseEvent = function(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY) { @@ -1450,9 +1469,9 @@ var MouseEvent = function(type, bubbles, cancelable, stageX, stageY, nativeEvent var p = MouseEvent.prototype = new createjs.Event(); // events: - + // TODO: deprecated. /** - * For MouseEvent objects of type "mousedown", mousemove events will be dispatched from the event object until the + * REMOVED. For MouseEvent objects of type "mousedown", mousemove events will be dispatched from the event object until the * user releases the mouse anywhere. This enables you to listen to mouse move interactions for the duration of a * press, which can be very useful for operations such as drag and drop. * @@ -1463,7 +1482,7 @@ var p = MouseEvent.prototype = new createjs.Event(); */ /** - * For MouseEvent objects of type "mousedown", a mouseup event will be dispatched from the event object when the + * REMOVED. For MouseEvent objects of type "mousedown", a mouseup event will be dispatched from the event object when the * user releases the mouse anywhere. This enables you to listen for a corresponding mouse up from a specific press, * which can be very useful for operations such as drag and drop. * @@ -1546,17 +1565,6 @@ var p = MouseEvent.prototype = new createjs.Event(); * @type {Boolean} */ p.primary = false; - -// mix-ins: - // EventDispatcher methods: - // TODO: deprecated: - p.addEventListener = null; - p.removeEventListener = null; - p.removeAllEventListeners = null; - p.dispatchEvent = null; - p.hasEventListener = null; - p._listeners = null; - createjs.EventDispatcher.initialize(p); // inject EventDispatcher methods. // getter / setters: /** @@ -1785,6 +1793,14 @@ var p = Matrix2D.prototype; * @type String **/ p.compositeOperation = null; + + /** + * Property representing the value for visible that will be applied to a display object. This is not part of matrix + * operations, but is used for operations like getConcatenatedMatrix to provide concatenated visible values. + * @property visible + * @type Boolean + **/ + p.visible = true; // constructor: /** @@ -1869,7 +1885,7 @@ var p = Matrix2D.prototype; **/ p.prependMatrix = function(matrix) { this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); - this.prependProperties(matrix.alpha, matrix.shadow, matrix.compositeOperation); + this.prependProperties(matrix.alpha, matrix.shadow, matrix.compositeOperation, matrix.visible); return this; }; @@ -1881,7 +1897,7 @@ var p = Matrix2D.prototype; **/ p.appendMatrix = function(matrix) { this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty); - this.appendProperties(matrix.alpha, matrix.shadow, matrix.compositeOperation); + this.appendProperties(matrix.alpha, matrix.shadow, matrix.compositeOperation, matrix.visible); return this; }; @@ -2047,6 +2063,7 @@ var p = Matrix2D.prototype; this.alpha = this.a = this.d = 1; this.b = this.c = this.tx = this.ty = 0; this.shadow = this.compositeOperation = null; + this.visible = true; return this; }; @@ -2141,13 +2158,15 @@ var p = Matrix2D.prototype; * @param {Number} [alpha=1] desired alpha value * @param {Shadow} [shadow=null] desired shadow value * @param {String} [compositeOperation=null] desired composite operation value + * @param {Boolean} [visible=true] desired visible value * @return {Matrix2D} This matrix. Useful for chaining method calls. */ - p.reinitialize = function(a, b, c, d, tx, ty, alpha, shadow, compositeOperation) { + p.reinitialize = function(a, b, c, d, tx, ty, alpha, shadow, compositeOperation, visible) { this.initialize(a,b,c,d,tx,ty); this.alpha = alpha == null ? 1 : alpha; this.shadow = shadow; this.compositeOperation = compositeOperation; + this.visible = visible == null ? true : visible; return this; }; @@ -2158,7 +2177,7 @@ var p = Matrix2D.prototype; * @return {Matrix2D} This matrix. Useful for chaining method calls. */ p.copy = function(matrix) { - return this.reinitialize(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty, matrix.alpha, matrix.shadow, matrix.compositeOperation); + return this.reinitialize(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty, matrix.alpha, matrix.shadow, matrix.compositeOperation, matrix.visible); }; /** @@ -2167,12 +2186,14 @@ var p = Matrix2D.prototype; * @param {Number} alpha desired alpha value * @param {Shadow} shadow desired shadow value * @param {String} compositeOperation desired composite operation value + * @param {Boolean} visible desired visible value * @return {Matrix2D} This matrix. Useful for chaining method calls. */ - p.appendProperties = function(alpha, shadow, compositeOperation) { + p.appendProperties = function(alpha, shadow, compositeOperation, visible) { this.alpha *= alpha; this.shadow = shadow || this.shadow; this.compositeOperation = compositeOperation || this.compositeOperation; + this.visible = this.visible && visible; return this; }; @@ -2182,12 +2203,14 @@ var p = Matrix2D.prototype; * @param {Number} alpha desired alpha value * @param {Shadow} shadow desired shadow value * @param {String} compositeOperation desired composite operation value + * @param {Boolean} visible desired visible value * @return {Matrix2D} This matrix. Useful for chaining method calls. */ - p.prependProperties = function(alpha, shadow, compositeOperation) { + p.prependProperties = function(alpha, shadow, compositeOperation, visible) { this.alpha *= alpha; this.shadow = this.shadow || shadow; this.compositeOperation = this.compositeOperation || compositeOperation; + this.visible = this.visible && visible; return this; }; @@ -2576,6 +2599,52 @@ var p = ButtonHelper.prototype; * @type Boolean **/ p.play = false; + +// getter / setters: + + /** + * Enables or disables the button functionality on the target. + * @property enabled + * @type {Boolean} + **/ + /** + * Enables or disables the button functionality on the target. + * @deprecated in favour of the enabled property. + * @method setEnabled + * @param {Boolean} value + **/ + p.setEnabled = function(value) { // TODO: deprecated. + var o = this.target; + this._enabled = value; + if (value) { + o.cursor = "pointer"; + o.addEventListener("rollover", this); + o.addEventListener("rollout", this); + o.addEventListener("mousedown", this); + o.addEventListener("pressup", this); + } else { + o.cursor = null; + o.removeEventListener("rollover", this); + o.removeEventListener("rollout", this); + o.removeEventListener("mousedown", this); + o.removeEventListener("pressup", this); + } + }; + /** + * Returns enabled state of this instance. + * @deprecated in favour of the enabled property. + * @method getEnabled + * @return {Boolean} The last value passed to setEnabled(). + **/ + p.getEnabled = function() { + return this._enabled; + }; + + try { + Object.defineProperties(p, { + enabled: { get: p.getEnabled, set: p.setEnabled } + }); + } catch (e) {} // TODO: use Log // private properties /** @@ -2592,6 +2661,13 @@ var p = ButtonHelper.prototype; **/ p._isOver = false; + /** + * @property _enabled + * @type Boolean + * @protected + **/ + p._enabled = false; + // constructor: /** * Initialization method. @@ -2612,7 +2688,7 @@ var p = ButtonHelper.prototype; p.initialize = function(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) { if (!target.addEventListener) { return; } this.target = target; - target.cursor = "pointer"; + target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes. this.overLabel = overLabel == null ? "over" : overLabel; this.outLabel = outLabel == null ? "out" : outLabel; this.downLabel = downLabel == null ? "down" : downLabel; @@ -2629,25 +2705,6 @@ var p = ButtonHelper.prototype; }; // public methods: - /** - * Enables or disables the button functionality on the target. - * @method setEnabled - * @param {Boolean} value - **/ - p.setEnabled = function(value) { - var o = this.target; - if (value) { - o.addEventListener("rollover", this); - o.addEventListener("rollout", this); - o.addEventListener("mousedown", this); - o.addEventListener("pressup", this); - } else { - o.removeEventListener("rollover", this); - o.removeEventListener("rollout", this); - o.removeEventListener("mousedown", this); - o.removeEventListener("pressup", this); - } - }; /** * Returns a string representation of this object. @@ -2667,7 +2724,6 @@ var p = ButtonHelper.prototype; **/ p.handleEvent = function(evt) { var label, t = this.target, type = evt.type; - if (type == "mousedown") { this._isPressed = true; label = this.downLabel; @@ -2958,6 +3014,11 @@ this.createjs = this.createjs||{}; * var spriteSheet = new createjs.SpriteSheet(data); * var animation = new createjs.Sprite(spriteSheet, "run"); * + * + * <strong>Warning:</strong> Images loaded cross-origin will throw cross-origin security errors when interacted with + * using a mouse, using methods such as `getObjectUnderPoint`, using filters, or caching. You can get around this by + * setting `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";` + * * @class SpriteSheet * @constructor * @param {Object} data An object describing the SpriteSheet data. @@ -4844,6 +4905,17 @@ var DisplayObject = function() { this.initialize(); }; var p = DisplayObject.prototype = new createjs.EventDispatcher(); + + +// static properties: + /** + * Listing of mouse event names. Used in _hasMouseEventListener. + * @property _MOUSE_EVENTS + * @protected + * @static + * @type {Array} + **/ + DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"]; /** * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}} @@ -4854,6 +4926,15 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); * @default false **/ DisplayObject.suppressCrossDomainErrors = false; + + /** + * @property _snapToPixelEnabled + * @protected + * @static + * @type {Boolean} + * @default false + **/ + DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access. /** * @property _hitTestCanvas @@ -4861,7 +4942,6 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); * @static * @protected **/ - /** * @property _hitTestContext * @type {CanvasRenderingContext2D} @@ -5170,21 +5250,14 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); p.compositeOperation = null; /** - * Indicates whether the display object should have its x & y position rounded prior to drawing it to stage. - * Snapping to whole pixels can result in a sharper and faster draw for images (ex. Bitmap & cached objects). - * This only applies if the enclosing stage has {{#crossLink "Stage/snapPixelsEnabled:property"}}{{/crossLink}} set - * to `true`. The snapToPixel property is `true` by default for {{#crossLink "Bitmap"}}{{/crossLink}} and {{#crossLink "Sprite"}}{{/crossLink}} - * instances, and `false` for all other display objects. - * - * Note that this applies only rounds the display object's local position. You should ensure that all of the display - * object's ancestors (parent containers) are also on a whole pixel. You can do this by setting the ancestors' - * snapToPixel property to `true`. + * Indicates whether the display object should be drawn to a whole pixel when + * {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole + * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true). * @property snapToPixel * @type {Boolean} - * @default false - * @deprecated Hardware acceleration in modern browsers makes this unnecessary. + * @default true **/ - p.snapToPixel = false; + p.snapToPixel = true; // TODO: remove handler docs in future: /** @@ -5423,9 +5496,12 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); } mtx = o._matrix.identity().appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY); - // TODO: should be a better way to manage this setting. For now, using dynamic access to avoid circular dependencies: - if (createjs["Stage"]._snapToPixelEnabled && o.snapToPixel) { ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx+0.5|0, mtx.ty+0.5|0); } - else { ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); } + var tx = mtx.tx, ty = mtx.ty; + if (DisplayObject._snapToPixelEnabled && o.snapToPixel) { + tx = tx + (tx < 0 ? -0.5 : 0.5) | 0; + ty = ty + (ty < 0 ? -0.5 : 0.5) | 0; + } + ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, tx, ty); ctx.globalAlpha *= o.alpha; if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; } if (o.shadow) { this._applyShadow(ctx, o.shadow); } @@ -5702,7 +5778,7 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); else { matrix = new createjs.Matrix2D(); } var o = this; while (o != null) { - matrix.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY).prependProperties(o.alpha, o.shadow, o.compositeOperation); + matrix.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY).prependProperties(o.alpha, o.shadow, o.compositeOperation, o.visible); o = o.parent; } return matrix; @@ -6042,9 +6118,24 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher(); return bounds.initialize(minX, minY, maxX-minX, maxY-minY); }; + + /** + * Indicates whether the display object has any mouse event listeners or a cursor. + * @method _isMouseOpaque + * @return {Boolean} + * @protected + **/ + p._hasMouseEventListener = function() { + var evts = DisplayObject._MOUSE_EVENTS; + for (var i= 0, l=evts.length; i<l; i++) { + if (this.hasEventListener(evts[i])) { return true; } + } + return !!this.cursor; + }; createjs.DisplayObject = DisplayObject; -}());/* +}()); +/* * Container * Visit http://createjs.com/ for documentation, updates and examples. * @@ -6607,25 +6698,30 @@ var p = Container.prototype = new createjs.DisplayObject(); * @param {Number} x * @param {Number} y * @param {Array} arr - * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and hitArea. + * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners. + * @param {Boolean} activeListener If true, there is an active mouse event listener. * @return {Array} * @protected **/ - p._getObjectsUnderPoint = function(x, y, arr, mouse) { + p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener) { var ctx = createjs.DisplayObject._hitTestContext; var mtx = this._matrix; + activeListener = activeListener || (mouse&&this._hasMouseEventListener()); // draw children one at a time, and check if we get a hit: - var l = this.children.length; + var children = this.children; + var l = children.length; for (var i=l-1; i>=0; i--) { - var child = this.children[i]; - var hitArea = mouse&&child.hitArea; + var child = children[i]; + var hitArea = child.hitArea; if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; } // if a child container has a hitArea then we only need to check its hitArea, so we can treat it as a normal DO: if (!hitArea && child instanceof Container) { - var result = child._getObjectsUnderPoint(x, y, arr, mouse); + var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener); if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; } } else { + if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; } + child.getConcatenatedMatrix(mtx); if (hitArea) { @@ -6742,17 +6838,6 @@ var Stage = function(canvas) { }; var p = Stage.prototype = new createjs.Container(); -// static properties: - /** - * @property _snapToPixelEnabled - * @protected - * @static - * @type {Boolean} - * @default false - * @deprecated Hardware acceleration in modern browsers makes this unnecessary. - **/ - Stage._snapToPixelEnabled = false; // snapToPixelEnabled is temporarily copied here during a draw to provide global access. - // events: /** @@ -6892,14 +6977,13 @@ var p = Stage.prototype = new createjs.Container(); * @deprecated Use addEventListener and the "stagemousedown" event. */ - // TODO: deprecated. /** - * Indicates whether this stage should use the {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of - * display objects when rendering them. + * Indicates whether display objects should be rendered on whole pixels. You can set the + * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of + * display objects to false to enable/disable this behaviour on a per instance basis. * @property snapToPixelEnabled * @type Boolean * @default false - * @deprecated Hardware acceleration makes this not beneficial **/ p.snapToPixelEnabled = false; @@ -6929,30 +7013,13 @@ var p = Stage.prototype = new createjs.Container(); **/ p.mouseMoveOutside = false; - // TODO: confirm naming and inclusion. + // TODO: deprecated. /** - * NOTE: this name is not final. Feedback is appreciated. - * - * The stage assigned to this property will have mouse interactions relayed to it after this stage handles them. - * This can be useful in cases where you have multiple canvases layered on top of one another and want your mouse - * events to pass through. For example, this would relay mouse events from topStage to bottomStage: - * - * topStage.nextStage = bottomStage; - * - * Note that each stage handles the interactions independently. As such, you could have a click register on an - * object in the top stage, and another click register in the bottom stage. Consider using a single canvas with - * cached {{#crossLink "Container"}}{{/crossLink}} instances instead of multiple canvases. - * - * MouseOver, MouseOut, RollOver, and RollOut interactions will not be passed through. They must be enabled using - * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} for each stage individually. - * - * In most instances, you will also want to disable DOM events for the next stage to avoid duplicate interactions. - * myNextStage.enableDOMEvents(false); - * + * Replaced by {{#crossLink "Stage/relayEventsTo"}}{{/crossLink}}. * @property nextStage * @type Stage + * @deprecated Use relayEventsTo instead. **/ - p.nextStage = null; /** * The hitArea property is not supported for Stage. @@ -6960,6 +7027,52 @@ var p = Stage.prototype = new createjs.Container(); * @type {DisplayObject} * @default null */ + +// getter / setters: + /** + * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them. + * This can be useful in cases where you have multiple layered canvases and want user interactions + * events to pass through. For example, this would relay mouse events from topStage to bottomStage: + * + * topStage.nextStage = bottomStage; + * + * To disable relaying, set nextStage to null. + * + * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings + * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled. + * Considerations when using roll over in relay targets:<OL> + * <LI> The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)</LI> + * <LI> All stages that wish to participate in mouse over interaction must enable them via enableMouseOver</LI> + * <LI> All relay targets will share the frequency value of the top-most stage</LI> + * </OL> + * To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing + * 30 as it's desired frequency): + * topStage.nextStage = targetStage; + * topStage.enableMouseOver(10); + * targetStage.enableMouseOver(30); + * + * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its + * DOM events using: + * + * targetStage.enableDOMEvents(false); + * + * @property nextStage + * @type {Stage} + **/ + p._get_nextStage = function() { + return this._nextStage; + }; + p._set_nextStage = function(value) { + if (this._nextStage) { this._nextStage._prevStage = null; } + if (value) { value._prevStage = this; } + this._nextStage = value; + }; + + try { + Object.defineProperties(p, { + nextStage: { get: p._get_nextStage, set: p._set_nextStage } + }); + } catch (e) {} // TODO: use Log // private properties: @@ -6994,6 +7107,20 @@ var p = Stage.prototype = new createjs.Container(); * @type Number **/ p._mouseOverIntervalID = null; + + /** + * @property _nextStage + * @protected + * @type Stage + **/ + p._nextStage = null; + + /** + * @property _prevStage + * @protected + * @type Stage + **/ + p._prevStage = null; // constructor: /** @@ -7043,7 +7170,7 @@ var p = Stage.prototype = new createjs.Container(); this.dispatchEvent("tickend"); } this.dispatchEvent("drawstart"); // TODO: make cancellable? - Stage._snapToPixelEnabled = this.snapToPixelEnabled; + createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled; if (this.autoClear) { this.clear(); } var ctx = this.canvas.getContext("2d"); ctx.save(); @@ -7267,6 +7394,8 @@ var p = Stage.prototype = new createjs.Container(); data = this._pointerData[id] = {x:0,y:0}; // if it's the first new touch, then make it the primary pointer id: if (this._primaryPointerID == null) { this._primaryPointerID = id; } + // if it's the mouse (id == -1) or the first new touch, then make it the primary pointer id: + if (this._primaryPointerID == null || this._primaryPointerID == -1) { this._primaryPointerID = id; } } return data; }; @@ -7288,29 +7417,25 @@ var p = Stage.prototype = new createjs.Container(); * @param {Event} e * @param {Number} pageX * @param {Number} pageY + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. **/ - p._handlePointerMove = function(id, e, pageX, pageY) { + p._handlePointerMove = function(id, e, pageX, pageY, owner) { + if (this._prevStage && owner === undefined) { return; } // redundant listener. if (!this.canvas) { return; } - var o = this._getPointerData(id); + var nextStage=this._nextStage, o=this._getPointerData(id); var inBounds = o.inBounds; this._updatePointerPosition(id, e, pageX, pageY); - if (!inBounds && !o.inBounds && !this.mouseMoveOutside) { return; } - if (id == -1 && o.inBounds == !inBounds) { - this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e); + if (inBounds || o.inBounds || this.mouseMoveOutside) { + if (id == -1 && o.inBounds == !inBounds) { + this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e); + } + + this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e); + this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e); } - this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e); - this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e); - - // TODO: deprecated: - var oEvent = o.event; - if (oEvent && oEvent.hasEventListener("mousemove")) { - // this doesn't use _dispatchMouseEvent because it requires re-targeting. - oEvent.dispatchEvent(new createjs.MouseEvent("mousemove", false, false, o.x, o.y, e, id, (id == this._primaryPointerID), o.rawX, o.rawY), o.target); - } - - this.nextStage&&this.nextStage._handlePointerMove(id, e, pageX, pageY); + nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null); }; /** @@ -7365,33 +7490,25 @@ var p = Stage.prototype = new createjs.Container(); * @param {Number} id * @param {Event} e * @param {Boolean} clear + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. **/ - p._handlePointerUp = function(id, e, clear) { - var o = this._getPointerData(id); - + p._handlePointerUp = function(id, e, clear, owner) { + var nextStage = this._nextStage, o = this._getPointerData(id); + if (this._prevStage && owner === undefined) { return; } // redundant listener. + this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e); - - var oTarget = o.target; - if (oTarget) { - if (this._getObjectsUnderPoint(o.x, o.y, null, true) == oTarget) { - this._dispatchMouseEvent(oTarget, "click", true, id, o, e); - } - this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e); - } - - // TODO: deprecated: - var oEvent = o.event; - if (oEvent && oEvent.hasEventListener("mouseup")) { - // this doesn't use _dispatchMouseEvent because it requires re-targeting. - oEvent.dispatchEvent(new createjs.MouseEvent("mouseup", false, false, o.x, o.y, e, id, (id==this._primaryPointerID), o.rawX, o.rawY), oTarget); - } - + + var target=null, oTarget = o.target; + if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); } + if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); } + this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e); + if (clear) { if (id==this._primaryPointerID) { this._primaryPointerID = null; } delete(this._pointerData[id]); - } else { o.event = o.target = null; } - - this.nextStage&&this.nextStage._handlePointerUp(id, e, clear); + } else { o.target = null; } + + nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this); }; /** @@ -7410,33 +7527,48 @@ var p = Stage.prototype = new createjs.Container(); * @param {Event} e * @param {Number} pageX * @param {Number} pageY + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. **/ - p._handlePointerDown = function(id, e, pageX, pageY) { + p._handlePointerDown = function(id, e, pageX, pageY, owner) { if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); } - var o = this._getPointerData(id); + var target = null, nextStage = this._nextStage, o = this._getPointerData(id); - this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e); + if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e); } + + + if (!owner) { + target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); + this._dispatchMouseEvent(o.target, "mousedown", true, id, o, e); + } - o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); - // TODO: holding onto the event is deprecated: - o.event = this._dispatchMouseEvent(o.target, "mousedown", true, id, o, e); - - this.nextStage&&this.nextStage._handlePointerDown(id, e, pageX, pageY); + nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this); }; /** * @method _testMouseOver * @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target) + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. + * @param {Stage} eventTarget The stage that the cursor is actively over. * @protected **/ - p._testMouseOver = function(clear) { + p._testMouseOver = function(clear, owner, eventTarget) { + if (this._prevStage && owner === undefined) { return; } // redundant listener. + + var nextStage = this._nextStage; + if (!this._mouseOverIntervalID) { + // not enabled for mouseover, but should still relay the event. + nextStage&&nextStage._testMouseOver(clear, owner, eventTarget); + return; + } + // only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs. if (this._primaryPointerID != -1 || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; } - var o = this._getPointerData(-1); - var e = o.posEvtObj; - var target, common = -1, cursor="", t, i, l; - if (clear || this.mouseInBounds && e && e.target == this.canvas) { + var o = this._getPointerData(-1), e = o.posEvtObj; + var isEventTarget = eventTarget || e&&(e.target == this.canvas); + var target=null, common = -1, cursor="", t, i, l; + + if (!owner && (clear || this.mouseInBounds && isEventTarget)) { target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true); this._mouseOverX = this.mouseX; this._mouseOverY = this.mouseY; @@ -7454,6 +7586,7 @@ var p = Stage.prototype = new createjs.Container(); t = t.parent; } this.canvas.style.cursor = cursor; + if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; } // find common ancestor: for (i=0,l=list.length; i<l; i++) { @@ -7476,20 +7609,23 @@ var p = Stage.prototype = new createjs.Container(); if (oldTarget != target) { this._dispatchMouseEvent(target, "mouseover", true, -1, o, e); } - + + nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this); }; /** * @method _handleDoubleClick * @protected * @param {MouseEvent} e + * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage. **/ - p._handleDoubleClick = function(e) { - var o = this._getPointerData(-1); - var target = this._getObjectsUnderPoint(o.x, o.y, null, true); - this._dispatchMouseEvent(target, "dblclick", true, -1, o, e); - - this.nextStage&&this.nextStage._handleDoubleClick(e); + p._handleDoubleClick = function(e, owner) { + var target=null, nextStage=this._nextStage, o=this._getPointerData(-1); + if (!owner) { + target = this._getObjectsUnderPoint(o.x, o.y, null, true); + this._dispatchMouseEvent(target, "dblclick", true, -1, o, e); + } + nextStage&&nextStage._handleDoubleClick(e, owner || target && this); }; /** @@ -7513,8 +7649,6 @@ var p = Stage.prototype = new createjs.Container(); */ var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId==this._primaryPointerID, o.rawX, o.rawY); target.dispatchEvent(evt); - // TODO: returning evt is deprecated: - return evt; }; createjs.Stage = Stage; @@ -7571,6 +7705,9 @@ this.createjs = this.createjs||{}; * the Bitmap can be cached.</li> * <li>Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This * happens in all browsers except recent Firefox builds.</li> + * <li>Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using + * methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting + * `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`</li> * </ol> * * @class Bitmap @@ -7592,14 +7729,6 @@ var p = Bitmap.prototype = new createjs.DisplayObject(); * @type Image | HTMLCanvasElement | HTMLVideoElement **/ p.image = null; - - /** - * Whether or not the Bitmap should be draw to the canvas at whole pixel coordinates. - * @property snapToPixel - * @type Boolean - * @default true - **/ - p.snapToPixel = true; /** * Specifies an area of the source image to draw. If omitted, the whole image will be drawn. @@ -7880,14 +8009,6 @@ var p = Sprite.prototype = new createjs.DisplayObject(); **/ p.spriteSheet = null; - /** - * Whether or not the image should be draw to the canvas at whole pixel coordinates. - * @property snapToPixel - * @type {Boolean} - * @default true - **/ - p.snapToPixel = true; - /** * @property offset * @type {Number} @@ -8936,9 +9057,27 @@ this.createjs = this.createjs || {}; function BitmapText(text, spriteSheet) { this.initialize(text, spriteSheet); } -var p = BitmapText.prototype = new createjs.DisplayObject(); +var p = BitmapText.prototype = new createjs.Container(); // static properties: + /** + * BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains + * an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be + * retained, slightly increasing memory use, but reducing instantiation. + * @property maxPoolSize + * @type Number + * @static + * @default 100 + **/ + BitmapText.maxPoolSize = 100; + + /** + * Sprite object pool. + * @type {Array} + * @static + * @private + */ + BitmapText._spritePool = []; // events: @@ -8994,8 +9133,8 @@ var p = BitmapText.prototype = new createjs.DisplayObject(); /** * If a space character is not defined in the sprite sheet, then empty pixels equal to - * spaceWidth will be inserted instead. If 0, then it will use a value calculated - * by checking for the width of the "1", "E", or "A" character (in that order). If + * spaceWidth will be inserted instead. If 0, then it will use a value calculated + * by checking for the width of the "1", "l", "E", or "A" character (in that order). If * those characters are not defined, it will use the width of the first frame of the * sprite sheet. * @property spaceWidth @@ -9005,14 +9144,20 @@ var p = BitmapText.prototype = new createjs.DisplayObject(); p.spaceWidth = 0; // private properties: + /** + * @property _oldProps + * @type Object + * @protected + **/ + p._oldProps = null; // constructor: /** - * @property DisplayObject_initialize + * @property Container_initialize * @type Function * @protected **/ - p.DisplayObject_initialize = p.initialize; + p.Container_initialize = p.initialize; /** * Initialization method. @@ -9022,10 +9167,11 @@ var p = BitmapText.prototype = new createjs.DisplayObject(); * @protected **/ p.initialize = function (text, spriteSheet) { - this.DisplayObject_initialize(); + this.Container_initialize(); this.text = text; this.spriteSheet = spriteSheet; + this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0}; }; // public methods: @@ -9034,21 +9180,31 @@ var p = BitmapText.prototype = new createjs.DisplayObject(); * @type Function * @protected **/ - p.DisplayObject_draw = p.draw; + p.Container_draw = p.draw; /** - * Draws the display object into the specified context ignoring it's visible, alpha, shadow, and transform. - * Returns true if the draw was handled (useful for overriding functionality). - * NOTE: This method is mainly for internal use, though it may be useful for advanced uses. - * @method draw - * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into. - * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache. - * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back - * into itself). + * Docced in superclass. **/ p.draw = function(ctx, ignoreCache) { - if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; } - this._drawText(ctx); + if (this.DisplayObject_draw(ctx, ignoreCache)) { return; } + this._updateText(); + this.Container_draw(ctx, ignoreCache); + }; + + /** + * @property Container_getBounds + * @type Function + * @protected + **/ + p.Container_getBounds = p.getBounds; + + /** + * Docced in superclass. + **/ + p.getBounds = function() { + // getBounds is somewhat expensive + this._updateText(); + return this.Container_getBounds(); }; /** @@ -9063,44 +9219,51 @@ var p = BitmapText.prototype = new createjs.DisplayObject(); return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent); }; - /** - * Docced in superclass. - */ - p.getBounds = function() { - var bounds = this._rectangle; - this._drawText(null, bounds); - return bounds.width ? bounds : null; - }; + // TODO: should probably disable addChild / addChildAt // private methods: /** - * @method _getFrame + * @method _getFrameIndex * @param {String} character * @param {SpriteSheet} spriteSheet + * @return {Number} * @protected **/ - p._getFrame = function(character, spriteSheet) { + p._getFrameIndex = function(character, spriteSheet) { var c, o = spriteSheet.getAnimation(character); if (!o) { (character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null); if (c) { o = spriteSheet.getAnimation(c); } } - return o && spriteSheet.getFrame(o.frames[0]); + return o && o.frames[0]; + }; + + /** + * @method _getFrame + * @param {String} character + * @param {SpriteSheet} spriteSheet + * @return {Object} + * @protected + **/ + p._getFrame = function(character, spriteSheet) { + var index = this._getFrameIndex(character, spriteSheet); + return index == null ? index : spriteSheet.getFrame(index); }; /** * @method _getLineHeight * @param {SpriteSheet} ss + * @return {Number} * @protected **/ p._getLineHeight = function(ss) { var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0); return frame ? frame.rect.height : 1; }; - /** * @method _getSpaceWidth * @param {SpriteSheet} ss + * @return {Number} * @protected **/ p._getSpaceWidth = function(ss) { @@ -9110,46 +9273,55 @@ var p = BitmapText.prototype = new createjs.DisplayObject(); /** * @method _drawText - * @param {CanvasRenderingContext2D} ctx - * @param {Object | Rectangle} bounds * @protected **/ - p._drawText = function(ctx, bounds) { - var w, h, rx, x=0, y=0, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet; - + p._updateText = function() { + var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet; + var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite; + + for (var n in o) { + if (o[n] != this[n]) { + o[n] = this[n]; + change = true; + } + } + if (!change) { return; } + var hasSpace = !!this._getFrame(" ", ss); if (!hasSpace && spaceW==0) { spaceW = this._getSpaceWidth(ss); } if (lineH==0) { lineH = this._getLineHeight(ss); } - var maxX = 0; for(var i=0, l=this.text.length; i<l; i++) { var character = this.text.charAt(i); - if (!hasSpace && character == " ") { + if (character == " " && !hasSpace) { x += spaceW; continue; } else if (character=="\n" || character=="\r") { if (character=="\r" && this.text.charAt(i+1) == "\n") { i++; } // crlf - if (x-rx > maxX) { maxX = x-rx; } x = 0; y += lineH; continue; } - var o = this._getFrame(character, ss); - if (!o) { continue; } - var rect = o.rect; - rx = o.regX; - w = rect.width; - ctx&&ctx.drawImage(o.image, rect.x, rect.y, w, h=rect.height, x-rx, y-o.regY, w, h); + var index = this._getFrameIndex(character, ss); + if (index == null) { continue; } - x += w + this.letterSpacing; - } - if (x-rx > maxX) { maxX = x-rx; } - - if (bounds) { - bounds.width = maxX-this.letterSpacing; - bounds.height = y+lineH; + if (childIndex < numKids) { + sprite = kids[childIndex]; + } else { + sprite = this.addChild( pool.length ? pool.pop() : new createjs.Sprite() ); + numKids++; + } + sprite.spriteSheet = ss; + sprite.gotoAndStop(index); + sprite.x = x; + sprite.y = y; + childIndex++; + + x += sprite.getBounds().width + this.letterSpacing; } + while (numKids > childIndex) { pool.push(sprite = kids.pop()); sprite.parent = null; } // faster than removeChild. + if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; } }; createjs.BitmapText = BitmapText; @@ -10059,7 +10231,6 @@ var p = DOMElement.prototype = new createjs.DisplayObject(); */ p.draw = function(ctx, ignoreCache) { // this relies on the _tick method because draw isn't called if a parent is not visible. - if (this.visible) { this._visible = true; } // the actual update happens in _handleDrawEnd return true; }; @@ -10169,7 +10340,6 @@ var p = DOMElement.prototype = new createjs.DisplayObject(); */ p._tick = function(params) { var stage = this.getStage(); - this._visible = false; stage&&stage.on("drawend", this._handleDrawEnd, this, true); this.DisplayObject__tick(params); }; @@ -10184,11 +10354,12 @@ var p = DOMElement.prototype = new createjs.DisplayObject(); if (!o) { return; } var style = o.style; - var visibility = this._visible ? "visible" : "hidden"; - if (visibility != style.visibility) { style.visibility = visibility; } - if (!this._visible) { return; } - var mtx = this.getConcatenatedMatrix(this._matrix); + + var visibility = mtx.visible ? "visible" : "hidden"; + if (visibility != style.visibility) { style.visibility = visibility; } + if (!mtx.visible) { return; } + var oMtx = this._oldMtx; var n = 10000; // precision if (!oMtx || oMtx.alpha != mtx.alpha) { @@ -10807,7 +10978,6 @@ this.createjs = this.createjs || {}; for(var i = 0; i < l; i += 4) { data[i + 3] = map[i] || 0; } - imageData.data = data; targetCtx.putImageData(imageData, targetX, targetY); return true; }; @@ -11771,8 +11941,9 @@ var Touch = function() { * @static **/ Touch.isSupported = function() { - return ('ontouchstart' in window) || // iOS - (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0); // IE10 + return ('ontouchstart' in window) // iOS + || (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10 + || (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0); // IE11+ }; /** @@ -11884,8 +12055,7 @@ var Touch = function() { var canvas = stage.canvas; var f = stage.__touch.f = function(e) { Touch._IE_handleEvent(stage,e); }; - var prefixed = (window.navigator["pointerEnabled"] === undefined); - if (prefixed) { + if (window.navigator["pointerEnabled"] === undefined) { canvas.addEventListener("MSPointerDown", f, false); window.addEventListener("MSPointerMove", f, false); window.addEventListener("MSPointerUp", f, false); @@ -11911,8 +12081,7 @@ var Touch = function() { Touch._IE_disable = function(stage) { var f = stage.__touch.f; - var prefixed = (window.navigator["pointerEnabled"] === undefined); - if (prefixed) { + if (window.navigator["pointerEnabled"] === undefined) { window.removeEventListener("MSPointerMove", f, false); window.removeEventListener("MSPointerUp", f, false); window.removeEventListener("MSPointerCancel", f, false); @@ -12041,6 +12210,6 @@ this.createjs = this.createjs || {}; * @type String * @static **/ - s.buildDate = /*date*/"Tue, 19 Nov 2013 03:57:30 GMT"; // injected by build process + s.buildDate = /*date*/"Tue, 28 Jan 2014 21:54:26 GMT"; // injected by build process })(); diff --git a/vendor/scripts/preloadjs-NEXT.combined.js b/vendor/scripts/preloadjs-NEXT.combined.js index 03fbbe7bc..236db9d7d 100644 --- a/vendor/scripts/preloadjs-NEXT.combined.js +++ b/vendor/scripts/preloadjs-NEXT.combined.js @@ -27,7 +27,7 @@ this.createjs = this.createjs||{}; * @type String * @static **/ - s.buildDate = /*date*/"Wed, 20 Nov 2013 16:17:10 GMT"; // injected by build process + s.buildDate = /*date*/"Wed, 18 Dec 2013 23:28:57 GMT"; // injected by build process })(); /* @@ -396,6 +396,7 @@ var p = EventDispatcher.prototype; target.hasEventListener = p.hasEventListener; target.dispatchEvent = p.dispatchEvent; target._dispatchEvent = p._dispatchEvent; + target.willTrigger = p.willTrigger; }; // constructor: @@ -612,7 +613,7 @@ var p = EventDispatcher.prototype; }; /** - * Indicates whether there is at least one listener for the specified event type and `useCapture` value. + * Indicates whether there is at least one listener for the specified event type. * @method hasEventListener * @param {String} type The string type of the event. * @return {Boolean} Returns true if there is at least one listener for the specified event. @@ -621,6 +622,26 @@ var p = EventDispatcher.prototype; var listeners = this._listeners, captureListeners = this._captureListeners; return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type])); }; + + /** + * Indicates whether there is at least one listener for the specified event type on this object or any of its + * ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the + * specified type is dispatched from this object, it will trigger at least one listener. + * + * This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire + * event flow for a listener, not just this object. + * @method willTrigger + * @param {String} type The string type of the event. + * @return {Boolean} Returns `true` if there is at least one listener for the specified event. + **/ + p.willTrigger = function(type) { + var o = this; + while (o) { + if (o.hasEventListener(type)) { return true; } + o = o.parent; + } + return false; + }; /** * @method toString @@ -877,25 +898,35 @@ this.createjs = this.createjs||{}; * The base loader, which defines all the generic callbacks and events. All loaders extend this class, including the * {{#crossLink "LoadQueue"}}{{/crossLink}}. * @class AbstractLoader - * @uses EventDispatcher + * @extends EventDispatcher */ var AbstractLoader = function () { this.init(); }; - AbstractLoader.prototype = {}; + AbstractLoader.prototype = new createjs.EventDispatcher(); //TODO: TEST! var p = AbstractLoader.prototype; var s = AbstractLoader; /** * The RegExp pattern to use to parse file URIs. This supports simple file names, as well as full domain URIs with - * query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6. + * query strings. The resulting match is: protocol:$1 domain:$2 relativePath:$3 path:$4 file:$5 extension:$6 query:$7. * @property FILE_PATTERN * @type {RegExp} * @static * @protected */ - s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/; + s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/; + + /** + * The RegExp pattern to use to parse path URIs. This supports protocols, relative files, and paths. The resulting + * match is: protocol:$1 relativePath:$2 path$3. + * @property PATH_PATTERN + * @type {RegExp} + * @static + * @protected + */ + s.PATH_PATTERN = /^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/; /** * If the loader has completed loading. This provides a quick check, but also ensures that the different approaches @@ -909,7 +940,7 @@ this.createjs = this.createjs||{}; /** * Determine if the loader was canceled. Canceled loads will not fire complete events. Note that * {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "AbstractLoader/close"}}{{/crossLink}} - * instead of canceled. + * instead of setting this property. * @property canceled * @type {Boolean} * @default false @@ -918,6 +949,15 @@ this.createjs = this.createjs||{}; /** * The current load progress (percentage) for this item. This will be a number between 0 and 1. + * + * <h4>Example</h4> + * + * var queue = new createjs.LoadQueue(); + * queue.loadFile("largeImage.png"); + * queue.on("progress", function() { + * console.log("Progress:", queue.progress, event.progress); + * }); + * * @property progress * @type {Number} * @default 0 @@ -933,15 +973,6 @@ this.createjs = this.createjs||{}; */ p._item = null; - /** - * A path that will be prepended on to the item's source parameter before it is loaded. - * @property _basePath - * @type {String} - * @private - * @since 0.3.1 - */ - p._basePath = null; - // Events /** * The event that is fired when the overall progress changes. @@ -980,7 +1011,7 @@ this.createjs = this.createjs||{}; * @param {String} type The event type. * @param {Object} [item] The item that was being loaded that caused the error. The item was specified in * the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} - * call. If only a string path or tag was specified, the object will contain that value as a property. + * call. If only a string path or tag was specified, the object will contain that value as a `src` property. * @param {String} [error] The error object or text. * @since 0.3.0 */ @@ -1015,18 +1046,6 @@ this.createjs = this.createjs||{}; * @deprecated Use addEventListener and the "error" event. */ - -// mix-ins: - // EventDispatcher methods: - p.addEventListener = null; - p.removeEventListener = null; - p.removeAllEventListeners = null; - p.dispatchEvent = null; - p.hasEventListener = null; - p._listeners = null; - createjs.EventDispatcher.initialize(p); - - /** * Get a reference to the manifest item that is loaded by this loader. In most cases this will be the value that was * passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or @@ -1145,11 +1164,11 @@ this.createjs = this.createjs||{}; }; /** - * Parse a file URI using the <code>AbstractLoader.FILE_PATTERN</code> RegExp pattern. + * Parse a file URI using the {{#crossLink "AbstractLoader/FILE_PATTERN:property"}}{{/crossLink}} RegExp pattern. * @method _parseURI * @param {String} path The file path to parse. - * @return {Array} The matched file contents. Please see the <code>AbstractLoader.FILE_PATTERN</code> property for - * details on the return value. This will return null if it does not match. + * @return {Array} The matched file contents. Please see the FILE_PATTERN property for details on the return value. + * This will return null if it does not match. * @protected */ p._parseURI = function(path) { @@ -1157,6 +1176,19 @@ this.createjs = this.createjs||{}; return path.match(s.FILE_PATTERN); }; + /** + * Parse a file URI using the {{#crossLink "AbstractLoader/PATH_PATTERN"}}{{/crossLink}} RegExp pattern. + * @method _parsePath + * @param {String} path The file path to parse. + * @return {Array} The matched path contents. Please see the PATH_PATTERN property for details on the return value. + * This will return null if it does not match. + * @protected + */ + p._parsePath = function(path) { + if (!path) { return null; } + return path.match(s.PATH_PATTERN); + }; + /** * Formats an object into a query string for either a POST or GET request. * @method _formatQueryString @@ -1179,25 +1211,16 @@ this.createjs = this.createjs||{}; }; /** - * A utility method that builds a file path using a source, a basePath, and a data object, and formats it into a new - * path. All of the loaders in PreloadJS use this method to compile paths when loading. + * A utility method that builds a file path using a source and a data object, and formats it into a new path. All + * of the loaders in PreloadJS use this method to compile paths when loading. * @method buildPath * @param {String} src The source path to add values to. - * @param {String} [basePath] A string to prepend to the file path. Sources beginning with http:// or similar will - * not receive a base path. * @param {Object} [data] Object used to append values to this request as a query string. Existing parameters on the * path will be preserved. * @returns {string} A formatted string that contains the path and the supplied parameters. * @since 0.3.1 */ - p.buildPath = function(src, _basePath, data) { - if (_basePath != null) { - var match = this._parseURI(src); - // IE 7,8 Return empty string here. - if (match == null || match[1] == null || match[1] == '') { - src = _basePath + src; - } - } + p.buildPath = function(src, data) { if (data == null) { return src; } @@ -1217,6 +1240,39 @@ this.createjs = this.createjs||{}; } }; + /** + * @method _isCrossDomain + * @param {Object} item A load item with a `src` property + * @return {Boolean} If the load item is loading from a different domain than the current location. + * @private + */ + p._isCrossDomain = function(item) { + var target = document.createElement("a"); + target.href = item.src; + + var host = document.createElement("a"); + host.href = location.href; + + var crossdomain = (target.hostname != "") && + (target.port != host.port || + target.protocol != host.protocol || + target.hostname != host.hostname); + return crossdomain; + } + + /** + * @method _isLocal + * @param {Object} item A load item with a `src` property + * @return {Boolean} If the load item is loading from the "file:" protocol. Assume that the host must be local as + * well. + * @private + */ + p._isLocal = function(item) { + var target = document.createElement("a"); + target.href = item.src; + return target.hostname == "" && target.protocol == "file:"; + }; + /** * @method toString * @return {String} a string representation of the instance. @@ -1274,6 +1330,7 @@ this.createjs = this.createjs||{}; * to load files and process results. * * <h4>Example</h4> + * * var queue = new createjs.LoadQueue(); * queue.installPlugin(createjs.Sound); * queue.on("complete", handleComplete, this); @@ -1293,13 +1350,23 @@ this.createjs = this.createjs||{}; * * <h4>Browser Support</h4> * PreloadJS is partially supported in all browsers, and fully supported in all modern browsers. Known exceptions: - * <ul><li>XHR loading of any content will not work in many older browsers (See a matrix here: <a href="http://caniuse.com/xhr2">http://caniuse.com/xhr2</a>). + * <ul><li>XHR loading of any content will not work in many older browsers (See a matrix here: <a href="http://caniuse.com/xhr2" target="_blank">http://caniuse.com/xhr2</a>). * In many cases, you can fall back on tag loading (images, audio, CSS, scripts, SVG, and JSONP). Text and * WebAudio will only work with XHR.</li> * <li>Some formats have poor support for complete events in IE 6, 7, and 8 (SVG, tag loading of scripts, XML/JSON)</li> * <li>Opera has poor support for SVG loading with XHR</li> * <li>CSS loading in Android and Safari will not work with tags (currently, a workaround is in progress)</li> - * </li> + * <li>Local loading is not permitted with XHR, which is required by some file formats. When testing local content + * use either a local server, or enable tag loading, which is supported for most formats. See {{#crossLink "LoadQueue/setUseXHR"}}{{/crossLink}} + * for more information.</li> + * </ul> + * + * <h4>Cross-domain Loading</h4> + * Most content types can be loaded cross-domain, as long as the server supports CORS. PreloadJS also has internal + * support for images served from a CORS-enabled server, via the `crossOrigin` argument on the {{#crossLink "LoadQueue"}}{{/crossLink}} + * constructor. If set to a string value (such as "Anonymous"), the "crossOrigin" property of images generated by + * PreloadJS is set to that value. Please note that setting a `crossOrigin` value on an image that is served from a + * server without CORS will cause other errors. For more info on CORS, visit https://en.wikipedia.org/wiki/Cross-origin_resource_sharing. * * @module PreloadJS * @main PreloadJS @@ -1308,8 +1375,6 @@ this.createjs = this.createjs||{}; // namespace: this.createjs = this.createjs||{}; -//TODO: addHeadTags support - /* TODO: WINDOWS ISSUES * No error for HTML audio in IE 678 @@ -1326,8 +1391,8 @@ TODO: WINDOWS ISSUES "use strict"; /** - * The LoadQueue class is the main API for preloading content. LoadQueue is a load manager, which maintains - * a single file, or a queue of files. + * The LoadQueue class is the main API for preloading content. LoadQueue is a load manager, which can preload either + * a single file, or queue of files. * * <b>Creating a Queue</b><br /> * To use LoadQueue, create a LoadQueue instance. If you want to force tag loading where possible, set the useXHR @@ -1337,24 +1402,35 @@ TODO: WINDOWS ISSUES * * <b>Listening for Events</b><br /> * Add any listeners you want to the queue. Since PreloadJS 0.3.0, the {{#crossLink "EventDispatcher"}}{{/crossLink}} - * lets you add as many listeners as you want for events. You can subscribe to complete, error, fileload, progress, - * and fileprogress. + * lets you add as many listeners as you want for events. You can subscribe to the following events:<ul> + * <li>{{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}}: fired when a queue completes loading all + * files</li> + * <li>{{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}: fired when the queue encounters an error with + * any file.</li> + * <li>{{#crossLink "AbstractLoader/progress:event"}}{{/crossLink}}: Progress for the entire queue has + * changed.</li> + * <li>{{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}}: A single file has completed loading.</li> + * <li>{{#crossLink "LoadQueue/fileprogress:event"}}{{/crossLink}}: Progress for a single file has changes. Note + * that only files loaded with XHR (or possibly by plugins) will fire progress events other than 0 or 100%.</li> + * </ul> * * queue.on("fileload", handleFileLoad, this); * queue.on("complete", handleComplete, this); * * <b>Adding files and manifests</b><br /> * Add files you want to load using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or add multiple files at a - * time using {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. Files are appended to the queue, so you can use - * these methods as many times as you like, whenever you like. + * time using a list or a manifest definition using {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. Files are + * appended to the end of the active queue, so you can use these methods as many times as you like, whenever you + * like. * * queue.loadFile("filePath/file.jpg"); * queue.loadFile({id:"image", src:"filePath/file.jpg"}); - * queue.loadManifest(["filePath/file.jpg", {id:"image", src:"filePath/file.jpg"}]; + * queue.loadManifest(["filePath/file.jpg", {id:"image", src:"filePath/file.jpg"}]); * - * If you pass <code>false</code> as the second parameter, the queue will not immediately load the files (unless it - * has already been started). Call the {{#crossLink "AbstractLoader/load"}}{{/crossLink}} method to begin a paused queue. - * Note that a paused queue will automatically resume when new files are added to it. + * If you pass `false` as the `loadNow` parameter, the queue will not kick of the load of the files, but it will not + * stop if it has already been started. Call the {{#crossLink "AbstractLoader/load"}}{{/crossLink}} method to begin + * a paused queue. Note that a paused queue will automatically resume when new files are added to it with a + * `loadNow` argument of `true`. * * queue.load(); * @@ -1367,40 +1443,42 @@ TODO: WINDOWS ISSUES * queue.loadFile({src:"path/to/myFile.mp3x", type:createjs.LoadQueue.SOUND}); * * // Note that PreloadJS will not read a file extension from the query string - * queue.loadFile({src:"http://server.com/proxy?file=image.jpg"}, type:createjs.LoadQueue.IMAGE}); + * queue.loadFile({src:"http://server.com/proxy?file=image.jpg", type:createjs.LoadQueue.IMAGE}); * - * Supported types include: + * Supported types are defined on the LoadQueue class, and include: * <ul> - * <li>createjs.LoadQueue.BINARY (Raw binary data via XHR)</li> - * <li>createjs.LoadQueue.CSS (CSS files)</li> - * <li>createjs.LoadQueue.IMAGE (Common image formats)</li> - * <li>createjs.LoadQueue.JAVASCRIPT (JavaScript files)</li> - * <li>createjs.LoadQueue.JSON (JSON data)</li> - * <li>createjs.LoadQueue.JSONP (JSON files cross-domain)</li> - * <li>createjs.LoadQueue.MANIFEST (A list of files to load in JSON format, see {{#crossLink "LoadQueue/MANIFEST:property"}}{{/crossLink}} )</li> - * <li>createjs.LoadQueue.SOUND (Audio file formats)</li> - * <li>createjs.LoadQueue.SVG (SVG files)</li> - * <li>createjs.LoadQueue.TEXT (Text files - XHR only)</li> - * <li>createjs.LoadQueue.XML (XML data)</li> + * <li>{{#crossLink "LoadQueue/BINARY:property"}}{{/crossLink}}: Raw binary data via XHR</li> + * <li>{{#crossLink "LoadQueue/CSS:property"}}{{/crossLink}}: CSS files</li> + * <li>{{#crossLink "LoadQueue/IMAGE:property"}}{{/crossLink}}: Common image formats</li> + * <li>{{#crossLink "LoadQueue/JAVASCRIPT:property"}}{{/crossLink}}: JavaScript files</li> + * <li>{{#crossLink "LoadQueue/JSON:property"}}{{/crossLink}}: JSON data</li> + * <li>{{#crossLink "LoadQueue/JSONP:property"}}{{/crossLink}}: JSON files cross-domain</li> + * <li>{{#crossLink "LoadQueue/MANIFEST:property"}}{{/crossLink}}: A list of files to load in JSON format, see + * {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}</li> + * <li>{{#crossLink "LoadQueue/SOUND:property"}}{{/crossLink}}: Audio file formats</li> + * <li>{{#crossLink "LoadQueue/SVG:property"}}{{/crossLink}}: SVG files</li> + * <li>{{#crossLink "LoadQueue/TEXT:property"}}{{/crossLink}}: Text files - XHR only</li> + * <li>{{#crossLink "LoadQueue/XML:property"}}{{/crossLink}}: XML data</li> * </ul> * * <b>Handling Results</b><br /> - * When a file is finished downloading, a "fileload" event is dispatched. In an example above, there is an event - * listener snippet for fileload. Loaded files are always an object that can be used immediately, including: + * When a file is finished downloading, a {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event is + * dispatched. In an example above, there is an event listener snippet for fileload. Loaded files are usually a + * resolved object that can be used immediately, including: * <ul> - * <li>Image: An <img /> tag</li> - * <li>Audio: An <audio /> tag</a> - * <li>JavaScript: A <script /> tag</li> - * <li>CSS: A <link /> tag</li> - * <li>XML: An XML DOM node</li> - * <li>SVG: An <object /> tag</li> - * <li>JSON: A formatted JavaScript Object</li> - * <li>Text: Raw text</li> - * <li>Binary: The binary loaded result</li> - * </ul> + * <li>Image: An <img /> tag</li> + * <li>Audio: An <audio /> tag</a> + * <li>JavaScript: A <script /> tag</li> + * <li>CSS: A <link /> tag</li> + * <li>XML: An XML DOM node</li> + * <li>SVG: An <object /> tag</li> + * <li>JSON: A formatted JavaScript Object</li> + * <li>Text: Raw text</li> + * <li>Binary: The binary loaded result</li> + * </ul> * * function handleFileLoad(event) { - * var item = event.item; // A reference to the item that was passed in + * var item = event.item; // A reference to the item that was passed in to the LoadQueue * var type = item.type; * * // Add any images to the page body. @@ -1411,22 +1489,23 @@ TODO: WINDOWS ISSUES * * At any time after the file has been loaded (usually after the queue has completed), any result can be looked up * via its "id" using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}. If no id was provided, then the "src" or - * file path can be used instead. It is recommended to always pass an id. + * file path can be used instead, including the `path` defined by a manifest, but <strong>not including</strong> a + * base path defined on the LoadQueue. It is recommended to always pass an id. * * var image = queue.getResult("image"); * document.body.appendChild(image); * - * Raw loaded content can be accessed using the <code>rawResult</code> property of the <code>fileload</code> event, - * or can be looked up using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}, and <code>true</code> as the 2nd - * parameter. This is only applicable for content that has been parsed for the browser, specifically, JavaScript, - * CSS, XML, SVG, and JSON objects. + * Raw loaded content can be accessed using the <code>rawResult</code> property of the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} + * event, or can be looked up using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}, passing `true` as the 2nd + * argument. This is only applicable for content that has been parsed for the browser, specifically: JavaScript, + * CSS, XML, SVG, and JSON objects, or anything loaded with XHR. * - * var image = queue.getResult("image", true); + * var image = queue.getResult("image", true); // load the binary image data loaded with XHR. * * <b>Plugins</b><br /> * LoadQueue has a simple plugin architecture to help process and preload content. For example, to preload audio, - * make sure to install the <a href="http://soundjs.com">SoundJS</a> Sound class, which will help preload HTML - * audio, Flash audio, and WebAudio files. This should be installed <b>before</b> loading any audio files. + * make sure to install the <a href="http://soundjs.com">SoundJS</a> Sound class, which will help load HTML audio, + * Flash audio, and WebAudio files. This should be installed <strong>before</strong> loading any audio files. * * queue.installPlugin(createjs.Sound); * @@ -1445,35 +1524,46 @@ TODO: WINDOWS ISSUES * </ul> * * @class LoadQueue - * @param {Boolean} [useXHR=true] Determines whether the preload instance will favor loading with XHR (XML HTTP Requests), - * or HTML tags. When this is <code>false</code>, LoadQueue will use tag loading when possible, and fall back on XHR + * @param {Boolean} [useXHR=true] Determines whether the preload instance will favor loading with XHR (XML HTTP + * Requests), or HTML tags. When this is `false`, the queue will use tag loading when possible, and fall back on XHR * when necessary. - * @param {String} basePath A path that will be prepended on to the source parameter of all items in the queue - * before they are loaded. Sources beginning with http:// or similar will not receive a base path. - * Note that a basePath provided to any loadFile or loadManifest call will override the - * basePath specified on the LoadQueue constructor. + * @param {String} [basePath=""] A path that will be prepended on to the source parameter of all items in the queue + * before they are loaded. Sources beginning with a protocol such as `http://` or a relative path such as `../` + * will not receive a base path. + * @param {String|Boolean} [crossOrigin=""] An optional flag to support images loaded from a CORS-enabled server. To + * use it, set this value to `true`, which will default the crossOrigin property on images to "Anonymous". Any + * string value will be passed through, but only "" and "Anonymous" are recommended. * @constructor * @extends AbstractLoader */ - var LoadQueue = function(useXHR, basePath) { - this.init(useXHR, basePath); + var LoadQueue = function(useXHR, basePath, crossOrigin) { + this.init(useXHR, basePath, crossOrigin); }; var p = LoadQueue.prototype = new createjs.AbstractLoader(); var s = LoadQueue; /** - * Time in milliseconds to assume a load has failed. - * @property LOAD_TIMEOUT + * Time in milliseconds to assume a load has failed. An {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}} + * event is dispatched if the timeout is reached before any data is received. + * @property loadTimeout * @type {Number} * @default 8000 * @static + * @since 0.4.1 */ - s.LOAD_TIMEOUT = 8000; + s.loadTimeout = 8000; + + /** + * Time in milliseconds to assume a load has failed. + * @type {Number} + * @deprecated in favor of the {{#crossLink "LoadQueue/loadTimeout:property"}}{{/crossLink}} property. + */ + s.LOAD_TIMEOUT = 0; // Preload Types /** - * The preload type for generic binary types. Note that images and sound files are treated as binary. + * The preload type for generic binary types. Note that images are loaded as binary files when using XHR. * @property BINARY * @type {String} * @default binary @@ -1482,7 +1572,8 @@ TODO: WINDOWS ISSUES s.BINARY = "binary"; /** - * The preload type for css files. CSS files are loaded into a LINK or STYLE tag (depending on the load type) + * The preload type for css files. CSS files are loaded using a <link> when loaded with XHR, or a + * <style> tag when loaded with tags. * @property CSS * @type {String} * @default css @@ -1491,7 +1582,7 @@ TODO: WINDOWS ISSUES s.CSS = "css"; /** - * The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an IMAGE tag. + * The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an <image> tag. * @property IMAGE * @type {String} * @default image @@ -1501,11 +1592,11 @@ TODO: WINDOWS ISSUES /** * The preload type for javascript files, usually with the "js" file extension. JavaScript files are loaded into a - * SCRIPT tag. + * <script> tag. * * Since version 0.4.1+, due to how tag-loaded scripts work, all JavaScript files are automatically injected into - * the BODY of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier, - * only tag-loaded scripts were injected. + * the body of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier, + * only tag-loaded scripts are injected. * @property JAVASCRIPT * @type {String} * @default javascript @@ -1516,7 +1607,8 @@ TODO: WINDOWS ISSUES /** * The preload type for json files, usually with the "json" file extension. JSON data is loaded and parsed into a * JavaScript object. Note that if a `callback` is present on the load item, the file will be loaded with JSONP, - * no matter what the {{#crossLink "LoadQueue/useXHR:property"}}{{/crossLink}} property is set to. + * no matter what the {{#crossLink "LoadQueue/useXHR:property"}}{{/crossLink}} property is set to, and the JSON + * must contain a matching wrapper function. * @property JSON * @type {String} * @default json @@ -1538,8 +1630,8 @@ TODO: WINDOWS ISSUES /** * The preload type for json-based manifest files, usually with the "json" file extension. The JSON data is loaded - * and parsed into a JavaScript object, and parsed. PreloadJS will then look for a "manifest" property in the JSON, - * which is an array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} + * and parsed into a JavaScript object. PreloadJS will then look for a "manifest" property in the JSON, which is an + * Array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} * method. If a "callback" is specified on the manifest object, then it will be loaded using JSONP instead, * regardless of what the {{#crossLink "LoadQueue/useXHR:property"}}{{/crossLink}} property is set to. * @property MANIFEST @@ -1551,7 +1643,8 @@ TODO: WINDOWS ISSUES s.MANIFEST = "manifest"; /** - * The preload type for sound files, usually mp3, ogg, or wav. Audio is loaded into an AUDIO tag. + * The preload type for sound files, usually mp3, ogg, or wav. When loading via tags, audio is loaded into an + * <audio> tag. * @property SOUND * @type {String} * @default sound @@ -1603,6 +1696,30 @@ TODO: WINDOWS ISSUES // Prototype + /** + * A path that will be prepended on to the item's `src`. The `_basePath` property will only be used if an item's + * source is relative, and does not include a protocol such as `http://`, or a relative path such as `../`. + * @property _basePath + * @type {String} + * @private + * @since 0.3.1 + */ + p._basePath = null; + + /** + * An optional flag to set on images that are loaded using PreloadJS, which enables CORS support. Images loaded + * cross-domain by servers that support CORS require the crossOrigin flag to be loaded and interacted with by + * a canvas. When loading locally, or with a server with no CORS support, this flag can cause other security issues, + * so it is recommended to only set it if you are sure the server supports it. Currently, supported values are "" + * and "Anonymous". + * @property _crossOrigin + * @type {String} + * @defaultValue "" + * @private + * @since 0.4.1 + */ + p._crossOrigin = ""; + /** * Use XMLHttpRequest (XHR) when possible. Note that LoadQueue will default to tag loading or XHR loading depending * on the requirements for a media type. For example, HTML audio can not be loaded with XHR, and WebAudio can not be @@ -1627,8 +1744,9 @@ TODO: WINDOWS ISSUES p.stopOnError = false; /** - * Ensure loaded scripts "complete" in the order they are specified. Note that scripts loaded via tags will only - * load one at a time, and will be added to the document when they are loaded. + * Ensure loaded scripts "complete" in the order they are specified. Loaded scripts are added to the document head + * once they are loaded. Note that scripts loaded via tags will load one-at-a-time when this property is `true`. + * load one at a time * @property maintainScriptOrder * @type {Boolean} * @default true @@ -1652,7 +1770,7 @@ TODO: WINDOWS ISSUES * @param {String} type The event type. * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the - * object will contain that value as a property. + * object will contain that value as a `src` property. * @param {Object} result The HTML tag or parsed result of the loaded item. * @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted * to a usable object. @@ -1666,7 +1784,7 @@ TODO: WINDOWS ISSUES * @param {String} type The event type. * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the - * object will contain that value as a property. + * object will contain that value as a `src` property. * @param {Number} loaded The number of bytes that have been loaded. Note that this may just be a percentage of 1. * @param {Number} total The total number of bytes. If it is unknown, the value is 1. * @param {Number} progress The amount that has been loaded between 0 and 1. @@ -1846,7 +1964,7 @@ TODO: WINDOWS ISSUES p._loadedScripts = null; // Overrides abstract method in AbstractLoader - p.init = function(useXHR, basePath) { + p.init = function(useXHR, basePath, crossOrigin) { this._numItems = this._numItemsLoaded = 0; this._paused = false; this._loadStartWasDispatched = false; @@ -1867,10 +1985,15 @@ TODO: WINDOWS ISSUES this._basePath = basePath; this.setUseXHR(useXHR); + this._crossOrigin = (crossOrigin === true) + ? "Anonymous" : (crossOrigin === false || crossOrigin == null) + ? "" : crossOrigin; }; /** * Change the usXHR value. Note that if this is set to true, it may fail depending on the browser's capabilities. + * Additionally, some files require XHR in order to load, such as JSON (without JSONP), Text, and XML, so XHR will + * be used regardless of what is passed to this method. * @method setUseXHR * @param {Boolean} value The new useXHR value to set. * @return {Boolean} The new useXHR value. If XHR is not supported by the browser, this will return false, even if @@ -1886,8 +2009,7 @@ TODO: WINDOWS ISSUES /** * Stops all queued and loading items, and clears the queue. This also removes all internal references to loaded - * content, and allows the queue to be used again. Items that have not yet started can be kicked off again using - * the {{#crossLink "AbstractLoader/load"}}{{/crossLink}} method. + * content, and allows the queue to be used again. * @method removeAll * @since 0.3.0 */ @@ -1898,8 +2020,20 @@ TODO: WINDOWS ISSUES /** * Stops an item from being loaded, and removes it from the queue. If nothing is passed, all items are removed. * This also removes internal references to loaded item(s). + * + * <h4>Example</h4> + * + * queue.loadManifest([ + * {src:"test.png", id:"png"}, + * {src:"test.jpg", id:"jpg"}, + * {src:"test.mp3", id:"mp3"} + * ]); + * queue.remove("png"); // Single item by ID + * queue.remove("png", "test.jpg"); // Items as arguments. Mixed id and src. + * queue.remove(["test.png", "jpg"]); // Items in an Array. Mixed id and src. + * * @method remove - * @param {String | Array} idsOrUrls The id or ids to remove from this queue. You can pass an item, an array of + * @param {String | Array} idsOrUrls* The id or ids to remove from this queue. You can pass an item, an array of * items, or multiple items as arguments. * @since 0.3.0 */ @@ -1919,11 +2053,9 @@ TODO: WINDOWS ISSUES // Destroy everything if (!args) { this.close(); - for (var n in this._loadItemsById) { this._disposeItem(this._loadItemsById[n]); } - this.init(this.useXHR); // Remove specific items @@ -2003,7 +2135,7 @@ TODO: WINDOWS ISSUES * a binary result to work with. Binary files are loaded using XHR2. * @method isBinary * @param {String} type The item type. - * @return If the specified type is binary. + * @return {Boolean} If the specified type is binary. * @private */ s.isBinary = function(type) { @@ -2016,6 +2148,30 @@ TODO: WINDOWS ISSUES } }; + + /** + * Determine if a specific type is a text based asset, and should be loaded as UTF-8. + * @method isText + * @param {String} type The item type. + * @return {Boolean} If the specified type is text. + * @private + */ + s.isText = function(type) { + switch (type) { + case createjs.LoadQueue.TEXT: + case createjs.LoadQueue.JSON: + case createjs.LoadQueue.MANIFEST: + case createjs.LoadQueue.XML: + case createjs.LoadQueue.HTML: + case createjs.LoadQueue.CSS: + case createjs.LoadQueue.SVG: + case createjs.LoadQueue.JAVASCRIPT: + return true; + default: + return false; + } + }; + /** * Register a plugin. Plugins can map to load types (sound, image, etc), or specific extensions (png, mp3, etc). * Currently, only one plugin can exist per type/extension. @@ -2054,8 +2210,14 @@ TODO: WINDOWS ISSUES /** * Set the maximum number of concurrent connections. Note that browsers and servers may have a built-in maximum * number of open connections, so any additional connections may remain in a pending state until the browser - * opens the connection. Note that when loading scripts using tags, and {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} + * opens the connection. When loading scripts using tags, and when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} * is `true`, only one script is loaded at a time due to browser limitations. + * + * <h4>Example</h4> + * + * var queue = new createjs.LoadQueue(); + * queue.setMaxConnections(10); // Allow 10 concurrent loads + * * @method setMaxConnections * @param {Number} value The number of concurrent loads to allow. By default, only a single connection per LoadQueue * is open at any time. @@ -2065,19 +2227,19 @@ TODO: WINDOWS ISSUES if (!this._paused && this._loadQueue.length > 0) { this._loadNext(); } - } + }; /** * Load a single file. To add multiple files at once, use the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} * method. * - * Note that files are always appended to the current queue, so this method can be used multiple times to add files. + * Files are always appended to the current queue, so this method can be used multiple times to add files. * To clear the queue first, use the {{#crossLink "AbstractLoader/close"}}{{/crossLink}} method. * @method loadFile * @param {Object | String} file The file object or path to load. A file can be either - * <ol> - * <li>a path to a resource (string). Note that this kind of load item will be - * converted to an object (see below) in the background.</li> + * <ul> + * <li>A string path to a resource. Note that this kind of load item will be converted to an object (see below) + * in the background.</li> * <li>OR an object that contains:<ul> * <li>src: The source of the file that is being loaded. This property is <b>required</b>. The source can * either be a string (recommended), or an HTML tag.</li> @@ -2087,16 +2249,22 @@ TODO: WINDOWS ISSUES * <li>id: A string identifier which can be used to reference the loaded object.</li> * <li>callback: Optional, used for JSONP requests, to define what method to call when the JSONP is loaded.</li> * <li>data: An arbitrary data object, which is included with the loaded object</li> - * <li>method: used to define if this request uses GET or POST when sending data to the server. Default; GET</li> + * <li>method: used to define if this request uses GET or POST when sending data to the server. The default + * value is "GET"</li> * <li>values: Optional object of name/value pairs to send to the server.</li> - * </ul> - * </ol> + * <li>headers: Optional object hash of headers to attach to an XHR request. PreloadJS will automatically + * attach some default headers when required, including Origin, Content-Type, and X-Requested-With. You may + * override the default headers if needed.</li> + * </ul> + * </ul> * @param {Boolean} [loadNow=true] Kick off an immediate load (true) or wait for a load call (false). The default * value is true. If the queue is paused using {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}}, and the value is - * true, the queue will resume automatically. - * @param {String} [basePath] An optional base path prepended to the file source when the file is loaded. - * Sources beginning with http:// or similar will not receive a base path. - * The load item will not be modified. + * `true`, the queue will resume automatically. + * @param {String} [basePath] A base path that will be prepended to each file. The basePath argument overrides the + * path specified in the constructor. Note that if you load a manifest using a file of type {{#crossLink "LoadQueue/MANIFEST:property"}}{{/crossLink}}, + * its files will <strong>NOT</strong> use the basePath parameter. <strong>The basePath parameter is deprecated.</strong> + * This parameter will be removed in a future version. Please either use the `basePath` parameter in the LoadQueue + * constructor, or a `path` property in a manifest definition. */ p.loadFile = function(file, loadNow, basePath) { if (file == null) { @@ -2105,51 +2273,74 @@ TODO: WINDOWS ISSUES this._sendError(event); return; } - this._addItem(file, basePath); + this._addItem(file, null, basePath); if (loadNow !== false) { this.setPaused(false); } else { this.setPaused(true); } - } + }; /** - * Load an array of items. To load a single file, use the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} method. + * Load an array of files. To load a single file, use the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} method. * The files in the manifest are requested in the same order, but may complete in a different order if the max * connections are set above 1 using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}. Scripts will load * in the right order as long as {{#crossLink "LoadQueue/maintainScriptOrder"}}{{/crossLink}} is true (which is * default). * - * Note that files are always appended to the current queue, so this method can be used multiple times to add files. + * Files are always appended to the current queue, so this method can be used multiple times to add files. * To clear the queue first, use the {{#crossLink "AbstractLoader/close"}}{{/crossLink}} method. * @method loadManifest - * @param {Array|String|Object} manifest The list of files to load. If a single object or string is passed, it will - * be loaded the same as a single-item array. Each load item can be either: + * @param {Array|String|Object} manifest An list of files to load. The loadManifest call supports four types of + * manifests: * <ol> - * <li>a path to a resource (string). Note that this kind of load item will be - * converted to an object (see below) in the background.</li> - * <li>OR an object that contains:<ul> - * <li>src: The source of the file that is being loaded. This property is <b>required</b>. - * The source can either be a string (recommended), or an HTML tag. </li> + * <li>A string path, which points to a manifest file, which is a JSON file that contains a "manifest" property, + * which defines the list of files to load, and can optionally contain a "path" property, which will be + * prepended to each file in the list.</li> + * <li>An object which defines a "src", which is a JSON or JSONP file. A "callback" can be defined for JSONP + * file. The JSON/JSONP file should contain a "manifest" property, which defines the list of files to load, + * and can optionally contain a "path" property, which will be prepended to each file in the list.</li> + * <li>An object which contains a "manifest" property, which defines the list of files to load, and can + * optionally contain a "path" property, which will be prepended to each file in the list.</li> + * <li>An Array of files to load.</li> + * </ol> + * + * Each "file" in a manifest can be either: + * <ul> + * <li>A string path to a resource (string). Note that this kind of load item will be converted to an object + * (see below) in the background.</li> + * <li>OR an object that contains:<ul> + * <li>src: The source of the file that is being loaded. This property is <b>required</b>. The source can + * either be a string (recommended), or an HTML tag.</li> * <li>type: The type of file that will be loaded (image, sound, json, etc). PreloadJS does auto-detection * of types using the extension. Supported types are defined on LoadQueue, such as <code>LoadQueue.IMAGE</code>. * It is recommended that a type is specified when a non-standard file URI (such as a php script) us used.</li> * <li>id: A string identifier which can be used to reference the loaded object.</li> - * <li>data: An arbitrary data object, which is returned with the loaded object</li> + * <li>callback: Optional, used for JSONP requests, to define what method to call when the JSONP is loaded.</li> + * <li>data: An arbitrary data object, which is included with the loaded object</li> + * <li>method: used to define if this request uses GET or POST when sending data to the server. The default + * value is "GET"</li> + * <li>values: Optional object of name/value pairs to send to the server.</li> + * <li>headers: Optional object hash of headers to attach to an XHR request. PreloadJS will automatically + * attach some default headers when required, including Origin, Content-Type, and X-Requested-With. You may + * override the default headers if needed.</li> * </ul> - * </ol> + * </ul> * @param {Boolean} [loadNow=true] Kick off an immediate load (true) or wait for a load call (false). The default * value is true. If the queue is paused using {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}} and this value is - * true, the queue will resume automatically. - * @param {String} [basePath] An optional base path prepended to each of the files' source when the file is loaded. - * Sources beginning with http:// or similar will not receive a base path. - * The load items will not be modified. + * `true`, the queue will resume automatically. + * @param {String} [basePath] A base path that will be prepended to each file. The basePath argument overrides the + * path specified in the constructor. Note that if you load a manifest using a file of type {{#crossLink "LoadQueue/MANIFEST:property"}}{{/crossLink}}, + * its files will <strong>NOT</strong> use the basePath parameter. <strong>The basePath parameter is deprecated.</strong> + * This parameter will be removed in a future version. Please either use the `basePath` parameter in the LoadQueue + * constructor, or a `path` property in a manifest definition. */ p.loadManifest = function(manifest, loadNow, basePath) { - var data = null; + var fileList = null; + var path = null; - // Proper list of items + // Array-based list of items if (manifest instanceof Array) { if (manifest.length == 0) { var event = new createjs.Event("error"); @@ -2157,23 +2348,44 @@ TODO: WINDOWS ISSUES this._sendError(event); return; } - data = manifest; + fileList = manifest; - } else { + // String-based. Only file manifests can be specified this way. Any other types will cause an error when loaded. + } else if (typeof(manifest) === "string") { + fileList = [{ + src: manifest, + type: s.MANIFEST + }]; - // Empty/null - if (manifest == null) { - var event = new createjs.Event("error"); - event.text = "PRELOAD_MANIFEST_NULL"; - this._sendError(event); - return; + } else if (typeof(manifest) == "object") { + + // An object that defines a manifest path + if (manifest.src !== undefined) { + if (manifest.type == null) { + manifest.type = s.MANIFEST; + } else if (manifest.type != s.MANIFEST) { + var event = new createjs.Event("error"); + event.text = "PRELOAD_MANIFEST_ERROR"; + this._sendError(event); + } + fileList = [manifest]; + + // An object that defines a manifest + } else if (manifest.manifest !== undefined) { + fileList = manifest.manifest; + path = manifest.path; } - data = [manifest]; + // Unsupported. This will throw an error. + } else { + var event = new createjs.Event("error"); + event.text = "PRELOAD_MANIFEST_NULL"; + this._sendError(event); + return; } - for (var i=0, l=data.length; i<l; i++) { - this._addItem(data[i], basePath); + for (var i=0, l=fileList.length; i<l; i++) { + this._addItem(fileList[i], path, basePath); } if (loadNow !== false) { @@ -2190,19 +2402,23 @@ TODO: WINDOWS ISSUES }; /** - * Look up a load item using either the "id" or "src" that was specified when loading it. + * Look up a load item using either the "id" or "src" that was specified when loading it. Note that if no "id" was + * supplied with the load item, the ID will be the "src", including a `path` property defined by a manifest. The + * `basePath` will not be part of the ID. * @method getItem * @param {String} value The <code>id</code> or <code>src</code> of the load item. * @return {Object} The load item that was initially requested using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} - * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. This object is also returned via the "fileload" event - * as the "item" parameter. + * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. This object is also returned via the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} + * event as the `item` parameter. */ p.getItem = function(value) { return this._loadItemsById[value] || this._loadItemsBySrc[value]; }; /** - * Look up a loaded result using either the "id" or "src" that was specified when loading it. + * Look up a loaded result using either the "id" or "src" that was specified when loading it. Note that if no "id" + * was supplied with the load item, the ID will be the "src", including a `path` property defined by a manifest. The + * `basePath` will not be part of the ID. * @method getResult * @param {String} value The <code>id</code> or <code>src</code> of the load item. * @param {Boolean} [rawResult=false] Return a raw result instead of a formatted result. This applies to content @@ -2211,19 +2427,19 @@ TODO: WINDOWS ISSUES * @return {Object} A result object containing the content that was loaded, such as: * <ul> * <li>An image tag (<image />) for images</li> - * <li>A script tag for JavaScript (<script />). Note that scripts loaded with tags may be added to the - * HTML head.</li> - * <li>A style tag for CSS (<style />)</li> + * <li>A script tag for JavaScript (<script />). Note that scripts are automatically added to the HTML + * DOM.</li> + * <li>A style tag for CSS (<style /> or <link >)</li> * <li>Raw text for TEXT</li> * <li>A formatted JavaScript object defined by JSON</li> * <li>An XML document</li> - * <li>An binary arraybuffer loaded by XHR</li> + * <li>A binary arraybuffer loaded by XHR</li> * <li>An audio tag (<audio >) for HTML audio. Note that it is recommended to use SoundJS APIs to play * loaded audio. Specifically, audio loaded by Flash and WebAudio will return a loader object using this method * which can not be used to play audio back.</li> * </ul> - * This object is also returned via the "fileload" event as the "item" parameter. Note that if a raw result is - * requested, but not found, the result will be returned instead. + * This object is also returned via the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event as the 'item` + * parameter. Note that if a raw result is requested, but not found, the result will be returned instead. */ p.getResult = function(value, rawResult) { var item = this._loadItemsById[value] || this._loadItemsBySrc[value]; @@ -2238,6 +2454,9 @@ TODO: WINDOWS ISSUES /** * Pause or resume the current load. Active loads will not be cancelled, but the next items in the queue will not * be processed when active loads complete. LoadQueues are not paused by default. + * + * Note that if new items are added to the queue using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}, + * a paused queue will be resumed, unless the `loadNow` argument is `false`. * @method setPaused * @param {Boolean} value Whether the queue should be paused or not. */ @@ -2267,14 +2486,18 @@ TODO: WINDOWS ISSUES * method. * @method _addItem * @param {String|Object} value The item to add to the queue. - * @param {String} basePath A path to prepend to the item's source. - * Sources beginning with http:// or similar will not receive a base path. + * @param {String} [path] An optional path prepended to the `src`. The path will only be prepended if the src is + * relative, and does not start with a protocol such as `http://`, or a path like `../`. If the LoadQueue was + * provided a {{#crossLink "_basePath"}}{{/crossLink}}, then it will optionally be prepended after. + * @param {String} [basePath] <strong>Deprecated</strong>An optional basePath passed into a {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} + * or {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} call. This parameter will be removed in a future tagged + * version. * @private */ - p._addItem = function(value, basePath) { - var item = this._createLoadItem(value); + p._addItem = function(value, path, basePath) { + var item = this._createLoadItem(value, path, basePath); // basePath and manifest path are added to the src. if (item == null) { return; } // Sometimes plugins or types should be skipped. - var loader = this._createLoader(item, basePath); + var loader = this._createLoader(item); if (loader != null) { this._loadQueue.push(loader); this._loadQueueBackup.push(loader); @@ -2301,10 +2524,15 @@ TODO: WINDOWS ISSUES * alter the load item. * @method _createLoadItem * @param {String | Object | HTMLAudioElement | HTMLImageElement} value The item that needs to be preloaded. + * @param {String} [path] A path to prepend to the item's source. Sources beginning with http:// or similar will + * not receive a path. Since PreloadJS 0.4.1, the src will be modified to include the `path` and {{#crossLink "LoadQueue/_basePath:property"}}{{/crossLink}} + * when it is added. + * @param {String} [basePath] <strong>Deprectated</strong> A base path to prepend to the items source in addition to + * the path argument. * @return {Object} The loader instance that will be used. * @private */ - p._createLoadItem = function(value) { + p._createLoadItem = function(value, path, basePath) { var item = null; // Create/modify a load item @@ -2314,7 +2542,7 @@ TODO: WINDOWS ISSUES src: value }; break; case "object": - if (window.HTMLAudioElement && value instanceof HTMLAudioElement) { + if (window.HTMLAudioElement && value instanceof window.HTMLAudioElement) { item = { tag: value, src: item.tag.src, @@ -2328,13 +2556,33 @@ TODO: WINDOWS ISSUES return null; } - // Note: This does NOT account for basePath. It should be fine. + // Determine Extension, etc. var match = this._parseURI(item.src); - if (match != null) { item.ext = match[5]; } + if (match != null) { item.ext = match[6]; } if (item.type == null) { item.type = this._getTypeByExtension(item.ext); } + // Inject path & basePath + var bp = ""; // Store the generated basePath + var useBasePath = basePath || this._basePath; + var autoId = item.src; + if (match && match[1] == null && match[3] == null) { + if (path) { + bp = path; + var pathMatch = this._parsePath(path); + autoId = path + autoId; + // Also append basePath + if (useBasePath != null && pathMatch && pathMatch[1] == null && pathMatch[2] == null) { + bp = useBasePath + bp; + } + } else if (useBasePath != null) { + bp = useBasePath; + } + } + item.src = bp + item.src; + item.path = bp; + if (item.type == createjs.LoadQueue.JSON || item.type == createjs.LoadQueue.MANIFEST) { item._loadAsJSONP = (item.callback != null); } @@ -2344,20 +2592,24 @@ TODO: WINDOWS ISSUES } // Create a tag for the item. This ensures there is something to either load with or populate when finished. - if (item.tag == null) { - item.tag = this._createTag(item.type); + if (item.tag === undefined || item.tag === null) { + item.tag = this._createTag(item); } // If there's no id, set one now. - if (item.id == null || item.id == "") { - item.id = item.src; + if (item.id === undefined || item.id === null || item.id === "") { + item.id = autoId; } // Give plugins a chance to modify the loadItem: var customHandler = this._typeCallbacks[item.type] || this._extensionCallbacks[item.ext]; if (customHandler) { - var result = customHandler(item.src, item.type, item.id, item.data); - //Plugin will handle the load, so just ignore it. + // Plugins are now passed both the full source, as well as a combined path+basePath (appropriately) + var result = customHandler.callback.call(customHandler.scope, item.src, item.type, item.id, item.data, + bp, this); + // NOTE: BasePath argument is deprecated. We pass it to plugins.allow SoundJS to modify the file. to sanymore. The full path is sent to the plugin + + // The plugin will handle the load, or has canceled it. Ignore it. if (result === false) { return null; @@ -2368,19 +2620,21 @@ TODO: WINDOWS ISSUES // Result is a loader class: } else { if (result.src != null) { item.src = result.src; } - if (result.id != null) { item.id = result.id; } - if (result.tag != null && result.tag.load instanceof Function) { //Item has what we need load + if (result.id != null) { item.id = result.id; } // TODO: Evaluate this. An overridden ID could be problematic + if (result.tag != null) { // Assumes that the returned tag either has a load method or a src setter. item.tag = result.tag; } - if (result.completeHandler != null) {item.completeHandler = result.completeHandler;} // we have to call back this function when we are done loading + if (result.completeHandler != null) { item.completeHandler = result.completeHandler; } + + // Allow type overriding: + if (result.type) { item.type = result.type; } + + // Update the extension in case the type changed: + match = this._parseURI(item.src); + if (match != null && match[6] != null) { + item.ext = match[6].toLowerCase(); + } } - - // Allow type overriding: - if (result.type) { item.type = result.type; } - - // Update the extension in case the type changed: - match = this._parseURI(item.src); - if (match != null && match[5] != null) { item.ext = match[5].toLowerCase(); } } // Store the item for lookup. This also helps clean-up later. @@ -2394,11 +2648,10 @@ TODO: WINDOWS ISSUES * Create a loader for a load item. * @method _createLoader * @param {Object} item A formatted load item that can be used to generate a loader. - * @param {String} basePath A path that will be prepended on to the source parameter of all items in the queue before they are loaded. Note that a basePath provided to any loadFile or loadManifest call will override the basePath specified on the LoadQueue constructor. * @return {AbstractLoader} A loader that can be used to load content. * @private */ - p._createLoader = function(item, basePath) { + p._createLoader = function(item) { // Initially, try and use the provided/supported XHR mode: var useXHR = this.useXHR; @@ -2421,13 +2674,10 @@ TODO: WINDOWS ISSUES // Note: IMAGE, CSS, SCRIPT, SVG can all use TAGS or XHR. } - // If no basepath was provided here (from _addItem), then use the LoadQueue._basePath instead. - if (basePath == null) { basePath = this._basePath; } - if (useXHR) { - return new createjs.XHRLoader(item, basePath); + return new createjs.XHRLoader(item, this._crossOrigin); } else { - return new createjs.TagLoader(item, basePath); + return new createjs.TagLoader(item); } }; @@ -2507,12 +2757,12 @@ TODO: WINDOWS ISSUES this._numItemsLoaded++; this._updateProgress(); - var event = new createjs.Event("error"); - event.text = "FILE_LOAD_ERROR"; - event.item = loader.getItem(); + var newEvent = new createjs.Event("error"); + newEvent.text = "FILE_LOAD_ERROR"; + newEvent.item = loader.getItem(); // TODO: Propagate actual error message. - this._sendError(event); + this._sendError(newEvent); if (!this.stopOnError) { this._removeLoadItem(loader); @@ -2550,17 +2800,26 @@ TODO: WINDOWS ISSUES } } + // Clean up the load item delete item._loadAsJSONP; + + // If the item was a manifest, then if (item.type == createjs.LoadQueue.MANIFEST) { - var manifest, result = loader.getResult(); - if (result != null && (manifest = result.manifest)) { - this.loadManifest(manifest); + var result = loader.getResult(); + if (result != null && result.manifest !== undefined) { + this.loadManifest(result, true); } } this._processFinishedLoad(item, loader); - } + }; + /** + * @method _processFinishedLoad + * @param {Object} item + * @param {AbstractLoader} loader + * @protected + */ p._processFinishedLoad = function(item, loader) { // Old handleFileTagComplete follows here. this._numItemsLoaded++; @@ -2588,10 +2847,12 @@ TODO: WINDOWS ISSUES if (item === null) { break; } // This is still loading. Do not process further. if (item === true) { continue; } // This has completed, and been processed. Move on. - // This item has finished, and is the next one to get dispatched. + // Append script tags to the head automatically. Tags do this in the loader, but XHR scripts have to maintain order. + var loadItem = this._loadedResults[item.id]; + (document.body || document.getElementsByTagName("body")[0]).appendChild(loadItem); + this._processFinishedLoad(item); this._loadedScripts[i] = true; - i--; l--; } }; @@ -2646,7 +2907,7 @@ TODO: WINDOWS ISSUES loaded += (chunk / remaining) * (remaining/this._numItems); } this._sendProgress(loaded); - } + }; /** * Clean out item results, to free them from memory. Mainly, the loaded item and results are cleared from internal @@ -2672,11 +2933,13 @@ TODO: WINDOWS ISSUES * Note that tags are not appended to the HTML body. * @private */ - p._createTag = function(type) { + p._createTag = function(item) { var tag = null; - switch (type) { + switch (item.type) { case createjs.LoadQueue.IMAGE: - return document.createElement("img"); + tag = document.createElement("img"); + if (this._crossOrigin != "" && !this._isLocal(item)) { tag.crossOrigin = this._crossOrigin; } + return tag; case createjs.LoadQueue.SOUND: tag = document.createElement("audio"); tag.autoplay = false; @@ -2802,7 +3065,7 @@ TODO: WINDOWS ISSUES * Dispatch a filestart event immediately before a file starts to load. Please see the {{#crossLink "LoadQueue/filestart:event"}}{{/crossLink}} * event for details on the event payload. * @method _sendFileStart - * @param {TagLoader | XHRLoader} loader + * @param {Object} item The item that is being loaded. * @protected */ p._sendFileStart = function(item) { @@ -2839,7 +3102,7 @@ TODO: WINDOWS ISSUES BrowserDetect.isOpera = (window.opera != null); BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); BrowserDetect.isIOS = agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1; - } + }; BrowserDetect.init(); @@ -2900,8 +3163,8 @@ this.createjs = this.createjs||{}; * @param {Object} item The item to load. Please see {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} for * information on load items. */ - var TagLoader = function (item, basePath) { - this.init(item, basePath); + var TagLoader = function (item) { + this.init(item); }; var p = TagLoader.prototype = new createjs.AbstractLoader(); @@ -2931,6 +3194,7 @@ this.createjs = this.createjs||{}; * @property _isAudio * @type {Boolean} * @default false + * @protected */ p._isAudio = false; @@ -2953,11 +3217,10 @@ this.createjs = this.createjs||{}; p._jsonResult = null; // Overrides abstract method in AbstractLoader - p.init = function (item, basePath) { + p.init = function (item) { this._item = item; - this._basePath = basePath; this._tag = item.tag; - this._isAudio = (window.HTMLAudioElement && item.tag instanceof HTMLAudioElement); + this._isAudio = (window.HTMLAudioElement && item.tag instanceof window.HTMLAudioElement); this._tagCompleteProxy = createjs.proxy(this._handleLoad, this); }; @@ -2979,7 +3242,6 @@ this.createjs = this.createjs||{}; p.cancel = function() { this.canceled = true; this._clean(); - var item = this.getItem(); }; // Overrides abstract method in AbstractLoader @@ -2987,9 +3249,10 @@ this.createjs = this.createjs||{}; var item = this._item; var tag = this._tag; - // In case we don't get any events. clearTimeout(this._loadTimeout); // Clear out any existing timeout - this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), createjs.LoadQueue.LOAD_TIMEOUT); + var duration = createjs.LoadQueue.LOAD_TIMEOUT; + if (duration == 0) { duration = createjs.LoadQueue.loadTimeout; } + this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), duration); if (this._isAudio) { tag.src = null; // Unset the source so we can set the preload type to "auto" without kicking off a load. This is only necessary for audio tags passed in by the developer. @@ -3010,7 +3273,7 @@ this.createjs = this.createjs||{}; tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this); } - var src = this.buildPath(item.src, this._basePath, item.values); + var src = this.buildPath(item.src, item.values); // Set the src after the events are all added. switch(item.type) { @@ -3128,14 +3391,16 @@ this.createjs = this.createjs||{}; var item = this.getItem(); var tag = item.tag; - if (this.loaded || this.isAudio && tag.readyState !== 4) { return; } //LM: Not sure if we still need the audio check. + if (this.loaded || this._isAudio && tag.readyState !== 4) { return; } //LM: Not sure if we still need the audio check. this.loaded = true; // Remove from the DOM switch (item.type) { case createjs.LoadQueue.SVG: + case createjs.LoadQueue.JSON: case createjs.LoadQueue.JSONP: // Note: Removing script tags is a fool's errand. case createjs.LoadQueue.MANIFEST: + case createjs.LoadQueue.CSS: // case createjs.LoadQueue.CSS: //LM: We may need to remove CSS tags loaded using a LINK tag.style.visibility = this._startTagVisibility; @@ -3158,16 +3423,25 @@ this.createjs = this.createjs||{}; clearTimeout(this._loadTimeout); // Delete handlers. - var tag = this.getItem().tag; - tag.onload = null; - tag.removeEventListener && tag.removeEventListener("canplaythrough", this._tagCompleteProxy, false); - tag.onstalled = null; - tag.onprogress = null; - tag.onerror = null; + var item = this.getItem(); + var tag = item.tag; + if (tag != null) { + tag.onload = null; + tag.removeEventListener && tag.removeEventListener("canplaythrough", this._tagCompleteProxy, false); + tag.onstalled = null; + tag.onprogress = null; + tag.onerror = null; - //TODO: Test this - if (tag.parentNode) { - tag.parentNode.removeChild(tag); + //TODO: Test this + if (tag.parentNode != null + && item.type == createjs.LoadQueue.SVG + && item.type == createjs.LoadQueue.JSON + && item.type == createjs.LoadQueue.MANIFEST + && item.type == createjs.LoadQueue.CSS + && item.type == createjs.LoadQueue.JSONP) { + // Note: Removing script tags is a fool's errand. + tag.parentNode.removeChild(tag); + } } var item = this.getItem(); @@ -3179,7 +3453,7 @@ this.createjs = this.createjs||{}; p.toString = function() { return "[PreloadJS TagLoader]"; - } + }; createjs.TagLoader = TagLoader; @@ -3233,12 +3507,32 @@ this.createjs = this.createjs || {}; * @constructor * @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} * for an overview of supported file properties. + * @param {String} [crossOrigin] An optional flag to support images loaded from a CORS-enabled server. Please see + * {{#crossLink "LoadQueue/_crossOrigin:property"}}{{/crossLink}} for more info. * @extends AbstractLoader */ - var XHRLoader = function (item, basePath) { - this.init(item, basePath); + var XHRLoader = function (item, crossOrigin) { + this.init(item, crossOrigin); }; + var s = XHRLoader; + + /** + * A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE. + * @property ACTIVEX_VERSIONS + * @type {Array} + * @since 0.4.2 + * @private + */ + s.ACTIVEX_VERSIONS = [ + "Msxml2.XMLHTTP.6.0", + "Msxml2.XMLHTTP.5.0", + "Msxml2.XMLHTTP.4.0", + "MSXML2.XMLHTTP.3.0", + "MSXML2.XMLHTTP", + "Microsoft.XMLHTTP" + ]; + var p = XHRLoader.prototype = new createjs.AbstractLoader(); //Protected @@ -3288,10 +3582,19 @@ this.createjs = this.createjs || {}; */ p._rawResponse = null; + /** + * See {{#crossLink "LoadQueue/_crossOrigin:property"}}{{/crossLink}} + * @property _crossOrigin + * @type {String} + * @defaultValue "" + * @private + */ + p._crossOrigin = ""; + // Overrides abstract method in AbstractLoader - p.init = function (item, basePath) { + p.init = function (item, crossOrigin) { this._item = item; - this._basePath = basePath; + this._crossOrigin = crossOrigin; if (!this._createXHR(item)) { //TODO: Throw error? } @@ -3345,7 +3648,13 @@ this.createjs = this.createjs || {}; this._request.ontimeout = createjs.proxy(this._handleTimeout, this); // Set up a timeout if we don't have XHR2 if (this._xhrLevel == 1) { - this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), createjs.LoadQueue.LOAD_TIMEOUT); + var duration = createjs.LoadQueue.LOAD_TIMEOUT; + if (duration == 0) { + duration = createjs.LoadQueue.loadTimeout; + } else { + try { console.warn("LoadQueue.LOAD_TIMEOUT has been deprecated in favor of LoadQueue.loadTimeout");} catch(e) {} + } + this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), duration); } // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these. @@ -3440,9 +3749,9 @@ this.createjs = this.createjs || {}; */ p._handleAbort = function (event) { this._clean(); - var event = new createjs.Event("error"); - event.text = "XHR_ABORTED"; - this._sendError(event); + var newEvent = new createjs.Event("error"); + newEvent.text = "XHR_ABORTED"; + this._sendError(newEvent); }; /** @@ -3582,42 +3891,31 @@ this.createjs = this.createjs || {}; */ p._createXHR = function (item) { // Check for cross-domain loads. We can't fully support them, but we can try. - var target = document.createElement("a"); - target.href = this.buildPath(item.src, this._basePath); + var crossdomain = this._isCrossDomain(item); + var headers = {}; - var host = document.createElement("a"); - host.href = location.href; - - var crossdomain = (target.hostname != "") && - (target.port != host.port || - target.protocol != host.protocol || - target.hostname != host.hostname); - - // Create the request. Fall back to whatever support we have. + // Create the request. Fallback to whatever support we have. var req = null; - if (crossdomain && window.XDomainRequest) { - req = new XDomainRequest(); // Note: IE9 will fail if this is not actually cross-domain. - } else if (window.XMLHttpRequest) { // Old IE versions use a different approach + if (window.XMLHttpRequest) { req = new XMLHttpRequest(); - } else { - try { - req = new ActiveXObject("Msxml2.XMLHTTP.6.0"); - } catch (e) { - try { - req = new ActiveXObject("Msxml2.XMLHTTP.3.0"); - } catch (e) { - try { - req = new ActiveXObject("Msxml2.XMLHTTP"); - } catch (e) { - return false; - } - } + // This is 8 or 9, so use XDomainRequest instead. + if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) { + req = new XDomainRequest(); } + } else { // Old IE versions use a different approach + for (var i = 0, l=s.ACTIVEX_VERSIONS.length; i<l; i++) { + var axVersion = s.ACTIVEX_VERSIONS[i]; + try { + req = new ActiveXObject(axVersions); + break; + } catch (e) {} + } + if (req == null) { return false; } } // IE9 doesn't support overrideMimeType(), so we need to check for it. - if (item.type == createjs.LoadQueue.TEXT && req.overrideMimeType) { - req.overrideMimeType("text/plain; charset=x-user-defined"); + if (createjs.LoadQueue.isText(item.type) && req.overrideMimeType) { + req.overrideMimeType("text/plain; charset=utf-8"); } // Determine the XHR level @@ -3625,29 +3923,44 @@ this.createjs = this.createjs || {}; var src = null; if (item.method == createjs.LoadQueue.GET) { - src = this.buildPath(item.src, this._basePath, item.values); + src = this.buildPath(item.src, item.values); } else { - src = this.buildPath(item.src, this._basePath); + src = item.src; } // Open the request. Set cross-domain flags if it is supported (XHR level 1 only) req.open(item.method || createjs.LoadQueue.GET, src, true); if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) { - req.setRequestHeader("Origin", location.origin); + headers["Origin"] = location.origin; } // To send data we need to set the Content-type header) - if (item.values && item.method == createjs.LoadQueue.POST) { - req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - } + if (item.values && item.method == createjs.LoadQueue.POST) { + headers["Content-Type"] = "application/x-www-form-urlencoded"; + } + + if (!crossdomain && !headers["X-Requested-With"]) { + headers["X-Requested-With"] = "XMLHttpRequest"; + } + + if (item.headers) { + for (var n in item.headers) { + headers[n] = item.headers[n]; + } + } // Binary files are loaded differently. if (createjs.LoadQueue.isBinary(item.type)) { req.responseType = "arraybuffer"; } + for (n in headers) { + req.setRequestHeader(n, headers[n]) + } + this._request = req; + return true; }; @@ -3686,7 +3999,8 @@ this.createjs = this.createjs || {}; // Note: Images need to wait for onload, but do use the cache. case createjs.LoadQueue.IMAGE: tag.onload = createjs.proxy(this._handleTagReady, this); - tag.src = this.buildPath(this._item.src, this._basePath, this._item.values); + if (this._crossOrigin != "") { tag.crossOrigin = "Anonymous"; }// We can assume this, since XHR images are always loaded on a server. + tag.src = this.buildPath(this._item.src, this._item.values); this._rawResponse = this._response; this._response = tag; @@ -3698,7 +4012,6 @@ this.createjs = this.createjs || {}; this._rawResponse = this._response; this._response = tag; - (document.body || document.getElementsByTagName("body")[0]).appendChild(tag); return true; case createjs.LoadQueue.CSS: @@ -3761,14 +4074,18 @@ this.createjs = this.createjs || {}; */ p._parseXML = function (text, type) { var xml = null; - if (window.DOMParser) { - var parser = new DOMParser(); - xml = parser.parseFromString(text, type); // OJR Opera throws DOMException: NOT_SUPPORTED_ERR // potential solution https://gist.github.com/1129031 - } else { // IE - xml = new ActiveXObject("Microsoft.XMLDOM"); - xml.async = false; - xml.loadXML(text); - } + try { + // CocoonJS does not support XML parsing with either method. + // Windows (?) Opera DOMParser throws DOMException: NOT_SUPPORTED_ERR // potential solution https://gist.github.com/1129031 + if (window.DOMParser) { + var parser = new DOMParser(); + xml = parser.parseFromString(text, type); + } else { // IE + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = false; + xml.loadXML(text); + } + } catch (e) {} return xml; }; @@ -3778,12 +4095,14 @@ this.createjs = this.createjs || {}; * @private */ p._handleTagReady = function () { + var tag = this._item.tag; + tag && (tag.onload = null); this._sendComplete(); - } + }; p.toString = function () { return "[PreloadJS XHRLoader]"; - } + }; createjs.XHRLoader = XHRLoader; diff --git a/vendor/scripts/soundjs-NEXT.combined.js b/vendor/scripts/soundjs-NEXT.combined.js index 4c5dae38c..09e8920c7 100644 --- a/vendor/scripts/soundjs-NEXT.combined.js +++ b/vendor/scripts/soundjs-NEXT.combined.js @@ -27,7 +27,7 @@ this.createjs = this.createjs || {}; * @type String * @static **/ - s.buildDate = /*date*/"Wed, 27 Nov 2013 19:49:18 GMT"; // injected by build process + s.buildDate = /*date*/"Tue, 21 Jan 2014 18:00:36 GMT"; // injected by build process })(); /* @@ -142,6 +142,7 @@ var p = EventDispatcher.prototype; target.hasEventListener = p.hasEventListener; target.dispatchEvent = p.dispatchEvent; target._dispatchEvent = p._dispatchEvent; + target.willTrigger = p.willTrigger; }; // constructor: @@ -358,7 +359,7 @@ var p = EventDispatcher.prototype; }; /** - * Indicates whether there is at least one listener for the specified event type and `useCapture` value. + * Indicates whether there is at least one listener for the specified event type. * @method hasEventListener * @param {String} type The string type of the event. * @return {Boolean} Returns true if there is at least one listener for the specified event. @@ -367,6 +368,26 @@ var p = EventDispatcher.prototype; var listeners = this._listeners, captureListeners = this._captureListeners; return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type])); }; + + /** + * Indicates whether there is at least one listener for the specified event type on this object or any of its + * ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the + * specified type is dispatched from this object, it will trigger at least one listener. + * + * This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire + * event flow for a listener, not just this object. + * @method willTrigger + * @param {String} type The string type of the event. + * @return {Boolean} Returns `true` if there is at least one listener for the specified event. + **/ + p.willTrigger = function(type) { + var o = this; + while (o) { + if (o.hasEventListener(type)) { return true; } + o = o.parent; + } + return false; + }; /** * @method toString @@ -880,9 +901,6 @@ this.createjs = this.createjs || {}; * <li>Master volume, mute, and stop controls for all sounds at once</li> * </ul> * - * <b>Please note that as of version 0.4.0, the "SoundJS" object only provides version information. All APIs from - * SoundJS are now available on the {{#crossLink "Sound"}}{{/crossLink}} class.</b> - * * <b>Controlling Sounds</b><br /> * Playing sounds creates {{#crossLink "SoundInstance"}}{{/crossLink}} instances, which can be controlled individually. * <ul><li>Pause, resume, seek, and stop sounds</li> @@ -997,7 +1015,7 @@ this.createjs = this.createjs || {}; * <b>Firefox 25 Web Audio limitations</b> * <ul><li>mp3 audio files do not load properly on all windows machines, reported * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br> - * For this reason it is recommended to pass ogg file first until this bug is resolved, if possible.</li></ul> + * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.</li></ul> * <b>Safari limitations</b><br /> * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul> @@ -1005,10 +1023,9 @@ this.createjs = this.createjs || {}; * <b>iOS 6 Web Audio limitations</b><br /> * <ul><li>Sound is initially muted and will only unmute through play being called inside a user initiated event * (touch/click).</li> - * <li>Despite suggestions to the opposite, we have control over audio volume through our gain nodes.</li> - * <li>A bug exists that will distort un-cached web audio when a video element is present in the DOM.</li> + * <li>A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio at a different sampleRate.</li> * <li>Note HTMLAudioPlugin is not supported on iOS by default. See {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} - * for more details.<li> + * for more details.</li> * </ul> * * <b>Android HTML Audio limitations</b><br /> @@ -1028,8 +1045,10 @@ this.createjs = this.createjs || {}; var s = Sound; /** + * DEPRECATED * This approach has is being replaced by {{#crossLink "Sound/alternateExtensions:property"}}{{/crossLink}}, and * support will be removed in the next version. + * * The character (or characters) that are used to split multiple paths from an audio source. * @property DELIMITER * @type {String} @@ -1039,16 +1058,6 @@ this.createjs = this.createjs || {}; */ s.DELIMITER = "|"; - /** - * The duration in milliseconds to determine a timeout. - * @property AUDIO_TIMEOUT - * @static - * @type {Number} - * @default 8000 - * @protected - */ - s.AUDIO_TIMEOUT = 8000; // TODO: This is not implemented // OJR remove property? doc'd as protected to remove from docs for now - /** * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of * instances of the sound are already playing. @@ -1143,12 +1152,12 @@ this.createjs = this.createjs || {}; * * NOTE this does not currently work for {{#crossLink "FlashPlugin"}}{{/crossLink}}. * - * More details on file formats can be found at http://en.wikipedia.org/wiki/Audio_file_format. A very detailed - * list of file formats can be found at http://www.fileinfo.com/filetypes/audio. A useful list of extensions for - * each format can be found at http://html5doctor.com/html5-audio-the-state-of-play/ + * More details on file formats can be found at <a href="http://en.wikipedia.org/wiki/Audio_file_format" target="_blank">http://en.wikipedia.org/wiki/Audio_file_format</a>.<br /> + * A very detailed list of file formats can be found at <a href="http://www.fileinfo.com/filetypes/audio" target="_blank">http://www.fileinfo.com/filetypes/audio</a>. * @property SUPPORTED_EXTENSIONS * @type {Array[String]} * @default ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"] + * @since 0.4.0 */ s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]; // OJR FlashPlugin does not currently support @@ -1156,9 +1165,12 @@ this.createjs = this.createjs || {}; * Some extensions use another type of extension support to play (one of them is a codex). This allows you to map * that support so plugins can accurately determine if an extension is supported. Adding to this list can help * plugins determine more accurately if an extension is supported. + * + * A useful list of extensions for each format can be found at <a href="http://html5doctor.com/html5-audio-the-state-of-play/" target="_blank">http://html5doctor.com/html5-audio-the-state-of-play/</a>. * @property EXTENSION_MAP * @type {Object} * @since 0.4.0 + * @default {m4a:"mp4"} */ s.EXTENSION_MAP = { m4a:"mp4" @@ -1181,7 +1193,7 @@ this.createjs = this.createjs || {}; * is called without passing a value for interrupt. * @property defaultInterruptBehavior * @type {String} - * @default none + * @default Sound.INTERRUPT_NONE, or "none" * @static * @since 0.4.0 */ @@ -1190,19 +1202,21 @@ this.createjs = this.createjs || {}; /** * An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin. * These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your - * extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. These audio files need - * to exist in the same location. + * extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need + * to exist in the same location, as only the extension is altered. * + * Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}} + * and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading. * <h4>Example</h4> - * var manifest = [ - * {src:"asset0.ogg", id:"example"}, - * ]; - * createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3 - * createjs.Sound.addEventListener("fileload", handleLoad); // call handleLoad when each sound loads - * createjs.Sound.registerManifest(manifest, assetPath); + * var manifest = [ + * {src:"myPath/mySound.ogg", id:"example"}, + * ]; + * createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3 + * createjs.Sound.addEventListener("fileload", handleLoad); // call handleLoad when each sound loads + * createjs.Sound.registerManifest(manifest, assetPath); + * // ... + * createjs.Sound.play("myPath/mySound.ogg"); // works regardless of what extension is supported. Note calling with ID is a better approach * - * Note that regardless of which file is loaded, you can create and play instances using the id or the same - * assetPath + src passed for loading. * @property alternateExtensions * @type {Array} * @since 0.5.2 @@ -1211,12 +1225,12 @@ this.createjs = this.createjs || {}; /** * Used internally to assign unique IDs to each SoundInstance. - * @property lastID + * @property _lastID * @type {Number} * @static * @protected */ - s.lastId = 0; + s._lastID = 0; /** * The currently active plugin. If this is null, then no plugin could be initialized. If no plugin was specified, @@ -1232,80 +1246,80 @@ this.createjs = this.createjs || {}; * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default * plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}). * If plugins have been registered, but none are applicable, then sound playback will fail. - * @property pluginsRegistered + * @property _pluginsRegistered * @type {Boolean} * @default false * @static * @protected */ - s.pluginsRegistered = false; + s._pluginsRegistered = false; /** * The master volume value, which affects all sounds. Use {{#crossLink "Sound/getVolume"}}{{/crossLink}} and * {{#crossLink "Sound/setVolume"}}{{/crossLink}} to modify the volume of all audio. - * @property masterVolume + * @property _masterVolume * @type {Number} * @default 1 * @protected * @since 0.4.0 */ - s.masterVolume = 1; + s._masterVolume = 1; /** * The master mute value, which affects all sounds. This is applies to all sound instances. This value can be set * through {{#crossLink "Sound/setMute"}}{{/crossLink}} and accessed via {{#crossLink "Sound/getMute"}}{{/crossLink}}. - * @property masterMute + * @property _masterMute * @type {Boolean} * @default false * @protected * @static * @since 0.4.0 */ - s.masterMute = false; + s._masterMute = false; /** * An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}. * When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}} - * method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/beginPlaying"}}{{/crossLink}} + * method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}} * method. - * @property instances + * @property _instances * @type {Array} * @protected * @static */ - s.instances = []; + s._instances = []; /** * An object hash storing sound sources via there corresponding ID. - * @property idHash + * @property _idHash * @type {Object} * @protected * @static */ - s.idHash = {}; + s._idHash = {}; /** * An object hash that stores preloading sound sources via the parsed source that is passed to the plugin. Contains the * source, id, and data that was passed in by the user. Parsed sources can contain multiple instances of source, id, * and data. - * @property preloadHash + * @property _preloadHash * @type {Object} * @protected * @static */ - s.preloadHash = {}; + s._preloadHash = {}; /** * An object that stands in for audio that fails to play. This allows developers to continue to call methods * on the failed instance without having to check if it is valid first. The instance is instantiated once, and * shared to keep the memory footprint down. - * @property defaultSoundInstance + * @property _defaultSoundInstance * @type {Object} * @protected * @static */ - s.defaultSoundInstance = null; + s._defaultSoundInstance = null; // mix-ins: // EventDispatcher methods: @@ -1344,19 +1358,19 @@ this.createjs = this.createjs || {}; /** * Used by external plugins to dispatch file load events. - * @method sendFileLoadEvent + * @method _sendFileLoadEvent * @param {String} src A sound file has completed loading, and should be dispatched. * @protected * @static * @since 0.4.1 */ - s.sendFileLoadEvent = function (src) { - if (!s.preloadHash[src]) { + s._sendFileLoadEvent = function (src) { + if (!s._preloadHash[src]) { return; } - for (var i = 0, l = s.preloadHash[src].length; i < l; i++) { - var item = s.preloadHash[src][i]; - s.preloadHash[src][i] = true; + for (var i = 0, l = s._preloadHash[src].length; i < l; i++) { + var item = s._preloadHash[src][i]; + s._preloadHash[src][i] = true; if (!s.hasEventListener("fileload")) { continue; } @@ -1402,6 +1416,11 @@ this.createjs = this.createjs || {}; * @deprecated */ s.registerPlugin = function (plugin) { + try { + console.log("createjs.Sound.registerPlugin has been deprecated. Please use registerPlugins."); + } catch (err) { + // you are in IE with the console closed, you monster + } return s._registerPlugin(plugin); }; @@ -1422,7 +1441,7 @@ this.createjs = this.createjs || {}; * @private */ s._registerPlugin = function (plugin) { - s.pluginsRegistered = true; + s._pluginsRegistered = true; if (plugin == null) { return false; } @@ -1462,19 +1481,18 @@ this.createjs = this.createjs || {}; * the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the * default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}. * - * * <h4>Example</h4> - * if (!createjs.initializeDefaultPlugins()) { return; } + * <h4>Example</h4> + * if (!createjs.initializeDefaultPlugins()) { return; } * * @method initializeDefaultPlugins - * @returns {Boolean} If a plugin is initialized (true) or not (false). If the browser does not have the - * capabilities to initialize any available plugins, this will return false. + * @returns {Boolean} True if a plugin was initialized, false otherwise. * @since 0.4.0 */ s.initializeDefaultPlugins = function () { if (s.activePlugin != null) { return true; } - if (s.pluginsRegistered) { + if (s._pluginsRegistered) { return false; } if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) { @@ -1489,10 +1507,10 @@ this.createjs = this.createjs || {}; * <h4>Example</h4> * This example sets up a Flash fallback, but only if there is no plugin specified yet. * - * if (!createjs.Sound.isReady()) { - * createjs.FlashPlugin.swfPath = "../src/SoundJS/"; - * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashPlugin]); - * } + * if (!createjs.Sound.isReady()) { + * createjs.FlashPlugin.swfPath = "../src/SoundJS/"; + * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashPlugin]); + * } * * @method isReady * @return {Boolean} If Sound has initialized a plugin. @@ -1508,6 +1526,9 @@ this.createjs = this.createjs || {}; * <ul> * <li><b>panning:</b> If the plugin can pan audio from left to right</li> * <li><b>volume;</b> If the plugin can control audio volume.</li> + * <li><b>tracks:</b> The maximum number of audio tracks that can be played back at a time. This will be -1 + * if there is no known limit.</li> + * <br />An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}: * <li><b>mp3:</b> If MP3 audio is supported.</li> * <li><b>ogg:</b> If OGG audio is supported.</li> * <li><b>wav:</b> If WAV audio is supported.</li> @@ -1517,8 +1538,7 @@ this.createjs = this.createjs || {}; * <li><b>aiff:</b> If aiff audio is supported.</li> * <li><b>wma:</b> If wma audio is supported.</li> * <li><b>mid:</b> If mid audio is supported.</li> - * <li><b>tracks:</b> The maximum number of audio tracks that can be played back at a time. This will be -1 - * if there is no known limit.</li> + * </ul> * @method getCapabilities * @return {Object} An object containing the capabilities of the active plugin. * @static @@ -1527,7 +1547,7 @@ this.createjs = this.createjs || {}; if (s.activePlugin == null) { return null; } - return s.activePlugin.capabilities; + return s.activePlugin._capabilities; }; /** @@ -1547,7 +1567,7 @@ this.createjs = this.createjs || {}; if (s.activePlugin == null) { return null; } - return s.activePlugin.capabilities[key]; + return s.activePlugin._capabilities[key]; }; /** @@ -1594,7 +1614,7 @@ this.createjs = this.createjs || {}; * channels for an audio instance, however a "channels" property can be appended to the data object if it is used * for other information. The audio channels will set a default based on plugin if no value is found. * @param {Boolean} [preload=true] If the sound should be internally preloaded so that it can be played back - * without an external preloader. + * without an external preloader. This is currently used by PreloadJS when loading sounds to disable internal preloading. * @param {string} basePath Set a path that will be prepended to src for loading. * @return {Object} An object with the modified values that were passed in, which defines the sound. * Returns false if the source cannot be parsed or no plugins can be initialized. @@ -1618,17 +1638,20 @@ this.createjs = this.createjs || {}; // branch to different parse based on alternate formats setting if (s.alternateExtensions.length) { - var details = s.parsePath2(src, "sound", id, data); + var details = s._parsePath2(src, "sound", id, data); } else { - var details = s.parsePath(src, "sound", id, data); + var details = s._parsePath(src, "sound", id, data); } if (details == null) { return false; } - if (basePath != null) {details.src = basePath + details.src;} + if (basePath != null) { + src = basePath + src; + details.src = basePath + details.src; + } if (id != null) { - s.idHash[id] = details.src; + s._idHash[id] = details.src; } var numChannels = null; // null tells SoundChannel to set this to it's internal maxDefault @@ -1656,6 +1679,7 @@ this.createjs = this.createjs || {}; } // If the loader returns a tag, return it instead for preloading. + // OJR all loaders currently use tags? if (loader.tag != null) { details.tag = loader.tag; } else if (loader.src) { @@ -1671,16 +1695,16 @@ this.createjs = this.createjs || {}; } if (preload != false) { - if (!s.preloadHash[details.src]) { - s.preloadHash[details.src] = []; + if (!s._preloadHash[details.src]) { + s._preloadHash[details.src] = []; } // we do this so we can store multiple id's and data if needed - s.preloadHash[details.src].push({src:src, id:id, data:data}); // keep this data so we can return it in fileload event - if (s.preloadHash[details.src].length == 1) { + s._preloadHash[details.src].push({src:src, id:id, data:data}); // keep this data so we can return it in fileload event + if (s._preloadHash[details.src].length == 1) { // if already loaded once, don't load a second time // OJR note this will disallow reloading a sound if loading fails or the source changes s.activePlugin.preload(details.src, loader); } else { // if src already loaded successfully, return true - if (s.preloadHash[details.src][0] == true) {return true;} + if (s._preloadHash[details.src][0] == true) {return true;} } } @@ -1704,13 +1728,13 @@ this.createjs = this.createjs || {}; * * @method registerManifest * @param {Array} manifest An array of objects to load. Objects are expected to be in the format needed for - * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data, preload:UseInternalPreloader}</code> - * with "id", "data", and "preload" being optional. - * @param {string} basePath Set a path that will be prepended to each src when loading. When creating or playing + * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code> + * with "id" and "data" being optional. + * @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing * audio that was loaded with a basePath by src, the basePath must be included. * @return {Object} An array of objects with the modified values that were passed in, which defines each sound. - * Like registerSound, it will return false for any values that the source cannot be parsed or if no plugins can be initialized. - * Also, it will returns true for any values that the source is already loaded. + * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized. + * Also, it will return true for any values when the source is already loaded. * @static * @since 0.4.0 */ @@ -1718,15 +1742,15 @@ this.createjs = this.createjs || {}; var returnValues = []; for (var i = 0, l = manifest.length; i < l; i++) { returnValues[i] = createjs.Sound.registerSound(manifest[i].src, manifest[i].id, manifest[i].data, manifest[i].preload, basePath); - } + } // OJR consider removing .preload from args, as it is only used by PreloadJS return returnValues; }; /** * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or * {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. - * Note this will stop playback on active instances playing this sound before deleting them. - * Note if you passed in a basePath, you need to prepend it to the src here. + * <br />Note this will stop playback on active instances playing this sound before deleting them. + * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here. * * <h4>Example</h4> * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg"); @@ -1747,12 +1771,12 @@ this.createjs = this.createjs || {}; if (src instanceof Object) { src = src.src; } - src = s.getSrcById(src); + src = s._getSrcById(src); if (s.alternateExtensions.length) { - var details = s.parsePath2(src); + var details = s._parsePath2(src); } else { - var details = s.parsePath(src); + var details = s._parsePath(src); } if (details == null) { return false; @@ -1760,18 +1784,18 @@ this.createjs = this.createjs || {}; if (basePath != null) {details.src = basePath + details.src;} src = details.src; - // remove src from idHash // Note "for in" can be a slow operation - for(var prop in s.idHash){ - if(s.idHash[prop] == src) { - delete(s.idHash[prop]); + // remove src from _idHash // Note "for in" can be a slow operation + for(var prop in s._idHash){ + if(s._idHash[prop] == src) { + delete(s._idHash[prop]); } } // clear from SoundChannel, which also stops and deletes all instances SoundChannel.removeSrc(src); - // remove src from preloadHash - delete(s.preloadHash[src]); + // remove src from _preloadHash + delete(s._preloadHash[src]); // activePlugin cleanup s.activePlugin.removeSound(src); @@ -1782,8 +1806,8 @@ this.createjs = this.createjs || {}; /** * Remove a manifest of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or * {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. - * Note this will stop playback on active instances playing this audio before deleting them. - * Note if you passed in a basePath, you do not need to pass it or add it to the src here. + * <br />Note this will stop playback on active instances playing this audio before deleting them. + * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here. * * <h4>Example</h4> * var manifest = [ @@ -1813,7 +1837,7 @@ this.createjs = this.createjs || {}; /** * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or * {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. - * Note this will stop playback on all active sound instances before deleting them. + * <br />Note this will stop playback on all active sound instances before deleting them. * * <h4>Example</h4> * createjs.Sound.removeAllSounds(); @@ -1823,8 +1847,8 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ s.removeAllSounds = function() { - s.idHash = {}; - s.preloadHash = {}; + s._idHash = {}; + s._preloadHash = {}; SoundChannel.removeAll(); s.activePlugin.removeAllSounds(); }; @@ -1846,16 +1870,16 @@ this.createjs = this.createjs || {}; */ s.loadComplete = function (src) { if (s.alternateExtensions.length) { - var details = s.parsePath2(src, "sound"); + var details = s._parsePath2(src, "sound"); } else { - var details = s.parsePath(src, "sound"); + var details = s._parsePath(src, "sound"); } if (details) { - src = s.getSrcById(details.src); + src = s._getSrcById(details.src); } else { - src = s.getSrcById(src); + src = s._getSrcById(src); } - return (s.preloadHash[src][0] == true); // src only loads once, so if it's true for the first it's true for all + return (s._preloadHash[src][0] == true); // src only loads once, so if it's true for the first it's true for all }; /** @@ -1863,7 +1887,7 @@ this.createjs = this.createjs || {}; * composite paths using {{#crossLink "Sound/DELIMITER:property"}}{{/crossLink}}, which defaults to "|". The first path supported by the * current browser/plugin will be used. * NOTE the "|" approach is deprecated and will be removed in the next version - * @method parsePath + * @method _parsePath * @param {String} value The path to an audio source. * @param {String} [type] The type of path. This will typically be "sound" or null. * @param {String} [id] The user-specified sound ID. This may be null, in which case the src will be used instead. @@ -1873,9 +1897,16 @@ this.createjs = this.createjs || {}; * and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. * @protected */ - s.parsePath = function (value, type, id, data) { + s._parsePath = function (value, type, id, data) { if (typeof(value) != "string") {value = value.toString();} var sounds = value.split(s.DELIMITER); + if (sounds.length > 1) { + try { + console.log("createjs.Sound.DELIMITER \"|\" loading approach has been deprecated. Please use the new alternateExtensions property."); + } catch (err) { + // you are in IE with the console closed, you monster + } + } var ret = {type:type || "sound", id:id, data:data}; var c = s.getCapabilities(); for (var i = 0, l = sounds.length; i < l; i++) { @@ -1898,8 +1929,8 @@ this.createjs = this.createjs || {}; return null; }; - // new approach, when old approach is deprecated this will become parsePath - s.parsePath2 = function (value, type, id, data) { + // new approach, when old approach is deprecated this will become _parsePath + s._parsePath2 = function (value, type, id, data) { if (typeof(value) != "string") {value = value.toString();} var match = value.match(s.FILE_PATTERN); @@ -1932,18 +1963,17 @@ this.createjs = this.createjs || {}; * SoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}. * Note that even on sounds with failed playback, you may still be able to call SoundInstance {{#crossLink "SoundInstance/play"}}{{/crossLink}}, * since the failure could be due to lack of available channels. If the src does not have a supported extension or - * if there is no available plugin, {{#crossLink "Sound/defaultSoundInstance:property"}}{{/crossLink}} will be - * returned, which will not play any audio, but will not generate errors. + * if there is no available plugin, a default SoundInstance will be returned which will not play any audio, but will not generate errors. * * <h4>Example</h4> * createjs.Sound.addEventListener("fileload", handleLoad); * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3); * function handleLoad(event) { * createjs.Sound.play("myID"); - * // alternately we could call the following + * // we can pass in options we want to set inside of an object, and store off SoundInstance for controlling + * var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1}); + * // alternately, we can pass full source path and specify each argument individually * var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", createjs.Sound.INTERRUPT_ANY, 0, 0, -1, 1, 0); - * // another alternative is to pass in just the options we want to set inside of an object - * var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1}); * } * * @method play @@ -1967,7 +1997,7 @@ this.createjs = this.createjs || {}; s.play = function (src, interrupt, delay, offset, loop, volume, pan) { var instance = s.createInstance(src); - var ok = s.playInstance(instance, interrupt, delay, offset, loop, volume, pan); + var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan); if (!ok) { instance.playFailed(); } @@ -1976,8 +2006,8 @@ this.createjs = this.createjs || {}; /** * Creates a {{#crossLink "SoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a - * supported extension or if there is no available plugin, a {{#crossLink "Sound/defaultSoundInstance:property"}}{{/crossLink}} - * will be returned that can be called safely but does nothing. + * supported extension or if there is no available plugin, a default SoundInstance will be returned that can be + * called safely but does nothing. * * <h4>Example</h4> * var myInstance = null; @@ -1997,15 +2027,15 @@ this.createjs = this.createjs || {}; */ s.createInstance = function (src) { if (!s.initializeDefaultPlugins()) { - return s.defaultSoundInstance; + return s._defaultSoundInstance; } - src = s.getSrcById(src); + src = s._getSrcById(src); if (s.alternateExtensions.length) { - var details = s.parsePath2(src, "sound"); + var details = s._parsePath2(src, "sound"); } else { - var details = s.parsePath(src, "sound"); + var details = s._parsePath(src, "sound"); } var instance = null; @@ -2016,11 +2046,11 @@ this.createjs = this.createjs || {}; } else { // the src is not supported, so give back a dummy instance. // This can happen if PreloadJS fails because the plugin does not support the ext, and was passed an id which - // will not get added to the idHash. - instance = Sound.defaultSoundInstance; + // will not get added to the _idHash. + instance = Sound._defaultSoundInstance; } - instance.uniqueId = s.lastId++; // OJR moved this here so we can have multiple plugins active in theory + instance.uniqueId = s._lastID++; return instance; }; @@ -2042,9 +2072,9 @@ this.createjs = this.createjs || {}; return false; } value = Math.max(0, Math.min(1, value)); - s.masterVolume = value; + s._masterVolume = value; if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) { - var instances = this.instances; // OJR does this impact garbage collection more than it helps performance? + var instances = this._instances; // OJR does this impact garbage collection more than it helps performance? for (var i = 0, l = instances.length; i < l; i++) { instances[i].setMasterVolume(value); } @@ -2053,7 +2083,7 @@ this.createjs = this.createjs || {}; /** * Get the master volume of Sound. The master volume is multiplied against each sound's individual volume. - * To get individual sound volume, use SoundInstance {{#crossLink "SoundInstance/volume"}}{{/crossLink}} instead. + * To get individual sound volume, use SoundInstance {{#crossLink "SoundInstance/volume:property"}}{{/crossLink}} instead. * * <h4>Example</h4> * var masterVolume = createjs.Sound.getVolume(); @@ -2063,7 +2093,7 @@ this.createjs = this.createjs || {}; * @static */ s.getVolume = function () { - return s.masterVolume; + return s._masterVolume; }; /** @@ -2093,22 +2123,22 @@ this.createjs = this.createjs || {}; return false; } - this.masterMute = value; + this._masterMute = value; if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) { - var instances = this.instances; + var instances = this._instances; for (var i = 0, l = instances.length; i < l; i++) { instances[i].setMasterMute(value); } } return true; - } + }; /** * Returns the global mute value. To get the mute value of an individual instance, use SoundInstance * {{#crossLink "SoundInstance/getMute"}}{{/crossLink}} instead. * * <h4>Example</h4> - * var masterMute = createjs.Sound.getMute(); + * var muted = createjs.Sound.getMute(); * * @method getMute * @return {Boolean} The mute value of Sound. @@ -2116,12 +2146,12 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ s.getMute = function () { - return this.masterMute; + return this._masterMute; }; /** * Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped, - * call {{#crossLink "SoundInstance.play"}}{{/crossLink}}. + * call SoundInstance {{#crossLink "SoundInstance/play"}}{{/crossLink}}. * * <h4>Example</h4> * createjs.Sound.stop(); @@ -2130,9 +2160,9 @@ this.createjs = this.createjs || {}; * @static */ s.stop = function () { - var instances = this.instances; + var instances = this._instances; for (var i = instances.length; i--; ) { - instances[i].stop(); // NOTE stop removes instance from this.instances + instances[i].stop(); // NOTE stop removes instance from this._instances } }; @@ -2143,7 +2173,7 @@ this.createjs = this.createjs || {}; /** * Play an instance. This is called by the static API, as well as from plugins. This allows the core class to * control delays. - * @method playInstance + * @method _playInstance * @param {SoundInstance} instance The {{#crossLink "SoundInstance"}}{{/crossLink}} to start playing. * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source, * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code> @@ -2162,13 +2192,14 @@ this.createjs = this.createjs || {}; * @protected * @static */ - s.playInstance = function (instance, interrupt, delay, offset, loop, volume, pan) { + s._playInstance = function (instance, interrupt, delay, offset, loop, volume, pan) { if (interrupt instanceof Object) { delay = interrupt.delay; offset = interrupt.offset; loop = interrupt.loop; volume = interrupt.volume; pan = interrupt.pan; + interrupt = interrupt.interrupt; } interrupt = interrupt || s.defaultInterruptBehavior; @@ -2179,7 +2210,7 @@ this.createjs = this.createjs || {}; if (pan == null) {pan = instance.pan;} if (delay == 0) { - var ok = s.beginPlaying(instance, interrupt, offset, loop, volume, pan); + var ok = s._beginPlaying(instance, interrupt, offset, loop, volume, pan); if (!ok) { return false; } @@ -2187,19 +2218,19 @@ this.createjs = this.createjs || {}; //Note that we can't pass arguments to proxy OR setTimeout (IE only), so just wrap the function call. // OJR WebAudio may want to handle this differently, so it might make sense to move this functionality into the plugins in the future var delayTimeoutId = setTimeout(function () { - s.beginPlaying(instance, interrupt, offset, loop, volume, pan); + s._beginPlaying(instance, interrupt, offset, loop, volume, pan); }, delay); - instance.delayTimeoutId = delayTimeoutId; + instance._delayTimeoutId = delayTimeoutId; } - this.instances.push(instance); + this._instances.push(instance); return true; }; /** * Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}. - * @method beginPlaying + * @method _beginPlaying * @param {SoundInstance} instance A {{#crossLink "SoundInstance"}}{{/crossLink}} to begin playback. * @param {String} [interrupt=none] How this sound interrupts other instances with the same source. Defaults to * {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}. Interrupts are defined as <code>INTERRUPT_TYPE</code> @@ -2214,16 +2245,16 @@ this.createjs = this.createjs || {}; * @protected * @static */ - s.beginPlaying = function (instance, interrupt, offset, loop, volume, pan) { + s._beginPlaying = function (instance, interrupt, offset, loop, volume, pan) { if (!SoundChannel.add(instance, interrupt)) { return false; } - var result = instance.beginPlaying(offset, loop, volume, pan); + var result = instance._beginPlaying(offset, loop, volume, pan); if (!result) { //LM: Should we remove this from the SoundChannel (see finishedPlaying) - var index = createjs.indexOf(this.instances, instance); + var index = createjs.indexOf(this._instances, instance); if (index > -1) { - this.instances.splice(index, 1); + this._instances.splice(index, 1); } return false; } @@ -2233,46 +2264,36 @@ this.createjs = this.createjs || {}; /** * Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned * instead. - * @method getSrcById + * @method _getSrcById * @param {String} value The ID the sound was registered with. * @return {String} The source of the sound. Returns null if src has been registered with this id. * @protected * @static */ - s.getSrcById = function (value) { - if (s.idHash == null || s.idHash[value] == null) { + s._getSrcById = function (value) { + if (s._idHash == null || s._idHash[value] == null) { return value; } - return s.idHash[value]; + return s._idHash[value]; }; /** * A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from * Sound management. It will be added again, if the sound re-plays. Note that this method is called from the * instances themselves. - * @method playFinished + * @method _playFinished * @param {SoundInstance} instance The instance that finished playback. * @protected * @static */ - s.playFinished = function (instance) { + s._playFinished = function (instance) { SoundChannel.remove(instance); - var index = createjs.indexOf(this.instances, instance); + var index = createjs.indexOf(this._instances, instance); if (index > -1) { - this.instances.splice(index, 1); + this._instances.splice(index, 1); } }; - /** - * REMOVED. Please use createjs.proxy instead - * @method proxy - * @param {Function} method The function to call - * @param {Object} scope The scope to call the method name on - * @protected - * @static - * @deprecated Deprecated in favor of createjs.proxy. - */ - createjs.Sound = Sound; /** @@ -2443,7 +2464,7 @@ this.createjs = this.createjs || {}; if (this.max == -1) { this.max = this.maxDefault; } - this.instances = []; + this._instances = []; }; /** @@ -2453,7 +2474,7 @@ this.createjs = this.createjs || {}; * @return {SoundInstance} The SoundInstance at a specific instance. */ p.get = function (index) { - return this.instances[index]; + return this._instances[index]; }; /** @@ -2466,7 +2487,7 @@ this.createjs = this.createjs || {}; if (!this.getSlot(interrupt, instance)) { return false; } - this.instances.push(instance); + this._instances.push(instance); this.length++; return true; }; @@ -2479,11 +2500,11 @@ this.createjs = this.createjs || {}; * return false. */ p.remove = function (instance) { - var index = createjs.indexOf(this.instances, instance); + var index = createjs.indexOf(this._instances, instance); if (index == -1) { return false; } - this.instances.splice(index, 1); + this._instances.splice(index, 1); this.length--; return true; }; @@ -2495,7 +2516,7 @@ this.createjs = this.createjs || {}; p.removeAll = function () { // Note that stop() removes the item from the list, but we don't want to assume that. for (var i=this.length-1; i>=0; i--) { - this.instances[i].stop(); + this._instances[i].stop(); } }; @@ -2541,7 +2562,7 @@ this.createjs = this.createjs || {}; } if (replacement != null) { - replacement.interrupt(); + replacement._interrupt(); this.remove(replacement); return true; } @@ -2558,7 +2579,7 @@ this.createjs = this.createjs || {}; // This is a dummy sound instance, which allows Sound to return something so developers don't need to check nulls. function SoundInstance() { this.isDefault = true; - this.addEventListener = this.removeEventListener = this.removeAllEventListeners = this.dispatchEvent = this.hasEventListener = this._listeners = this.interrupt = this.playFailed = this.pause = this.resume = this.play = this.beginPlaying = this.cleanUp = this.stop = this.setMasterVolume = this.setVolume = this.mute = this.setMute = this.getMute = this.setPan = this.getPosition = this.setPosition = function () { + this.addEventListener = this.removeEventListener = this.removeAllEventListeners = this.dispatchEvent = this.hasEventListener = this._listeners = this._interrupt = this._playFailed = this.pause = this.resume = this.play = this._beginPlaying = this._cleanUp = this.stop = this.setMasterVolume = this.setVolume = this.mute = this.setMute = this.getMute = this.setPan = this.getPosition = this.setPosition = this.playFailed = function () { return false; }; this.getVolume = this.getPan = this.getDuration = function () { @@ -2570,14 +2591,7 @@ this.createjs = this.createjs || {}; } } - Sound.defaultSoundInstance = new SoundInstance(); - - //TODO: Moved to createjs/utils/Proxy.js. Deprecate on next version. - if (createjs.proxy == null) { - createjs.proxy = function() { - throw("Proxy has been moved to an external file, and must be included separately."); - } - } + Sound._defaultSoundInstance = new SoundInstance(); /** @@ -2653,22 +2667,15 @@ this.createjs = this.createjs || {}; "use strict"; /** - * Play sounds using Web Audio in the browser. The WebAudio plugin has been successfully tested with: - * <ul><li>Google Chrome, version 23+ on OS X and Windows</li> - * <li>Safari 6+ on OS X</li> - * <li>Mobile Safari on iOS 6+</li> - * <li>Firefox 25+ on OS X, Windows, and Fx OS</li> - * </ul> - * - * The WebAudioPlugin is currently the default plugin, and will be used anywhere that it is supported. Currently - * Chrome and Safari offer support. Firefox and Android Chrome both offer support for web audio in upcoming - * releases. To change plugin priority, check out the Sound API {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method. + * Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used + * anywhere that it is supported. To change plugin priority, check out the Sound API + * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method. - * <h4>Known Browser and OS issues for Web Audio Plugin</h4> + * <h4>Known Browser and OS issues for Web Audio</h4> * <b>Firefox 25</b> * <ul><li>mp3 audio files do not load properly on all windows machines, reported * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br> - * For this reason it is recommended to pass ogg file first until this bug is resolved, if possible.</li></ul> + * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.</li></ul> * <br /> * <b>Webkit (Chrome and Safari)</b> * <ul><li>AudioNode.disconnect does not always seem to work. This can cause the file size to grow over time if you @@ -2676,30 +2683,30 @@ this.createjs = this.createjs || {}; * <br /> * <b>iOS 6 limitations</b> * <ul><li>Sound is initially muted and will only unmute through play being called inside a user initiated event (touch/click).</li> - * <li>Despite suggestions to the opposite, we have relative control over audio volume through the gain nodes.</li> - * <li>A bug exists that will distort uncached audio when a video element is present in the DOM.</li> + * <li>A bug exists that will distort uncached audio when a video element is present in the DOM. You can avoid this bug + * by ensuring the audio and video audio share the same sampleRate.</li> * </ul> * @class WebAudioPlugin * @constructor * @since 0.4.0 */ function WebAudioPlugin() { - this.init(); + this._init(); } var s = WebAudioPlugin; /** - * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/generateCapabilities:method"}}{{/crossLink}} + * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}} * method and is used internally. - * @property capabilities + * @property _capabilities * @type {Object} * @default null * @protected * @static */ - s.capabilities = null; + s._capabilities = null; /** * Determine if the plugin can be used in the current browser/OS. @@ -2710,9 +2717,9 @@ this.createjs = this.createjs || {}; s.isSupported = function () { // check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file var isMobilePhoneGap = createjs.Sound.BrowserDetect.isIOS || createjs.Sound.BrowserDetect.isAndroid || createjs.Sound.BrowserDetect.isBlackberry; - // OJR isMobile may be redundant with isFileXHRSupported available. Consider removing. - if (location.protocol == "file:" && !isMobilePhoneGap && !this.isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally - s.generateCapabilities(); + // OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing. + if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally + s._generateCapabilities(); if (s.context == null) { return false; } @@ -2721,14 +2728,14 @@ this.createjs = this.createjs || {}; /** * Determine if XHR is supported, which is necessary for web audio. - * @method isFileXHRSupported + * @method _isFileXHRSupported * @return {Boolean} If XHR is supported. * @since 0.4.2 * @protected * @static */ - s.isFileXHRSupported = function() { - // it's much easier to detect when something goes wrong, so let's start optimisticaly + s._isFileXHRSupported = function() { + // it's much easier to detect when something goes wrong, so let's start optimistically var supported = true; var xhr = new XMLHttpRequest(); @@ -2755,12 +2762,12 @@ this.createjs = this.createjs || {}; /** * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} * method for an overview of plugin capabilities. - * @method generateCapabiities + * @method _generateCapabilities * @static * @protected */ - s.generateCapabilities = function () { - if (s.capabilities != null) { + s._generateCapabilities = function () { + if (s._capabilities != null) { return; } // Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section, @@ -2782,12 +2789,12 @@ this.createjs = this.createjs || {}; } // this handles if only deprecated Web Audio API calls are supported - s.compatibilitySetUp(); + s._compatibilitySetUp(); // playing this inside of a touch event will enable audio on iOS, which starts muted s.playEmptySound(); - s.capabilities = { + s._capabilities = { panning:true, volume:true, tracks:-1 @@ -2799,13 +2806,13 @@ this.createjs = this.createjs || {}; for (var i = 0, l = supportedExtensions.length; i < l; i++) { var ext = supportedExtensions[i]; var playType = extensionMap[ext] || ext; - s.capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != ""); + s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != ""); } // OJR another way to do this might be canPlayType:"m4a", codex: mp4 // 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround. // See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels. if (s.context.destination.numberOfChannels < 2) { - s.capabilities.panning = false; + s._capabilities.panning = false; } // set up AudioNodes that all of our source audio will connect to @@ -2821,11 +2828,11 @@ this.createjs = this.createjs || {}; * Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that * don't support new calls. * - * @method compatibilitySetUp + * @method _compatibilitySetUp * @protected * @since 0.4.2 */ - s.compatibilitySetUp = function() { + s._compatibilitySetUp = function() { //assume that if one new call is supported, they all are if (s.context.createGain) { return; } @@ -2838,7 +2845,7 @@ this.createjs = this.createjs || {}; audioNode.__proto__.stop = audioNode.__proto__.noteOff; // panningModel - this.panningModel = 0; + this._panningModel = 0; }; /** @@ -2872,17 +2879,17 @@ this.createjs = this.createjs || {}; var p = WebAudioPlugin.prototype; - p.capabilities = null; // doc'd above + p._capabilities = null; // doc'd above /** - * The internal volume value of the plugin. - * @property volume + * The internal master volume value of the plugin. + * @property _volume * @type {Number} * @default 1 * @protected */ // TODO refactor Sound.js so we can use getter setter for volume - p.volume = 1; + p._volume = 1; /** * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin @@ -2894,22 +2901,22 @@ this.createjs = this.createjs || {}; /** * Value to set panning model to equal power for SoundInstance. Can be "equalpower" or 0 depending on browser implementation. - * @property panningModel + * @property _panningModel * @type {Number / String} * @protected */ - p.panningModel = "equalpower"; + p._panningModel = "equalpower"; /** - * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion according to - * http://www.w3.org/TR/webaudio/#DynamicsCompressorNode. It is connected to <code>context.destination</code>. + * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion. + * It is connected to <code>context.destination</code>. * @property dynamicsCompressorNode * @type {AudioNode} */ p.dynamicsCompressorNode = null; /** - * A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}. + * A GainNode for controlling master _volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}. * @property gainNode * @type {AudioGainNode} */ @@ -2920,20 +2927,20 @@ this.createjs = this.createjs || {}; * prevents having to load and decode audio files more than once. If a load has been started on a file, * <code>arrayBuffers[src]</code> will be set to true. Once load is complete, it is set the the loaded * ArrayBuffer instance. - * @property arrayBuffers + * @property _arrayBuffers * @type {Object} * @protected */ - p.arrayBuffers = null; + p._arrayBuffers = null; /** * An initialization function run by the constructor - * @method init + * @method _init * @protected */ - p.init = function () { - this.capabilities = s.capabilities; - this.arrayBuffers = {}; + p._init = function () { + this._capabilities = s._capabilities; + this._arrayBuffers = {}; this.context = s.context; this.gainNode = s.gainNode; @@ -2942,7 +2949,7 @@ this.createjs = this.createjs || {}; /** * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}. - * Note that WebAudio provides a <code>Loader</code> instance, which <a href="http://preloadjs.com">PreloadJS</a> + * Note that WebAudio provides a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a> * can use to assist with preloading. * @method register * @param {String} src The source of the audio @@ -2951,7 +2958,7 @@ this.createjs = this.createjs || {}; * @return {Object} A result object, containing a "tag" for preloading purposes. */ p.register = function (src, instances) { - this.arrayBuffers[src] = true; // This is needed for PreloadJS + this._arrayBuffers[src] = true; // This is needed for PreloadJS var tag = new createjs.WebAudioPlugin.Loader(src, this); return { tag:tag @@ -2966,7 +2973,7 @@ this.createjs = this.createjs || {}; * @return {Boolean} */ p.isPreloadStarted = function (src) { - return (this.arrayBuffers[src] != null); + return (this._arrayBuffers[src] != null); }; /** @@ -2976,17 +2983,7 @@ this.createjs = this.createjs || {}; * @return {Boolean} */ p.isPreloadComplete = function (src) { - return (!(this.arrayBuffers[src] == null || this.arrayBuffers[src] == true)); - }; - - /** - * Remove a source from our preload list. Note this does not cancel a preload. - * @method removeFromPreload - * @param {String} src The sound URI to unload. - * @deprecated - */ - p.removeFromPreload = function (src) { - delete(this.arrayBuffers[src]); + return (!(this._arrayBuffers[src] == null || this._arrayBuffers[src] == true)); }; /** @@ -2996,7 +2993,7 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ p.removeSound = function (src) { - delete(this.arrayBuffers[src]); + delete(this._arrayBuffers[src]); }; /** @@ -3006,7 +3003,7 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ p.removeAllSounds = function () { - this.arrayBuffers = {}; + this._arrayBuffers = {}; }; /** @@ -3016,17 +3013,17 @@ this.createjs = this.createjs || {}; * @return {Boolean} */ p.addPreloadResults = function (src, result) { - this.arrayBuffers[src] = result; + this._arrayBuffers[src] = result; }; /** * Handles internal preload completion. - * @method handlePreloadComplete + * @method _handlePreloadComplete * @protected */ - p.handlePreloadComplete = function () { + p._handlePreloadComplete = function () { //LM: I would recommend having the Loader include an "event" in the onload, and properly binding this callback. - createjs.Sound.sendFileLoadEvent(this.src); // fire event or callback on Sound + createjs.Sound._sendFileLoadEvent(this.src); // fire event or callback on Sound // note "this" will reference Loader object }; @@ -3035,12 +3032,11 @@ this.createjs = this.createjs || {}; * @method preload * @param {String} src The sound URI to load. * @param {Object} instance Not used in this plugin. - * @protected */ p.preload = function (src, instance) { - this.arrayBuffers[src] = true; + this._arrayBuffers[src] = true; var loader = new createjs.WebAudioPlugin.Loader(src, this); - loader.onload = this.handlePreloadComplete; + loader.onload = this._handlePreloadComplete; loader.load(); }; @@ -3065,18 +3061,18 @@ this.createjs = this.createjs || {}; * instances manually otherwise. */ p.setVolume = function (value) { - this.volume = value; - this.updateVolume(); + this._volume = value; + this._updateVolume(); return true; }; /** * Set the gain value for master audio. Should not be called externally. - * @method updateVolume + * @method _updateVolume * @protected */ - p.updateVolume = function () { - var newVolume = createjs.Sound.masterMute ? 0 : this.volume; + p._updateVolume = function () { + var newVolume = createjs.Sound._masterMute ? 0 : this._volume; if (newVolume != this.gainNode.gain.value) { this.gainNode.gain.value = newVolume; } @@ -3088,7 +3084,7 @@ this.createjs = this.createjs || {}; * @return The volume level, between 0 and 1. */ p.getVolume = function () { - return this.volume; + return this._volume; }; /** @@ -3099,7 +3095,7 @@ this.createjs = this.createjs || {}; * @return {Boolean} If the mute call succeeds. */ p.setMute = function (value) { - this.updateVolume(); + this._updateVolume(); return true; }; @@ -3132,10 +3128,10 @@ this.createjs = this.createjs || {}; * playback has completed, a simple call to the {{#crossLink "SoundInstance/play"}}{{/crossLink}} instance method * will rebuild the references the Sound class need to control it. * - * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3"); - * myInstance.addEventListener("complete", playAgain); - * function playAgain(event) { - * myInstance.play(); + * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2}); + * myInstance.addEventListener("loop", handleLoop); + * function handleLoop(event) { + * myInstance.volume = myInstance.volume * 0.5; * } * * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails @@ -3153,7 +3149,7 @@ this.createjs = this.createjs || {}; * @constructor */ function SoundInstance(src, owner) { - this.init(src, owner); + this._init(src, owner); } var p = SoundInstance.prototype = new createjs.EventDispatcher(); @@ -3163,7 +3159,6 @@ this.createjs = this.createjs || {}; * @property src * @type {String} * @default null - * @protected */ p.src = null; @@ -3185,38 +3180,37 @@ this.createjs = this.createjs || {}; /** * The plugin that created the instance - * @property owner + * @property _owner * @type {WebAudioPlugin} * @default null * @protected */ - p.owner = null; + p._owner = null; /** * How far into the sound to begin playback in milliseconds. This is passed in when play is called and used by * pause and setPosition to track where the sound should be at. * Note this is converted from milliseconds to seconds for consistency with the WebAudio API. - * @property offset + * @property _offset * @type {Number} * @default 0 * @protected */ - p.offset = 0; + p._offset = 0; /** * The time in milliseconds before the sound starts. * Note this is handled by {{#crossLink "Sound"}}{{/crossLink}}. - * @property delay + * @property _delay * @type {Number} * @default 0 * @protected */ - p.delay = 0; - + p._delay = 0; // OJR remove this property from SoundInstance as it is not used here? /** * The volume of the sound, between 0 and 1. - * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower, + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower, * and Internet Explorer 8 or lower. Instead use {{#crossLink "SoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "SoundInstance/getVolume"}}{{/crossLink}}. * * The actual output volume of a sound can be calculated using: @@ -3237,7 +3231,7 @@ this.createjs = this.createjs || {}; if (Number(value) == null) {return false} value = Math.max(0, Math.min(1, value)); this._volume = value; - this.updateVolume(); + this._updateVolume(); } }); } catch (e) { @@ -3245,10 +3239,11 @@ this.createjs = this.createjs || {}; }; /** - * The pan of the sound, between -1 (left) and 1 (right). Note that pan does not work for HTML Audio. - * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, + * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio. + * + * <br />Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower, * and Internet Explorer 8 or lower. Instead use {{#crossLink "SoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "SoundInstance/getPan"}}{{/crossLink}}. - * Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio. + * <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio. * * @property pan * @type {Number} @@ -3262,7 +3257,7 @@ this.createjs = this.createjs || {}; return this._pan; }, set: function(value) { - if (!this.owner.capabilities.panning || Number(value) == null) {return false;} + if (!this._owner._capabilities.panning || Number(value) == null) {return false;} value = Math.max(-1, Math.min(1, value)); // force pan to stay in the -1 to 1 range // Note that panning in WebAudioPlugin can support 3D audio, but our implementation does not. @@ -3278,51 +3273,50 @@ this.createjs = this.createjs || {}; /** * The length of the audio clip, in milliseconds. * Use {{#crossLink "SoundInstance/getDuration:method"}}{{/crossLink}} to access. - * @property pan + * @property _duration * @type {Number} * @default 0 * @protected */ - p.duration = 0; + p._duration = 0; /** * The number of play loops remaining. Negative values will loop infinitely. - * @property remainingLoops + * @property _remainingLoops * @type {Number} * @default 0 * @protected */ - p.remainingLoops = 0; + p._remainingLoops = 0; /** * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this SoundInstance is played with a delay. * This allows SoundInstance to remove the delay if stop or pause or cleanup are called before playback begins. - * @property delayTimeoutId + * @property _delayTimeoutId * @type {timeoutVariable} * @default null * @protected * @since 0.4.0 */ - p.delayTimeoutId = null; + p._delayTimeoutId = null; /** * Timeout that is created internally to handle sound playing to completion. Stored so we can remove it when * stop, pause, or cleanup are called - * @property soundCompleteTimeout + * @property _soundCompleteTimeout * @type {timeoutVariable} * @default null * @protected * @since 0.4.0 */ - p.soundCompleteTimeout = null; + p._soundCompleteTimeout = null; /** * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * GainNode for controlling <code>SoundInstance</code> volume. Connected to the WebAudioPlugin {{#crossLink "WebAudioPlugin/gainNode:property"}}{{/crossLink}} + * <br />GainNode for controlling <code>SoundInstance</code> volume. Connected to the WebAudioPlugin {{#crossLink "WebAudioPlugin/gainNode:property"}}{{/crossLink}} * that sequences to <code>context.destination</code>. * @property gainNode * @type {AudioGainNode} - * @default null * @since 0.4.0 * */ @@ -3330,20 +3324,18 @@ this.createjs = this.createjs || {}; /** * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * A panNode allowing left and right audio channel panning only. Connected to SoundInstance {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}. + * <br />A panNode allowing left and right audio channel panning only. Connected to SoundInstance {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}. * @property panNode * @type {AudioPannerNode} - * @default null * @since 0.4.0 */ p.panNode = null; /** * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * sourceNode is the audio source. Connected to {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}. + * <br />sourceNode is the audio source. Connected to SoundInstance {{#crossLink "SoundInstance/panNode:property"}}{{/crossLink}}. * @property sourceNode * @type {AudioNode} - * @default null * @since 0.4.0 * */ @@ -3351,62 +3343,51 @@ this.createjs = this.createjs || {}; /** * NOTE this only exists as a {{#crossLink "WebAudioPlugin"}}{{/crossLink}} property and is only intended for use by advanced users. - * sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth + * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth * looping. Connected to {{#crossLink "SoundInstance/gainNode:property"}}{{/crossLink}}. - * @property sourceNodeNext + * @property _sourceNodeNext * @type {AudioNode} * @default null * @protected * @since 0.4.1 * */ - p.sourceNodeNext = null; + p._sourceNodeNext = null; /** * Determines if the audio is currently muted. * Use {{#crossLink "SoundInstance/getMute:method"}}{{/crossLink}} and {{#crossLink "SoundInstance/setMute:method"}}{{/crossLink}} to access. - * @property muted + * @property _muted * @type {Boolean} * @default false * @protected */ - p.muted = false; + p._muted = false; /** - * Determines if the audio is currently paused. + * Read only value that tells you if the audio is currently paused. * Use {{#crossLink "SoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "SoundInstance/resume:method"}}{{/crossLink}} to set. * @property paused * @type {Boolean} - * @default false - * @protected */ - p.paused = false; + p.paused = false; // this value will not be used, and is only set + p._paused = false; // this value is used internally for setting paused /** * WebAudioPlugin only. * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused. - * @property startTime + * @property _startTime * @type {Number} * @default 0 * @protected * @since 0.4.0 */ - p.startTime = 0; + p._startTime = 0; // Proxies, make removing listeners easier. - p.endedHandler = null; - p.readyHandler = null; - p.stalledHandler = null; + p._endedHandler = null; // Events - /** - * The event that is fired when a sound is ready to play. - * @event ready - * @param {Object} target The object that dispatched the event. - * @param {String} type The event type. - * @since 0.4.0 - */ - /** * The event that is fired when playback has started successfully. * @event succeeded @@ -3452,13 +3433,6 @@ this.createjs = this.createjs || {}; */ //TODO: Deprecated - /** - * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/ready:event"}}{{/crossLink}} - * event. - * @property onReady - * @type {Function} - * @deprecated Use addEventListener and the "ready" event. - */ /** * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/succeeded:event"}}{{/crossLink}} * event. @@ -3497,11 +3471,11 @@ this.createjs = this.createjs || {}; /** * A helper method that dispatches all events for SoundInstance. - * @method sendEvent + * @method _sendEvent * @param {String} type The event type * @protected */ - p.sendEvent = function (type) { + p._sendEvent = function (type) { var event = new createjs.Event(type); this.dispatchEvent(event); }; @@ -3509,39 +3483,37 @@ this.createjs = this.createjs || {}; // Constructor /** * Initialize the SoundInstance. This is called from the constructor. - * @method init + * @method _init * @param {string} src The source of the audio. * @param {Class} owner The plugin that created this instance. * @protected */ - p.init = function (src, owner) { - this.owner = owner; + p._init = function (src, owner) { + this._owner = owner; this.src = src; - this.gainNode = this.owner.context.createGain(); + this.gainNode = this._owner.context.createGain(); - this.panNode = this.owner.context.createPanner(); //TODO test how this affects when we have mono audio - this.panNode.panningModel = this.owner.panningModel; + this.panNode = this._owner.context.createPanner(); //TODO test how this affects when we have mono audio + this.panNode.panningModel = this._owner._panningModel; this.panNode.connect(this.gainNode); - if (this.owner.isPreloadComplete(this.src)) { - this.duration = this.owner.arrayBuffers[this.src].duration * 1000; + if (this._owner.isPreloadComplete(this.src)) { + this._duration = this._owner._arrayBuffers[this.src].duration * 1000; } - this.endedHandler = createjs.proxy(this.handleSoundComplete, this); - this.readyHandler = createjs.proxy(this.handleSoundReady, this); - this.stalledHandler = createjs.proxy(this.handleSoundStalled, this); + this._endedHandler = createjs.proxy(this._handleSoundComplete, this); }; /** * Clean up the instance. Remove references and clean up any additional properties such as timers. - * @method cleanup + * @method _cleanUp * @protected */ - p.cleanUp = function () { + p._cleanUp = function () { if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - this.sourceNode = this.cleanUpAudioNode(this.sourceNode); - this.sourceNodeNext = this.cleanUpAudioNode(this.sourceNodeNext); + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); } if (this.gainNode.numberOfOutputs != 0) { @@ -3549,25 +3521,26 @@ this.createjs = this.createjs || {}; } // this works because we only have one connection, and it returns 0 if we've already disconnected it. // OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work. - clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound - clearTimeout(this.soundCompleteTimeout); // clear timeout that triggers sound complete + clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound + clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete - this.startTime = 0; // This is used by getPosition + this._startTime = 0; // This is used by getPosition if (window.createjs == null) { return; } - createjs.Sound.playFinished(this); + createjs.Sound._playFinished(this); }; /** * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection + * @method _cleanUpAudioNode * @param audioNode * @return {audioNode} * @protected * @since 0.4.1 */ - p.cleanUpAudioNode = function(audioNode) { + p._cleanUpAudioNode = function(audioNode) { if(audioNode) { audioNode.stop(0); audioNode.disconnect(this.panNode); @@ -3578,68 +3551,64 @@ this.createjs = this.createjs || {}; /** * The sound has been interrupted. - * @method interrupt + * @method _interrupt * @protected */ - p.interrupt = function () { - this.cleanUp(); + p._interrupt = function () { + this._cleanUp(); this.playState = createjs.Sound.PLAY_INTERRUPTED; - this.paused = false; - this.sendEvent("interrupted"); + this.paused = this._paused = false; + this._sendEvent("interrupted"); }; - // Playback has stalled, and therefore failed. - p.handleSoundStalled = function (event) { - this.sendEvent("failed"); - }; - - // The sound is ready for playing - p.handleSoundReady = function (event) { + /** + * Handles starting playback when the sound is ready for playing. + * @method _handleSoundReady + * @protected + */ + p._handleSoundReady = function (event) { if (window.createjs == null) { return; } - if ((this.offset*1000) > this.getDuration()) { // converting offset to ms + if ((this._offset*1000) > this.getDuration()) { // converting offset to ms this.playFailed(); return; - } else if (this.offset < 0) { // may not need this check if play ignores negative values, this is not specified in the API http://www.w3.org/TR/webaudio/#AudioBufferSourceNode - this.offset = 0; + } else if (this._offset < 0) { // may not need this check if play ignores negative values, this is not specified in the API http://www.w3.org/TR/webaudio/#AudioBufferSourceNode + this._offset = 0; } this.playState = createjs.Sound.PLAY_SUCCEEDED; - this.paused = false; + this.paused = this._paused = false; - this.gainNode.connect(this.owner.gainNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it. + this.gainNode.connect(this._owner.gainNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it. - var dur = this.owner.arrayBuffers[this.src].duration; - this.sourceNode = this.createAndPlayAudioNode((this.owner.context.currentTime - dur), this.offset); - this.duration = dur * 1000; // NOTE *1000 because WebAudio reports everything in seconds but js uses milliseconds - this.startTime = this.sourceNode.startTime - this.offset; + var dur = this._owner._arrayBuffers[this.src].duration; + this.sourceNode = this._createAndPlayAudioNode((this._owner.context.currentTime - dur), this._offset); + this._duration = dur * 1000; // NOTE *1000 because WebAudio reports everything in seconds but js uses milliseconds + this._startTime = this.sourceNode.startTime - this._offset; - this.soundCompleteTimeout = setTimeout(this.endedHandler, (dur - this.offset) * 1000); + this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - this._offset) * 1000); - if(this.remainingLoops != 0) { - this.sourceNodeNext = this.createAndPlayAudioNode(this.startTime, 0); + if(this._remainingLoops != 0) { + this._sourceNodeNext = this._createAndPlayAudioNode(this._startTime, 0); } }; /** * Creates an audio node using the current src and context, connects it to the gain node, and starts playback. - * @method createAudioNode + * @method _createAndPlayAudioNode * @param {Number} startTime The time to add this to the web audio context, in seconds. * @param {Number} offset The amount of time into the src audio to start playback, in seconds. * @return {audioNode} * @protected * @since 0.4.1 */ - p.createAndPlayAudioNode = function(startTime, offset) { - // WebAudio supports BufferSource, MediaElementSource, and MediaStreamSource. - // NOTE MediaElementSource requires different commands to play, pause, and stop because it uses audio tags. - // The same is assumed for MediaStreamSource, although it may share the same commands as MediaElementSource. - var audioNode = this.owner.context.createBufferSource(); - audioNode.buffer = this.owner.arrayBuffers[this.src]; + p._createAndPlayAudioNode = function(startTime, offset) { + var audioNode = this._owner.context.createBufferSource(); + audioNode.buffer = this._owner._arrayBuffers[this.src]; audioNode.connect(this.panNode); - var currentTime = this.owner.context.currentTime; + var currentTime = this._owner.context.currentTime; audioNode.startTime = startTime + audioNode.buffer.duration; //currentTime + audioNode.buffer.duration - (currentTime - startTime); audioNode.start(audioNode.startTime, offset, audioNode.buffer.duration - offset); return audioNode; @@ -3652,14 +3621,13 @@ this.createjs = this.createjs || {}; * * <h4>Example</h4> * var myInstance = createjs.Sound.createInstance(mySrc); - * myInstance.play(createjs.Sound.INTERRUPT_ANY); - * // alternatively, we can pass in options we want to set in an object - * myInstance.play({offset:1, loop:2, pan:0.5}); + * myInstance.play({offset:1, loop:2, pan:0.5}); // options as object properties + * myInstance.play(createjs.Sound.INTERRUPT_ANY); // options as parameters * * @method play * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source, * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code> - * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior"}}{{/crossLink}}. + * constants on the Sound class, with the default defined by Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}. * <br /><strong>OR</strong><br /> * This parameter can be an object that contains any or all optional properties by name, including: interrupt, * delay, offset, loop, volume, and pan (see the above code sample). @@ -3671,21 +3639,21 @@ this.createjs = this.createjs || {}; * for HTML Audio. */ p.play = function (interrupt, delay, offset, loop, volume, pan) { - this.cleanUp(); - createjs.Sound.playInstance(this, interrupt, delay, offset, loop, volume, pan); + this._cleanUp(); + createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); }; /** * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the * src is loaded, otherwise playback will fail. - * @method beginPlaying + * @method _beginPlaying * @param {Number} offset How far into the sound to begin playback, in milliseconds. * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops. * @param {Number} volume The volume of the sound, between 0 and 1. * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio. * @protected */ - p.beginPlaying = function (offset, loop, volume, pan) { + p._beginPlaying = function (offset, loop, volume, pan) { if (window.createjs == null) { return; } @@ -3694,14 +3662,14 @@ this.createjs = this.createjs || {}; return; } - this.offset = offset / 1000; //convert ms to sec - this.remainingLoops = loop; + this._offset = offset / 1000; //convert ms to sec + this._remainingLoops = loop; this.volume = volume; this.pan = pan; - if (this.owner.isPreloadComplete(this.src)) { - this.handleSoundReady(null); - this.sendEvent("succeeded"); + if (this._owner.isPreloadComplete(this.src)) { + this._handleSoundReady(null); + this._sendEvent("succeeded"); return 1; } else { this.playFailed(); @@ -3721,19 +3689,19 @@ this.createjs = this.createjs || {}; * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing. */ p.pause = function () { - if (!this.paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - this.paused = true; + if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { + this.paused = this._paused = true; - this.offset = this.owner.context.currentTime - this.startTime; // this allows us to restart the sound at the same point in playback - this.cleanUpAudioNode(this.sourceNode); - this.cleanUpAudioNode(this.sourceNodeNext); + this._offset = this._owner.context.currentTime - this._startTime; // this allows us to restart the sound at the same point in playback + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this.sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); if (this.gainNode.numberOfOutputs != 0) { this.gainNode.disconnect(); } // this works because we only have one connection, and it returns 0 if we've already disconnected it. - clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound - clearTimeout(this.soundCompleteTimeout); // clear timeout that triggers sound complete + clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound + clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete return true; } return false; @@ -3753,10 +3721,10 @@ this.createjs = this.createjs || {}; * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused. */ p.resume = function () { - if (!this.paused) { + if (!this._paused) { return false; } - this.handleSoundReady(null); + this._handleSoundReady(null); return true; }; @@ -3772,14 +3740,15 @@ this.createjs = this.createjs || {}; * @return {Boolean} If the stop call succeeds. */ p.stop = function () { - this.cleanUp(); + this.paused = this._paused = false; + this._cleanUp(); this.playState = createjs.Sound.PLAY_FINISHED; - this.offset = 0; // set audio to start at the beginning + this._offset = 0; // set audio to start at the beginning return true; }; /** - * NOTE that you can set volume directly, and setVolume remains to allow support for IE8 with FlashPlugin. + * NOTE that you can set volume directly as a property, and setVolume remains to allow support for IE8 with FlashPlugin. * Set the volume of the instance. You can retrieve the volume using {{#crossLink "SoundInstance/getVolume"}}{{/crossLink}}. * * <h4>Example</h4> @@ -3801,12 +3770,12 @@ this.createjs = this.createjs || {}; /** * Internal function used to update the volume based on the instance volume, master volume, instance mute value, * and master mute value. - * @method updateVolume + * @method _updateVolume * @return {Boolean} if the volume was updated. * @protected */ - p.updateVolume = function () { - var newVolume = this.muted ? 0 : this._volume; + p._updateVolume = function () { + var newVolume = this._muted ? 0 : this._volume; if (newVolume != this.gainNode.gain.value) { this.gainNode.gain.value = newVolume; return true; @@ -3815,7 +3784,7 @@ this.createjs = this.createjs || {}; }; /** - * NOTE that you can get volume directly, and getVolume remains to allow support for IE8 with FlashPlugin. + * NOTE that you can access volume directly as a property, and getVolume remains to allow support for IE8 with FlashPlugin. * * Get the volume of the instance. The actual output volume of a sound can be calculated using: * <code>myInstance.getVolume() * createjs.Sound.getVolume();</code> @@ -3827,14 +3796,6 @@ this.createjs = this.createjs || {}; return this.volume; }; - /** - * REMOVED. <strong>Please use {{#crossLink "SoundInstance/setMute"}}{{/crossLink}} instead</strong>. - * @method mute - * @param {Boolean} value If the sound should be muted or not. - * @return {Boolean} If the mute call succeeds. - * @deprecated This method has been replaced by setMute. - */ - /** * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound mute. @@ -3853,8 +3814,8 @@ this.createjs = this.createjs || {}; return false; } - this.muted = value; - this.updateVolume(); + this._muted = value; + this._updateVolume(); return true; }; @@ -3870,11 +3831,11 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ p.getMute = function () { - return this.muted; + return this._muted; }; /** - * NOTE that you can set pan directly, and getPan remains to allow support for IE8 with FlashPlugin. + * NOTE that you can set pan directly as a property, and getPan remains to allow support for IE8 with FlashPlugin. * * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. @@ -3894,7 +3855,7 @@ this.createjs = this.createjs || {}; }; /** - * NOTE that you can get volume directly, and getPan remains to allow support for IE8 with FlashPlugin. + * NOTE that you can access pan directly as a property, and getPan remains to allow support for IE8 with FlashPlugin. * * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is * actually 3D audio. @@ -3911,7 +3872,7 @@ this.createjs = this.createjs || {}; }; /** - * Get the position of the playhead in the instance in milliseconds. + * Get the position of the playhead of the instance in milliseconds. * * <h4>Example</h4> * @@ -3921,17 +3882,17 @@ this.createjs = this.createjs || {}; * @return {Number} The position of the playhead in the sound, in milliseconds. */ p.getPosition = function () { - if (this.paused || this.sourceNode == null) { - var pos = this.offset; + if (this._paused || this.sourceNode == null) { + var pos = this._offset; } else { - var pos = this.owner.context.currentTime - this.startTime; + var pos = this._owner.context.currentTime - this._startTime; } return pos * 1000; // pos in seconds * 1000 to give milliseconds }; /** - * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or even + * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or * stopped. * * <h4>Example</h4> @@ -3942,17 +3903,17 @@ this.createjs = this.createjs || {}; * @param {Number} value The position to place the playhead, in milliseconds. */ p.setPosition = function (value) { - this.offset = value / 1000; // convert milliseconds to seconds + this._offset = value / 1000; // convert milliseconds to seconds if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) { // we need to stop this sound from continuing to play, as we need to recreate the sourceNode to change position - this.cleanUpAudioNode(this.sourceNode); - this.cleanUpAudioNode(this.sourceNodeNext); - clearTimeout(this.soundCompleteTimeout); // clear timeout that triggers sound complete - } // NOTE we cannot just call cleanup because it also calls the Sound function playFinished which releases this instance in SoundChannel + this.sourceNode = this._cleanUpAudioNode(this.sourceNode); + this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext); + clearTimeout(this._soundCompleteTimeout); // clear timeout that triggers sound complete + } // NOTE we cannot just call cleanup because it also calls the Sound function _playFinished which releases this instance in SoundChannel - if (!this.paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { - this.handleSoundReady(null); + if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) { + this._handleSoundReady(null); } return true; @@ -3960,7 +3921,7 @@ this.createjs = this.createjs || {}; /** * Get the duration of the instance, in milliseconds. Note in most cases, you need to play a sound using - * {{#crossLink "SoundInstance/play"}}{{/crossLink}} or the Sound API {{#crossLink "Sound.play"}}{{/crossLink}} + * {{#crossLink "SoundInstance/play"}}{{/crossLink}} or the Sound API {{#crossLink "Sound/play"}}{{/crossLink}} * method before its duration can be reported accurately. * * <h4>Example</h4> @@ -3971,43 +3932,47 @@ this.createjs = this.createjs || {}; * @return {Number} The duration of the sound instance in milliseconds. */ p.getDuration = function () { - return this.duration; + return this._duration; }; - // Audio has finished playing. Manually loop it if required. - // called internally by soundCompleteTimeout in WebAudioPlugin - p.handleSoundComplete = function (event) { - this.offset = 0; // have to set this as it can be set by pause during playback + /** + * Audio has finished playing. Manually loop it if required. + * @method _handleSoundComplete + * @param event + * @protected + */ + // called internally by _soundCompleteTimeout in WebAudioPlugin + p._handleSoundComplete = function (event) { + this._offset = 0; // have to set this as it can be set by pause during playback + if (this._remainingLoops != 0) { + this._remainingLoops--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1 - if (this.remainingLoops != 0) { - this.remainingLoops--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1 - - // OJR we are using a look ahead approach to ensure smooth looping. We add sourceNodeNext to the audio + // OJR we are using a look ahead approach to ensure smooth looping. We add _sourceNodeNext to the audio // context so that it starts playing even if this callback is delayed. This technique and the reasons for // using it are described in greater detail here: http://www.html5rocks.com/en/tutorials/audio/scheduling/ // NOTE the cost of this is that our audio loop may not always match the loop event timing precisely. - if(this.sourceNodeNext) { // this can be set to null, but this should not happen when looping - this.cleanUpAudioNode(this.sourceNode); - this.sourceNode = this.sourceNodeNext; - this.startTime = this.sourceNode.startTime; - this.sourceNodeNext = this.createAndPlayAudioNode(this.startTime, 0); - this.soundCompleteTimeout = setTimeout(this.endedHandler, this.duration); + if(this._sourceNodeNext) { // this can be set to null, but this should not happen when looping + this._cleanUpAudioNode(this.sourceNode); + this.sourceNode = this._sourceNodeNext; + this._startTime = this.sourceNode.startTime; + this._sourceNodeNext = this._createAndPlayAudioNode(this._startTime, 0); + this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration); } else { - this.handleSoundReady(null); + this._handleSoundReady(null); } - this.sendEvent("loop"); + this._sendEvent("loop"); return; } if (window.createjs == null) { return; } - this.cleanUp(); + this._cleanUp(); this.playState = createjs.Sound.PLAY_FINISHED; - this.sendEvent("complete"); + this._sendEvent("complete"); }; // Play has failed, which can happen for a variety of reasons. @@ -4015,9 +3980,9 @@ this.createjs = this.createjs || {}; if (window.createjs == null) { return; } - this.cleanUp(); + this._cleanUp(); this.playState = createjs.Sound.PLAY_FAILED; - this.sendEvent("failed"); + this._sendEvent("failed"); }; p.toString = function () { @@ -4040,7 +4005,7 @@ this.createjs = this.createjs || {}; * @constructor */ function Loader(src, owner) { - this.init(src, owner); + this._init(src, owner); } var p = Loader.prototype; @@ -4097,7 +4062,7 @@ this.createjs = this.createjs || {}; p.onError = null; // constructor - p.init = function (src, owner) { + p._init = function (src, owner) { this.src = src; this.originalSrc = src; this.owner = owner; @@ -4229,7 +4194,7 @@ this.createjs = this.createjs || {}; * <b>All browsers</b><br /> * Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed * this limit, you can expect to see unpredictable results. This will be seen as soon as you register sounds, as - * tags are precreated to all Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as + * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as * a guide to how many total audio tags you can safely use in all browsers. * * <b>IE 9 html limitations</b><br /> @@ -4245,15 +4210,19 @@ this.createjs = this.createjs || {}; * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul> * * <b>iOS 6 limitations</b><br /> - * Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+). HTML Audio is disabled by - * default as it can only have one <audio> tag, can not preload or autoplay the audio, can not cache the audio, - * and can not play the audio except inside a user initiated event. - *<br /><br /> - * <b>Android HTML Audio limitations</b><br /> + * <ul><li>Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)</li> + * <li>HTML Audio is disabled by default because</li> + * <li>can only have one <audio> tag</li> + * <li>can not preload or autoplay the audio</li> + * <li>can not cache the audio</li> + * <li>can not play the audio except inside a user initiated event.</li> + * </ul> + * + * <b>Android Native Browser limitations</b><br /> * <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li> - * <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.</li> + * <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.</li></ul> * <b> Android Chrome 26.0.1410.58 specific limitations</b><br /> - * <li>Chrome reports true when you run createjs.Sound.BrowserDetect.isChrome, but is a different browser + * <ul><li>Chrome reports true when you run createjs.Sound.BrowserDetect.isChrome, but is a different browser * with different abilities.</li> * <li>Can only play 1 sound at a time.</li> * <li>Sound is not cached.</li> @@ -4267,7 +4236,7 @@ this.createjs = this.createjs || {}; * @constructor */ function HTMLAudioPlugin() { - this.init(); + this._init(); } var s = HTMLAudioPlugin; @@ -4283,61 +4252,55 @@ this.createjs = this.createjs || {}; s.MAX_INSTANCES = 30; /** - * The capabilities of the plugin. This is generated via the the SoundInstance {{#crossLink "HTMLAudioPlugin/generateCapabilities"}}{{/crossLink}} + * Event constant for the "canPlayThrough" event for cleaner code. + * @property _AUDIO_READY + * @type {String} + * @default canplaythrough + * @static + * @protected + */ + s._AUDIO_READY = "canplaythrough"; + + /** + * Event constant for the "ended" event for cleaner code. + * @property _AUDIO_ENDED + * @type {String} + * @default ended + * @static + * @protected + */ + s._AUDIO_ENDED = "ended"; + + /** + * Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events. + * @property _AUDIO_SEEKED + * @type {String} + * @default seeked + * @static + * @protected + */ + s._AUDIO_SEEKED = "seeked"; + + /** + * Event constant for the "stalled" event for cleaner code. + * @property _AUDIO_STALLED + * @type {String} + * @default stalled + * @static + * @protected + */ + s._AUDIO_STALLED = "stalled"; + + /** + * The capabilities of the plugin. This is generated via the the SoundInstance {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}} * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all * of the available properties. - * @property capabilities + * @property _capabilities * @type {Object} * @protected * @static */ - s.capabilities = null; - - /** - * Event constant for the "canPlayThrough" event for cleaner code. - * @property AUDIO_READY - * @type {String} - * @default canplaythrough - * @static - */ - s.AUDIO_READY = "canplaythrough"; - - /** - * Event constant for the "ended" event for cleaner code. - * @property AUDIO_ENDED - * @type {String} - * @default ended - * @static - */ - s.AUDIO_ENDED = "ended"; - - /** - * Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events. - * @property AUDIO_SEEKED - * @type {String} - * @default seeked - * @static - */ - s.AUDIO_SEEKED = "seeked"; - - /** - * Event constant for the "error" event for cleaner code. - * @property AUDIO_ERROR - * @type {String} - * @default error - * @static - */ - s.AUDIO_ERROR = "error"; //TODO: Handle error cases - - /** - * Event constant for the "stalled" event for cleaner code. - * @property AUDIO_STALLED - * @type {String} - * @default stalled - * @static - */ - s.AUDIO_STALLED = "stalled"; - + s._capabilities = null; /** * Allows users to enable HTML audio on IOS, which is disabled by default. @@ -4362,15 +4325,12 @@ this.createjs = this.createjs || {}; * @static */ s.isSupported = function () { - // HTML audio can be enable on iOS by removing this if statement, but it is not recommended due to the limitations: - // iOS can only have a single <audio> instance, cannot preload or autoplay, cannot cache sound, and can only be - // played in response to a user event (click) if (createjs.Sound.BrowserDetect.isIOS && !s.enableIOS) { return false; } - s.generateCapabilities(); + s._generateCapabilities(); var t = s.tag; // OJR do we still need this check, when cap will already be null if this is the case - if (t == null || s.capabilities == null) { + if (t == null || s._capabilities == null) { return false; } return true; @@ -4379,12 +4339,12 @@ this.createjs = this.createjs || {}; /** * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} * method for an overview of plugin capabilities. - * @method generateCapabiities + * @method _generateCapabilities * @static * @protected */ - s.generateCapabilities = function () { - if (s.capabilities != null) { + s._generateCapabilities = function () { + if (s._capabilities != null) { return; } var t = s.tag = document.createElement("audio"); @@ -4392,7 +4352,7 @@ this.createjs = this.createjs || {}; return null; } - s.capabilities = { + s._capabilities = { panning:true, volume:true, tracks:-1 @@ -4404,23 +4364,23 @@ this.createjs = this.createjs || {}; for (var i = 0, l = supportedExtensions.length; i < l; i++) { var ext = supportedExtensions[i]; var playType = extensionMap[ext] || ext; - s.capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != ""); + s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != ""); } // OJR another way to do this might be canPlayType:"m4a", codex: mp4 } var p = HTMLAudioPlugin.prototype; // doc'd above - p.capabilities = null; + p._capabilities = null; /** * Object hash indexed by the source of each file to indicate if an audio source is loaded, or loading. - * @property audioSources + * @property _audioSources * @type {Object} * @protected * @since 0.4.0 */ - p.audioSources = null; + p._audioSources = null; /** * The default number of instances to allow. Passed back to {{#crossLink "Sound"}}{{/crossLink}} when a source @@ -4440,18 +4400,18 @@ this.createjs = this.createjs || {}; /** * An initialization function run by the constructor - * @method init + * @method _init * @protected */ - p.init = function () { - this.capabilities = s.capabilities; - this.audioSources = {}; + p._init = function () { + this._capabilities = s._capabilities; + this._audioSources = {}; }; /** * Pre-register a sound instance when preloading/setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}. * Note that this provides an object containing a tag used for preloading purposes, which - * <a href="http://preloadjs.com">PreloadJS</a> can use to assist with preloading. + * <a href="http://preloadjs.com" target="_blank">PreloadJS</a> can use to assist with preloading. * @method register * @param {String} src The source of the audio * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time. @@ -4459,17 +4419,17 @@ this.createjs = this.createjs || {}; * controlling how many instances of a source can be played by default. */ p.register = function (src, instances) { - this.audioSources[src] = true; // Note this does not mean preloading has started + this._audioSources[src] = true; // Note this does not mean preloading has started var channel = createjs.HTMLAudioPlugin.TagPool.get(src); var tag = null; var l = instances || this.defaultNumChannels; for (var i = 0; i < l; i++) { // OJR should we be enforcing s.MAX_INSTANCES here? Does the chrome bug still exist, or can we change this code? - tag = this.createTag(src); + tag = this._createTag(src); channel.add(tag); } tag.id = src; // co-opting id as we need a way to store original src in case it is changed before loading - this.loadedHandler = createjs.proxy(this.handleTagLoad, this); // we need this bind to be able to remove event listeners + this.loadedHandler = createjs.proxy(this._handleTagLoad, this); // we need this bind to be able to remove event listeners tag.addEventListener && tag.addEventListener("canplaythrough", this.loadedHandler); if(tag.onreadystatechange == null) { tag.onreadystatechange = this.loadedHandler; @@ -4488,14 +4448,17 @@ this.createjs = this.createjs || {}; }; }; + // TODO remove this when | approach is removed /** + * Deprecated as this will not be required with new approach to basePath. * Checks if src was changed on tag used to create instances in TagPool before loading * Currently PreloadJS does this when a basePath is set, so we are replicating that behavior for internal preloading. - * @method handleTagLoad + * @method _handleTagLoad * @param event * @protected + * @deprecated */ - p.handleTagLoad = function(event) { + p._handleTagLoad = function(event) { // cleanup and so we don't send the event more than once event.target.removeEventListener && event.target.removeEventListener("canplaythrough", this.loadedHandler); event.target.onreadystatechange = null; @@ -4507,12 +4470,12 @@ this.createjs = this.createjs || {}; /** * Create an HTML audio tag. - * @method createTag + * @method _createTag * @param {String} src The source file to set for the audio tag. * @return {HTMLElement} Returns an HTML audio tag. * @protected */ - p.createTag = function (src) { + p._createTag = function (src) { var tag = document.createElement("audio"); tag.autoplay = false; tag.preload = "none"; @@ -4529,7 +4492,7 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ p.removeSound = function (src) { - delete(this.audioSources[src]); + delete(this._audioSources[src]); createjs.HTMLAudioPlugin.TagPool.remove(src); }; @@ -4540,7 +4503,7 @@ this.createjs = this.createjs || {}; * @since 0.4.1 */ p.removeAllSounds = function () { - this.audioSources = {}; // this drops all references, in theory freeing them for garbage collection + this._audioSources = {}; // this drops all references, in theory freeing them for garbage collection createjs.HTMLAudioPlugin.TagPool.removeAll(); }; @@ -4554,7 +4517,7 @@ this.createjs = this.createjs || {}; // if this sound has not be registered, create a tag and preload it if (!this.isPreloadStarted(src)) { var channel = createjs.HTMLAudioPlugin.TagPool.get(src); - var tag = this.createTag(src); + var tag = this._createTag(src); tag.id = src; channel.add(tag); this.preload(src, {tag:tag}); @@ -4571,7 +4534,7 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ p.isPreloadStarted = function (src) { - return (this.audioSources[src] != null); + return (this._audioSources[src] != null); }; /** @@ -4582,7 +4545,7 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ p.preload = function (src, instance) { - this.audioSources[src] = true; + this._audioSources[src] = true; new createjs.HTMLAudioPlugin.Loader(src, instance.tag); }; @@ -4601,18 +4564,18 @@ this.createjs = this.createjs || {}; // NOTE Documentation for the SoundInstance class in WebAudioPlugin file. Each plugin generates a SoundInstance that // follows the same interface. function SoundInstance(src, owner) { - this.init(src, owner); + this._init(src, owner); } var p = SoundInstance.prototype = new createjs.EventDispatcher(); - p.src = null, + p.src = null; p.uniqueId = -1; p.playState = null; - p.owner = null; + p._owner = null; p.loaded = false; - p.offset = 0; - p.delay = 0; + p._offset = 0; + p._delay = 0; p._volume = 1; // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors try { @@ -4624,49 +4587,50 @@ this.createjs = this.createjs || {}; if (Number(value) == null) {return;} value = Math.max(0, Math.min(1, value)); this._volume = value; - this.updateVolume(); + this._updateVolume(); } }); } catch (e) { // dispatch message or error? }; p.pan = 0; - p.duration = 0; - p.remainingLoops = 0; - p.delayTimeoutId = null; + p._duration = 0; + p._remainingLoops = 0; + p._delayTimeoutId = null; p.tag = null; - p.muted = false; + p._muted = false; p.paused = false; + p._paused = false; // Proxies, make removing listeners easier. - p.endedHandler = null; - p.readyHandler = null; - p.stalledHandler = null; + p._endedHandler = null; + p._readyHandler = null; + p._stalledHandler = null; p.loopHandler = null; // Constructor - p.init = function (src, owner) { + p._init = function (src, owner) { this.src = src; - this.owner = owner; + this._owner = owner; - this.endedHandler = createjs.proxy(this.handleSoundComplete, this); - this.readyHandler = createjs.proxy(this.handleSoundReady, this); - this.stalledHandler = createjs.proxy(this.handleSoundStalled, this); + this._endedHandler = createjs.proxy(this._handleSoundComplete, this); + this._readyHandler = createjs.proxy(this._handleSoundReady, this); + this._stalledHandler = createjs.proxy(this._handleSoundStalled, this); this.loopHandler = createjs.proxy(this.handleSoundLoop, this); }; - p.sendEvent = function (type) { + p._sendEvent = function (type) { var event = new createjs.Event(type); this.dispatchEvent(event); }; - p.cleanUp = function () { + p._cleanUp = function () { var tag = this.tag; if (tag != null) { tag.pause(); - tag.removeEventListener(createjs.HTMLAudioPlugin.AUDIO_ENDED, this.endedHandler, false); - tag.removeEventListener(createjs.HTMLAudioPlugin.AUDIO_READY, this.readyHandler, false); - tag.removeEventListener(createjs.HTMLAudioPlugin.AUDIO_SEEKED, this.loopHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); + tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); try { tag.currentTime = 0; } catch (e) { @@ -4675,30 +4639,30 @@ this.createjs = this.createjs || {}; this.tag = null; } - clearTimeout(this.delayTimeoutId); + clearTimeout(this._delayTimeoutId); if (window.createjs == null) { return; } - createjs.Sound.playFinished(this); + createjs.Sound._playFinished(this); }; - p.interrupt = function () { + p._interrupt = function () { if (this.tag == null) { return; } this.playState = createjs.Sound.PLAY_INTERRUPTED; - this.cleanUp(); - this.paused = false; - this.sendEvent("interrupted"); + this._cleanUp(); + this.paused = this._paused = false; + this._sendEvent("interrupted"); }; // Public API p.play = function (interrupt, delay, offset, loop, volume, pan) { - this.cleanUp(); //LM: Is this redundant? - createjs.Sound.playInstance(this, interrupt, delay, offset, loop, volume, pan); + this._cleanUp(); //LM: Is this redundant? + createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); }; - p.beginPlaying = function (offset, loop, volume, pan) { + p._beginPlaying = function (offset, loop, volume, pan) { if (window.createjs == null) { return -1; } @@ -4708,70 +4672,70 @@ this.createjs = this.createjs || {}; return -1; } - tag.addEventListener(createjs.HTMLAudioPlugin.AUDIO_ENDED, this.endedHandler, false); + tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false); // Reset this instance. - this.offset = offset; + this._offset = offset; this.volume = volume; this.pan = pan; // not pan has no effect - this.updateVolume(); // note this will set for mute and masterMute - this.remainingLoops = loop; + this._updateVolume(); // note this will set for mute and _masterMute + this._remainingLoops = loop; if (tag.readyState !== 4) { - tag.addEventListener(createjs.HTMLAudioPlugin.AUDIO_READY, this.readyHandler, false); - tag.addEventListener(createjs.HTMLAudioPlugin.AUDIO_STALLED, this.stalledHandler, false); + tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); + tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false); tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set. tag.load(); } else { - this.handleSoundReady(null); + this._handleSoundReady(null); } - this.sendEvent("succeeded"); + this._sendEvent("succeeded"); return 1; }; // Note: Sounds stall when trying to begin playback of a new audio instance when the existing instances // has not loaded yet. This doesn't mean the sound will not play. - p.handleSoundStalled = function (event) { - this.cleanUp(); // OJR NOTE this will stop playback, and I think we should remove this and let the developer decide how to handle stalled instances - this.sendEvent("failed"); + p._handleSoundStalled = function (event) { + this._cleanUp(); // OJR NOTE this will stop playback, and I think we should remove this and let the developer decide how to handle stalled instances + this._sendEvent("failed"); }; - p.handleSoundReady = function (event) { + p._handleSoundReady = function (event) { if (window.createjs == null) { return; } - // OJR would like a cleaner way to do this in init, discuss with LM - this.duration = this.tag.duration * 1000; // need this for setPosition on stopped sounds + // OJR would like a cleaner way to do this in _init, discuss with LM + this._duration = this.tag.duration * 1000; // need this for setPosition on stopped sounds this.playState = createjs.Sound.PLAY_SUCCEEDED; - this.paused = false; - this.tag.removeEventListener(createjs.HTMLAudioPlugin.AUDIO_READY, this.readyHandler, false); + this.paused = this._paused = false; + this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false); - if (this.offset >= this.getDuration()) { + if (this._offset >= this.getDuration()) { this.playFailed(); // OJR: throw error? return; - } else if (this.offset > 0) { - this.tag.currentTime = this.offset * 0.001; + } else if (this._offset > 0) { + this.tag.currentTime = this._offset * 0.001; } - if (this.remainingLoops == -1) { + if (this._remainingLoops == -1) { this.tag.loop = true; } - if(this.remainingLoops != 0) { - this.tag.addEventListener(createjs.HTMLAudioPlugin.AUDIO_SEEKED, this.loopHandler, false); + if(this._remainingLoops != 0) { + this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); this.tag.loop = true; } this.tag.play(); }; p.pause = function () { - if (!this.paused && this.playState == createjs.Sound.PLAY_SUCCEEDED && this.tag != null) { - this.paused = true; + if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED && this.tag != null) { + this.paused = this._paused = true; // Note: when paused by user, we hold a reference to our tag. We do not release it until stopped. this.tag.pause(); - clearTimeout(this.delayTimeoutId); + clearTimeout(this._delayTimeoutId); return true; } @@ -4779,24 +4743,24 @@ this.createjs = this.createjs || {}; }; p.resume = function () { - if (!this.paused || this.tag == null) { + if (!this._paused || this.tag == null) { return false; } - this.paused = false; + this.paused = this._paused = false; this.tag.play(); return true; }; p.stop = function () { - this.offset = 0; + this._offset = 0; this.pause(); this.playState = createjs.Sound.PLAY_FINISHED; - this.cleanUp(); + this._cleanUp(); return true; }; p.setMasterVolume = function (value) { - this.updateVolume(); + this._updateVolume(); return true; }; @@ -4805,9 +4769,9 @@ this.createjs = this.createjs || {}; return true; }; - p.updateVolume = function () { + p._updateVolume = function () { if (this.tag != null) { - var newVolume = (this.muted || createjs.Sound.masterMute) ? 0 : this._volume * createjs.Sound.masterVolume; + var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume; if (newVolume != this.tag.volume) { this.tag.volume = newVolume; } @@ -4822,7 +4786,7 @@ this.createjs = this.createjs || {}; }; p.setMasterMute = function (isMuted) { - this.updateVolume(); + this._updateVolume(); return true; }; @@ -4831,13 +4795,13 @@ this.createjs = this.createjs || {}; return false; } - this.muted = isMuted; - this.updateVolume(); + this._muted = isMuted; + this._updateVolume(); return true; }; p.getMute = function () { - return this.muted; + return this._muted; }; // Can not set pan in HTML @@ -4851,53 +4815,53 @@ this.createjs = this.createjs || {}; p.getPosition = function () { if (this.tag == null) { - return this.offset; + return this._offset; } return this.tag.currentTime * 1000; }; p.setPosition = function (value) { if (this.tag == null) { - this.offset = value + this._offset = value } else { - this.tag.removeEventListener(createjs.HTMLAudioPlugin.AUDIO_SEEKED, this.loopHandler, false); + this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); try { this.tag.currentTime = value * 0.001; } catch (error) { // Out of range return false; } - this.tag.addEventListener(createjs.HTMLAudioPlugin.AUDIO_SEEKED, this.loopHandler, false); + this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); } return true; }; p.getDuration = function () { // NOTE this will always return 0 until sound has been played. - return this.duration; + return this._duration; }; - p.handleSoundComplete = function (event) { - this.offset = 0; + p._handleSoundComplete = function (event) { + this._offset = 0; if (window.createjs == null) { return; } this.playState = createjs.Sound.PLAY_FINISHED; - this.cleanUp(); - this.sendEvent("complete"); + this._cleanUp(); + this._sendEvent("complete"); }; // handles looping functionality // NOTE with this approach audio will loop as reliably as the browser allows // but we could end up sending the loop event after next loop playback begins p.handleSoundLoop = function (event) { - this.offset = 0; + this._offset = 0; - this.remainingLoops--; - if(this.remainingLoops == 0) { + this._remainingLoops--; + if(this._remainingLoops == 0) { this.tag.loop = false; - this.tag.removeEventListener(createjs.HTMLAudioPlugin.AUDIO_SEEKED, this.loopHandler, false); + this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false); } - this.sendEvent("loop"); + this._sendEvent("loop"); }; p.playFailed = function () { @@ -4905,8 +4869,8 @@ this.createjs = this.createjs || {}; return; } this.playState = createjs.Sound.PLAY_FAILED; - this.cleanUp(); - this.sendEvent("failed"); + this._cleanUp(); + this._sendEvent("failed"); }; p.toString = function () { @@ -4934,8 +4898,8 @@ this.createjs = this.createjs || {}; * @since 0.4.0 */ function Loader(src, tag) { - this.init(src, tag); - } + this._init(src, tag); + }; var p = Loader.prototype; @@ -4970,7 +4934,7 @@ this.createjs = this.createjs || {}; p.loadedHandler = null; // constructor - p.init = function (src, tag) { + p._init = function (src, tag) { this.src = src; this.tag = tag; @@ -5029,13 +4993,13 @@ this.createjs = this.createjs || {}; p.sendLoadedEvent = function (evt) { this.tag.removeEventListener && this.tag.removeEventListener("canplaythrough", this.loadedHandler); // cleanup and so we don't send the event more than once this.tag.onreadystatechange = null; // cleanup and so we don't send the event more than once - createjs.Sound.sendFileLoadEvent(this.src); // fire event or callback on Sound + createjs.Sound._sendFileLoadEvent(this.src); // fire event or callback on Sound }; // used for debugging p.toString = function () { return "[HTMLAudioPlugin Loader]"; - } + }; createjs.HTMLAudioPlugin.Loader = Loader; @@ -5055,7 +5019,7 @@ this.createjs = this.createjs || {}; * @protected */ function TagPool(src) { - this.init(src); + this._init(src); } var s = TagPool; @@ -5081,7 +5045,7 @@ this.createjs = this.createjs || {}; channel = s.tags[src] = new TagPool(src); } return channel; - } + }; /** * Delete a TagPool and all related tags. Note that if the TagPool does not exist, this will fail. @@ -5098,7 +5062,7 @@ this.createjs = this.createjs || {}; channel.removeAll(); delete(s.tags[src]); return true; - } + }; /** * Delete all TagPools and all related tags. @@ -5110,7 +5074,7 @@ this.createjs = this.createjs || {}; s.tags[channel].removeAll(); // this stops and removes all active instances } s.tags = {}; - } + }; /** * Get a tag instance. This is a shortcut method. @@ -5125,7 +5089,7 @@ this.createjs = this.createjs || {}; return null; } return channel.get(); - } + }; /** * Return a tag instance. This is a shortcut method. @@ -5141,7 +5105,7 @@ this.createjs = this.createjs || {}; return null; } return channel.set(tag); - } + }; /** * A function to check if src has changed in the loaded audio tag. @@ -5158,7 +5122,7 @@ this.createjs = this.createjs || {}; return null; } channel.checkSrcChange(); - } + }; var p = TagPool.prototype; @@ -5198,7 +5162,7 @@ this.createjs = this.createjs || {}; p.tags = null; // constructor - p.init = function (src) { + p._init = function (src) { this.src = src; this.tags = []; }; @@ -5275,7 +5239,7 @@ this.createjs = this.createjs || {}; p.toString = function () { return "[HTMLAudioPlugin TagPool]"; - } + }; createjs.HTMLAudioPlugin.TagPool = TagPool; diff --git a/vendor/scripts/tweenjs-NEXT.combined.js b/vendor/scripts/tweenjs-NEXT.combined.js index 8b4fd3266..179548b27 100644 --- a/vendor/scripts/tweenjs-NEXT.combined.js +++ b/vendor/scripts/tweenjs-NEXT.combined.js @@ -364,6 +364,7 @@ var p = EventDispatcher.prototype; target.hasEventListener = p.hasEventListener; target.dispatchEvent = p.dispatchEvent; target._dispatchEvent = p._dispatchEvent; + target.willTrigger = p.willTrigger; }; // constructor: @@ -580,7 +581,7 @@ var p = EventDispatcher.prototype; }; /** - * Indicates whether there is at least one listener for the specified event type and `useCapture` value. + * Indicates whether there is at least one listener for the specified event type. * @method hasEventListener * @param {String} type The string type of the event. * @return {Boolean} Returns true if there is at least one listener for the specified event. @@ -589,6 +590,26 @@ var p = EventDispatcher.prototype; var listeners = this._listeners, captureListeners = this._captureListeners; return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type])); }; + + /** + * Indicates whether there is at least one listener for the specified event type on this object or any of its + * ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the + * specified type is dispatched from this object, it will trigger at least one listener. + * + * This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire + * event flow for a listener, not just this object. + * @method willTrigger + * @param {String} type The string type of the event. + * @return {Boolean} Returns `true` if there is at least one listener for the specified event. + **/ + p.willTrigger = function(type) { + var o = this; + while (o) { + if (o.hasEventListener(type)) { return true; } + o = o.parent; + } + return false; + }; /** * @method toString @@ -2726,7 +2747,7 @@ this.createjs = this.createjs || {}; * @type String * @static **/ - s.version = /*version*/"0.5.0"; // injected by build process + s.version = /*version*/"NEXT"; // injected by build process /** * The build date for this release in UTC format. @@ -2734,6 +2755,6 @@ this.createjs = this.createjs || {}; * @type String * @static **/ - s.buildDate = /*date*/"Wed, 25 Sep 2013 17:09:35 GMT"; // injected by build process + s.buildDate = /*date*/"Thu, 12 Dec 2013 23:37:07 GMT"; // injected by build process })();