From 2bbda206e7336088dbfdbf13f599cee770ed4f4b Mon Sep 17 00:00:00 2001
From: Scott Erickson <sderickson@gmail.com>
Date: Wed, 7 May 2014 10:59:12 -0700
Subject: [PATCH 1/2] Updated createjs.

---
 vendor/scripts/SpriteContainer.js         |    1 +
 vendor/scripts/SpriteStage.js             |    1 +
 vendor/scripts/easeljs-NEXT.combined.js   |  171 ++-
 vendor/scripts/preloadjs-NEXT.combined.js |  126 ++-
 vendor/scripts/soundjs-NEXT.combined.js   | 1222 +++++++++------------
 vendor/scripts/tweenjs-NEXT.combined.js   |   12 +-
 6 files changed, 722 insertions(+), 811 deletions(-)

diff --git a/vendor/scripts/SpriteContainer.js b/vendor/scripts/SpriteContainer.js
index 49f31a26f..885fa6946 100644
--- a/vendor/scripts/SpriteContainer.js
+++ b/vendor/scripts/SpriteContainer.js
@@ -41,6 +41,7 @@ this.createjs = this.createjs||{};
  *     - all children (with the exception of DOMElement) MUST use the same spriteSheet.
  *
  * <h4>Example</h4>
+ *
  *      var data = {
  *          images: ["sprites.jpg"],
  *          frames: {width:50, height:50},
diff --git a/vendor/scripts/SpriteStage.js b/vendor/scripts/SpriteStage.js
index 926de4a71..df88567fa 100644
--- a/vendor/scripts/SpriteStage.js
+++ b/vendor/scripts/SpriteStage.js
@@ -403,6 +403,7 @@ var p = SpriteStage.prototype = new createjs.Stage();
 	 * Children also MUST have either an image or spriteSheet defined on them (unless it's a DOMElement).
 	 * 
 	 * <h4>Example</h4>
+	 *
 	 *      addChildAt(child1, index);
 	 *
 	 * You can also add multiple children, such as:
diff --git a/vendor/scripts/easeljs-NEXT.combined.js b/vendor/scripts/easeljs-NEXT.combined.js
index 9e51cf98c..84543cfe7 100644
--- a/vendor/scripts/easeljs-NEXT.combined.js
+++ b/vendor/scripts/easeljs-NEXT.combined.js
@@ -31,6 +31,7 @@
  * files of each library and are available on the createsjs namespace directly.
  *
  * <h4>Example</h4>
+ *
  *      myObject.addEventListener("change", createjs.proxy(myMethod, scope));
  *
  * @module CreateJS
@@ -240,6 +241,18 @@ var p = Event.prototype;
 	p.clone = function() {
 		return new Event(this.type, this.bubbles, this.cancelable);
 	};
+	
+	/**
+	 * Provides a chainable shortcut method for setting a number of properties on the instance.
+	 *
+	 * @method set
+	 * @param {Object} props A generic object containing properties to copy to the instance.
+	 * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
+	*/
+	p.set = function(props) {
+		for (var n in props) { this[n] = props[n]; }
+		return this;
+	};
 
 	/**
 	 * Returns a string representation of this object.
@@ -862,6 +875,7 @@ this.createjs = this.createjs||{};
  * should not be instantiated.
  *
  * <h4>Example</h4>
+ *
  *      createjs.Ticker.addEventListener("tick", handleTick);
  *      function handleTick(event) {
  *          // Actions carried out each frame
@@ -937,6 +951,7 @@ var Ticker = function() {
 	 * {{#crossLink "Ticker/setPaused"}}{{/crossLink}}.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      createjs.Ticker.addEventListener("tick", handleTick);
 	 *      function handleTick(event) {
 	 *          console.log("Paused:", event.paused, event.delta);
@@ -1229,6 +1244,7 @@ var Ticker = function() {
 	 * callback when Ticker was paused. This is no longer the case.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      createjs.Ticker.addEventListener("tick", handleTick);
 	 *      createjs.Ticker.setPaused(true);
 	 *      function handleTick(event) {
@@ -1251,6 +1267,7 @@ var Ticker = function() {
 	 * callback when Ticker was paused. This is no longer the case.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      createjs.Ticker.addEventListener("tick", handleTick);
 	 *      createjs.Ticker.setPaused(true);
 	 *      function handleTick(event) {
@@ -2279,8 +2296,9 @@ this.createjs = this.createjs||{};
  * Represents a point on a 2 dimensional x / y coordinate system.
  *
  * <h4>Example</h4>
- *      var point = new Point(0, 100);
- *
+ * 
+ *      var point = new createjs.Point(0, 100);
+ * 
  * @class Point
  * @param {Number} [x=0] X position.
  * @param {Number} [y=0] Y position.
@@ -2393,7 +2411,8 @@ this.createjs = this.createjs||{};
 /**
  * Represents a rectangle as defined by the points (x, y) and (x+width, y+height).
  *
- * @example
+ * <h4>Example</h4>
+ *
  *      var rect = new createjs.Rectangle(0, 0, 100, 100);
  *
  * @class Rectangle
@@ -2789,6 +2808,7 @@ this.createjs = this.createjs||{};
  * via its <code>shadow</code> property.
  *
  * <h4>Example</h4>
+ *
  *      myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10);
  *
  * @class Shadow
@@ -3427,6 +3447,7 @@ Command.prototype.exec = function(scope) { this.f.apply(scope, this.params); };
  * context of an Easel display list.
  *
  * <h4>Example</h4>
+ *
  *      var g = new createjs.Graphics();
  *	    g.setStrokeStyle(1);
  *	    g.beginStroke(createjs.Graphics.getRGB(0,0,0));
@@ -3760,7 +3781,7 @@ var p = Graphics.prototype;
 
 	/**
 	 * Draws only the path described for this Graphics instance, skipping any non-path instructions, including fill and
-	 * stroke descriptions. Used by <code>DisplayObject.clippingPath</code> to draw the clipping path, for example.
+	 * stroke descriptions. Used for <code>DisplayObject.mask</code> to draw the clipping path, for example.
 	 * @method drawAsPath
 	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
 	 **/
@@ -5785,9 +5806,8 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher();
 	};
 
 	/**
-	 * Tests whether the display object intersects the specified local point (ie. draws a pixel with alpha > 0 at
-	 * the specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all
-	 * transform properties including regX/Y.
+	 * Tests whether the display object intersects the specified point in local coordinates (ie. draws a pixel with alpha > 0 at
+	 * the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation of the display object.
 	 *
 	 * <h4>Example</h4>
 	 *
@@ -5804,7 +5824,7 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher();
 	 * local Point.
 	*/
 	p.hitTest = function(x, y) {
-		// TODO: update with support for .hitArea and update hitArea docs?
+		// TODO: update with support for .hitArea & .mask and update hitArea / mask docs?
 		var ctx = DisplayObject._hitTestContext;
 		ctx.setTransform(1, 0, 0, 1, -x, -y);
 		this.draw(ctx);
@@ -5816,7 +5836,7 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher();
 	};
 	
 	/**
-	 * Provides a chainable shortcut method for setting a number of properties on a DisplayObject instance.
+	 * Provides a chainable shortcut method for setting a number of properties on the instance.
 	 *
 	 * <h4>Example</h4>
 	 *
@@ -5826,7 +5846,7 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher();
 	 *
 	 * @method set
 	 * @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
-	 * @return {DisplayObject} Returns The DisplayObject instance the method is called on (useful for chaining calls.)
+	 * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.)
 	*/
 	p.set = function(props) {
 		for (var n in props) { this[n] = props[n]; }
@@ -6002,17 +6022,16 @@ var p = DisplayObject.prototype = new createjs.EventDispatcher();
 	
 	/**
 	 * @method _tick
-	 * @param {Array} params Parameters to pass on to any listeners of the tick function. This will usually include the
+	 * @param {Object} props Props to copy to the tick event object. This will usually include the
 	 * properties from the {{#crossLink "Ticker"}}{{/crossLink}} "tick" event, such as `delta` and `paused`, but may
 	 * be undefined or contain other values depending on the usage by the application.
 	 * @protected
 	 **/
-	p._tick = function(params) {
+	p._tick = function(props) {
 		// because tick can be really performance sensitive, we'll inline some of the dispatchEvent work.
 		var ls = this._listeners;
 		if (ls && ls["tick"]) {
-			var evt = new createjs.Event("tick");
-			evt.params = params;
+			var evt = new createjs.Event("tick").set(props);
 			this._dispatchEvent(evt, this, 2);
 		}
 	};
@@ -6180,6 +6199,7 @@ this.createjs = this.createjs||{};
  * Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
  *
  * <h4>Example</h4>
+ *
  *      var container = new createjs.Container();
  *      container.addChild(bitmapInstance, shapeInstance);
  *      container.x = 100;
@@ -6299,6 +6319,7 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * Adds a child to the top of the display list.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      container.addChild(bitmapInstance);
 	 *
 	 *  You can also add multiple children at once:
@@ -6327,6 +6348,7 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * setting its parent to this Container.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      addChildAt(child1, index);
 	 *
 	 * You can also add multiple children, such as:
@@ -6364,6 +6386,7 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * already known.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      container.removeChild(child);
 	 *
 	 * You can also remove multiple children:
@@ -6422,6 +6445,7 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * Removes all children from the display list.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      container.removeAlLChildren();
 	 *
 	 * @method removeAllChildren
@@ -6435,6 +6459,7 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * Returns the child at the specified index.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      container.getChildAt(2);
 	 *
 	 * @method getChildAt
@@ -6483,6 +6508,7 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * Returns the index of the specified child in the display list, or -1 if it is not in the display list.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      var index = container.getChildIndex(child);
 	 *
 	 * @method getChildIndex
@@ -6590,6 +6616,8 @@ var p = Container.prototype = new createjs.DisplayObject();
 	 * of visual depth, with the top-most display object at index 0. This uses shape based hit detection, and can be an
 	 * expensive operation to run, so it is best to use it carefully. For example, if testing for objects under the
 	 * mouse, test on tick (instead of on mousemove), and only if the mouse's position has changed.
+	 * 
+	 * Accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}.
 	 * @method getObjectsUnderPoint
 	 * @param {Number} x The x position in the container to test.
 	 * @param {Number} y The y position in the container to test.
@@ -6679,18 +6707,18 @@ var p = Container.prototype = new createjs.DisplayObject();
 	
 	/**
 	 * @method _tick
-	 * @param {Array} params Parameters to pass onto the DisplayObject {{#crossLink "DisplayObject/tick"}}{{/crossLink}}
+	 * @param {Object} props Properties to copy to the DisplayObject {{#crossLink "DisplayObject/tick"}}{{/crossLink}} event object.
 	 * function.
 	 * @protected
 	 **/
-	p._tick = function(params) {
+	p._tick = function(props) {
 		if (this.tickChildren) {
 			for (var i=this.children.length-1; i>=0; i--) {
 				var child = this.children[i];
-				if (child.tickEnabled && child._tick) { child._tick(params); }
+				if (child.tickEnabled && child._tick) { child._tick(props); }
 			}
 		}
-		this.DisplayObject__tick(params);
+		this.DisplayObject__tick(props);
 	};
 
 	/**
@@ -6713,8 +6741,23 @@ var p = Container.prototype = new createjs.DisplayObject();
 		var l = children.length;
 		for (var i=l-1; i>=0; i--) {
 			var child = children[i];
-			var hitArea = child.hitArea;
+			var hitArea = child.hitArea, mask = child.mask;
 			if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; }
+			if (!hitArea && mask && mask.graphics && !mask.graphics.isEmpty()) {
+				var maskMtx = mask.getMatrix(mask._matrix).prependMatrix(this.getConcatenatedMatrix(mtx));
+				ctx.setTransform(maskMtx.a,  maskMtx.b, maskMtx.c, maskMtx.d, maskMtx.tx-x, maskMtx.ty-y);
+				
+				// draw the mask as a solid fill:
+				mask.graphics.drawAsPath(ctx);
+				ctx.fillStyle = "#000";
+				ctx.fill();
+				
+				// if we don't hit the mask, then no need to keep looking at this DO:
+				if (!this._testHit(ctx)) { continue; }
+				ctx.setTransform(1, 0, 0, 1, 0, 0);
+				ctx.clearRect(0, 0, 2, 2);
+			}
+			
 			// 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, activeListener);
@@ -7138,30 +7181,19 @@ var p = Stage.prototype = new createjs.Container();
 // public methods:
 
 	/**
-	 * Each time the update method is called, the stage will tick all descendants (see: {{#crossLink "DisplayObject/tick"}}{{/crossLink}})
-	 * and then render the display list to the canvas. Any parameters passed to `update()` will be passed on to any
-	 * {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}} event handlers.
-	 *
-	 * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
-	 * a tick event object (or equivalent) be passed as the first parameter to update(). For example:
-	 *
-	 *      Ticker.addEventListener("tick", handleTick);
-	 * 	    function handleTick(evtObj) {
-	 * 	     	// do some work here, then update the stage, passing through the event object:
-	 * 	    	myStage.update(evtObj);
-	 * 	    }
+	 * Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}}
+	 * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false,
+	 * and then render the display list to the canvas.
 	 *
 	 * @method update
-	 * @param {*} [params]* Params to include when ticking descendants. The first param should usually be a tick event.
+	 * @param {*} [params]* Params to pass to .tick() if .tickOnUpdate is true.
 	 **/
 	p.update = function(params) {
 		if (!this.canvas) { return; }
-		if (this.tickOnUpdate) {
-			this.dispatchEvent("tickstart");  // TODO: make cancellable?
-			this.tickEnabled&&this._tick((arguments.length ? arguments : null));
-			this.dispatchEvent("tickend");
+		if (this.tickOnUpdate) { // update this logic in SpriteStage when necessary
+			this.tick.apply(this, arguments);
 		}
-		this.dispatchEvent("drawstart"); // TODO: make cancellable?
+		this.dispatchEvent("drawstart"); //TODO: make cancellable?
 		createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
 		if (this.autoClear) { this.clear(); }
 		var ctx = this.canvas.getContext("2d");
@@ -7171,6 +7203,47 @@ var p = Stage.prototype = new createjs.Container();
 		ctx.restore();
 		this.dispatchEvent("drawend");
 	};
+	
+	/**
+	 * Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
+	 * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
+	 *
+	 * Any parameters passed to `tick()` will be included as an array in the "param" property of the event object dispatched
+	 * to {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}} event handlers. Additionally, if the first parameter
+	 * is a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or has equivalent properties), then the delta,
+	 * time, runTime, and paused properties will be copied to the event object.
+	 *
+	 * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
+	 * a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent) be passed as the first parameter
+	 * to tick(). For example:
+	 *
+	 * 	    Ticker.on("tick", handleTick);
+	 * 	    function handleTick(evtObj) {
+	 * 	    	// do some work here, then update the stage, passing through the tick event object as the first param
+	 * 	    	// and some custom data as the second and third param:
+	 * 	    	myStage.update(evtObj, "hello", 2014);
+	 * 	    }
+	 * 	    
+	 * 	    // ...
+	 * 	    myDisplayObject.on("tick", handleDisplayObjectTick);
+	 * 	    function handleDisplayObjectTick(evt) {
+	 * 	    	console.log(evt.params[0]); // the original tick evtObj
+	 * 	    	console.log(evt.delta, evt.paused); // ex. "17 false"
+	 * 	    	console.log(evt.params[1], evt.params[2]); // "hello 2014"
+	 * 	    }
+	 * 
+	 * @method tick
+	 * @param {*} [params]* Params to include when ticking descendants. The first param should usually be a tick event.
+	 **/
+	p.tick = function(params) {
+		this.dispatchEvent("tickstart");  //TODO: make cancellable?
+		var args = arguments.length ? Array.prototype.slice.call(arguments,0) : null;
+		var evt = args&&args[0];
+		var props = evt&&(evt.delta != null) ? {delta:evt.delta, paused:evt.paused, time:evt.time, runTime:evt.runTime } : {};
+		props.params = args;
+		this.tickEnabled&&this._tick(props);
+		this.dispatchEvent("tickend");
+	};
 
 	/**
 	 * Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}}
@@ -7263,6 +7336,7 @@ var p = Stage.prototype = new createjs.Container();
 	 * independently of mouse move events via the optional `frequency` parameter.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      var stage = new createjs.Stage("canvasId");
 	 *      stage.enableMouseOver(10); // 10 updates per second
 	 *
@@ -7687,6 +7761,7 @@ this.createjs = this.createjs||{};
  * HTML element, or a string.
  *
  * <h4>Example</h4>
+ *
  *      var bitmap = new createjs.Bitmap("imagePath.jpg");
  *
  * <strong>Notes:</strong>
@@ -7924,6 +7999,7 @@ this.createjs = this.createjs||{};
  * See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations.
  *
  * <h4>Example</h4>
+ *
  *      var instance = new createjs.Sprite(spriteSheet);
  *      instance.gotoAndStop("frameName");
  *
@@ -7935,7 +8011,7 @@ this.createjs = this.createjs||{};
  * @constructor
  * @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame
  * dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
- * @param {String|Number} frameOrAnimation The frame number or animation to play initially.
+ * @param {String|Number} [frameOrAnimation] The frame number or animation to play initially.
  **/
 var Sprite = function(spriteSheet, frameOrAnimation) {
   this.initialize(spriteSheet, frameOrAnimation);
@@ -8254,14 +8330,15 @@ var p = Sprite.prototype = new createjs.DisplayObject();
 	/**
 	 * Advances the <code>currentFrame</code> if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}}
 	 * ticks.
+	 * @param {Object} props Properties to copy to the DisplayObject {{#crossLink "DisplayObject/tick"}}{{/crossLink}} event object.
 	 * @protected
 	 * @method _tick
 	 **/
-	p._tick = function(params) {
+	p._tick = function(props) {
 		if (!this.paused) {
-			this.advance(params&&params[0]&&params[0].delta);
+			this.advance(props&&props.delta);
 		}
-		this.DisplayObject__tick(params);
+		this.DisplayObject__tick(props);
 	};
 
 
@@ -8338,7 +8415,7 @@ var p = Sprite.prototype = new createjs.DisplayObject();
 
 	/**
 	 * @method cloneProps
-	 * @param {Text} o
+	 * @param {Sprite} o
 	 * @protected
 	 **/
 	p.cloneProps = function(o) {
@@ -8477,6 +8554,7 @@ this.createjs = this.createjs||{};
  * rendering cost.
  *
  * <h4>Example</h4>
+ *
  *      var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100);
  *      var shape = new createjs.Shape(graphics);
  *
@@ -8629,6 +8707,7 @@ this.createjs = this.createjs||{};
  * multiple font styles, you will need to create multiple text instances, and position them manually.
  *
  * <h4>Example</h4>
+ *
  *      var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
  *      text.x = 100;
  *      text.textBaseline = "alphabetic";
@@ -10341,14 +10420,14 @@ var p = DOMElement.prototype = new createjs.DisplayObject();
 
 	/**
 	 * @method _tick
-	 * @param {Array} params Parameters to pass onto the DisplayObject {{#crossLink "DisplayObject/tick"}}{{/crossLink}}
+	 * @param {Object} props Properties to copy to the DisplayObject {{#crossLink "DisplayObject/tick"}}{{/crossLink}} event object.
 	 * function.
 	 * @protected
 	 */
-	p._tick = function(params) {
+	p._tick = function(props) {
 		var stage = this.getStage();
 		stage&&stage.on("drawend", this._handleDrawEnd, this, true);
-		this.DisplayObject__tick(params);
+		this.DisplayObject__tick(props);
 	};
 	
 	/**
@@ -10427,6 +10506,7 @@ this.createjs = this.createjs||{};
  * {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
  *
  * <h4>Example</h4>
+ *
  *      myInstance.filters = [
  *          new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0),
  *          new createjs.BlurFilter(5, 5, 10)
@@ -11428,6 +11508,7 @@ this.createjs = this.createjs||{};
 	 * chained calls.
 	 *
 	 * <h4>Example</h4>
+	 *
 	 *      myColorMatrix.adjustHue(20).adjustBrightness(50);
 	 *
 	 * See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}
diff --git a/vendor/scripts/preloadjs-NEXT.combined.js b/vendor/scripts/preloadjs-NEXT.combined.js
index 039c8aa61..94e9f4e28 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*/"Thu, 06 Mar 2014 22:58:10 GMT"; // injected by build process
+	s.buildDate = /*date*/"Wed, 02 Apr 2014 17:54:19 GMT"; // injected by build process
 
 })();
 /*
@@ -909,24 +909,32 @@ this.createjs = this.createjs||{};
 	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 relativePath:$3 path:$4 file:$5 extension:$6 query:$7.
-	 * @property FILE_PATTERN
-	 * @type {RegExp}
+	 * The Regular Expression used to test file URLS for an absolute path.
+	 * @property ABSOLUTE_PATH
 	 * @static
-	 * @protected
+	 * @type {RegExp}
+	 * @since 0.4.2
 	 */
-	s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
+	s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
 
 	/**
-	 * 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}
+	 * The Regular Expression used to test file URLS for an absolute path.
+	 * @property RELATIVE_PATH
 	 * @static
-	 * @protected
+	 * @type {RegExp}
+	 * @since 0.4.2
 	 */
-	s.PATH_PATTERN = /^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/;
+	s.RELATIVE_PATT = (/^[./]*?\//i);
+
+	/**
+	 * The Regular Expression used to test file URLS for an extension. Note that URIs must already have the query string
+	 * removed.
+	 * @property EXTENSION_PATT
+	 * @static
+	 * @type {RegExp}
+	 * @since 0.4.2
+	 */
+	s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
 
 	/**
 	 * If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
@@ -1164,29 +1172,49 @@ this.createjs = this.createjs||{};
 	};
 
 	/**
-	 * 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 FILE_PATTERN property for details on the return value.
-	 * This will return null if it does not match.
-	 * @protected
+	 * Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
+	 * <ul>
+	 *     <li>If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
+	 *     `//networkPath`)</li>
+	 *     <li>If the path is relative. Relative paths start with `../` or `/path` (or similar)</li>
+	 *     <li>The file extension. This is determined by the filename with an extension. Query strings are dropped, and
+	 *     the file path is expected to follow the format `name.ext`.</li>
+	 * </ul>
+	 *
+	 * <strong>Note:</strong> This has changed from earlier versions, which used a single, complicated Regular Expression, which
+	 * was difficult to maintain, and over-aggressive in determining all file properties. It has been simplified to
+	 * only pull out what it needs.
+	 * @param path
+	 * @returns {Object} An Object with an `absolute` and `relative` Boolean, as well as an optional 'extension` String
+	 * property, which is the lowercase extension.
+	 * @private
 	 */
 	p._parseURI = function(path) {
-		if (!path) { return null; }
-		return path.match(s.FILE_PATTERN);
-	};
+		var info = { absolute: false, relative:false };
+		if (path == null) { return info; };
 
-	/**
-	 * 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);
+		// Drop the query string
+		var queryIndex = path.indexOf("?");
+		if (queryIndex > -1) {
+			path = path.substr(0,queryIndex);
+		}
+
+		// Absolute
+		var match;
+		if (s.ABSOLUTE_PATT.test(path)) {
+			info.absolute = true;
+
+		// Relative
+		} else if (s.RELATIVE_PATT.test(path)) {
+			info.relative = true;
+		}
+
+		// Extension
+		if (match = path.match(s.EXTENSION_PATT)) {
+			info.extension = match[1].toLowerCase();
+		}
+		return info;
 	};
 
 	/**
@@ -2593,7 +2621,7 @@ TODO: WINDOWS ISSUES
 
 		// Determine Extension, etc.
 		var match = this._parseURI(item.src);
-		if (match != null) { item.ext = match[6]; }
+		if (match.extension) { item.ext = match.extension; }
 		if (item.type == null) {
 			item.type = this._getTypeByExtension(item.ext);
 		}
@@ -2602,13 +2630,13 @@ TODO: WINDOWS ISSUES
 		var bp = ""; // Store the generated basePath
 		var useBasePath = basePath || this._basePath;
 		var autoId = item.src;
-		if (match && match[1] == null && match[3] == null) {
+		if (!match.absolute && !match.relative) {
 			if (path) {
 				bp = path;
-				var pathMatch = this._parsePath(path);
+				var pathMatch = this._parseURI(path);
 				autoId = path + autoId;
 				// Also append basePath
-				if (useBasePath != null && pathMatch && pathMatch[1] == null && pathMatch[2] == null) {
+				if (useBasePath != null && !pathMatch.absolute && !pathMatch.relative) {
 					bp = useBasePath + bp;
 				}
 			} else if (useBasePath != null) {
@@ -2666,8 +2694,8 @@ TODO: WINDOWS ISSUES
 
 				// Update the extension in case the type changed:
 				match = this._parseURI(item.src);
-				if (match != null && match[6] != null) {
-					item.ext = match[6].toLowerCase();
+				if (match.extension != null) {
+					item.ext = match.extension;
 				}
 			}
 		}
@@ -3396,7 +3424,16 @@ this.createjs = this.createjs||{};
 			item.type == createjs.LoadQueue.CSS) {
 				this._startTagVisibility = tag.style.visibility;
 				tag.style.visibility = "hidden";
-				(document.body || document.getElementsByTagName("body")[0]).appendChild(tag);
+				var node = document.body || document.getElementsByTagName("body")[0];
+				if (node == null) {
+					if (item.type == createjs.LoadQueue.SVG) {
+						this._handleSVGError();
+						return;
+					} else {
+						node = document.head || document.getElementsByTagName("head");
+					}
+				}
+				node.appendChild(tag);
 		}
 
 		// Note: Previous versions didn't seem to work when we called load() for OGG tags in Firefox. Seems fixed in 15.0.1
@@ -3405,6 +3442,13 @@ this.createjs = this.createjs||{};
 		}
 	};
 
+	p._handleSVGError = function() {
+		this._clean();
+		var event = new createjs.Event("error");
+		event.text = "SVG_NO_BODY";
+		this._sendError(event);
+	};
+
 	p._handleJSONPLoad = function(data) {
 		this._jsonResult = data;
 	};
@@ -3488,8 +3532,8 @@ this.createjs = this.createjs||{};
 				// case createjs.LoadQueue.CSS:
 				//LM: We may need to remove CSS tags loaded using a LINK
 				tag.style.visibility = this._startTagVisibility;
-				(document.body || document.getElementsByTagName("body")[0]).removeChild(tag);
-			break;
+				tag.parentNode && tag.parentNode.contains(tag) && tag.parentNode.removeChild(tag);
+				break;
 			default:
 		}
 
diff --git a/vendor/scripts/soundjs-NEXT.combined.js b/vendor/scripts/soundjs-NEXT.combined.js
index 09e8920c7..42d6571a9 100644
--- a/vendor/scripts/soundjs-NEXT.combined.js
+++ b/vendor/scripts/soundjs-NEXT.combined.js
@@ -856,6 +856,72 @@ this.createjs = this.createjs||{};
 		};
 	}
 
+}());/*
+* defineProperty
+* Visit http://createjs.com/ for documentation, updates and examples.
+*
+* Copyright (c) 2010 gskinner.com, inc.
+* 
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use,
+* copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following
+* conditions:
+* 
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+* 
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+  * @module CreateJS
+ */
+
+// namespace:
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+(function() {
+	"use strict";
+
+	/**
+	 * Boolean value indicating if Object.defineProperty is supported.
+	 *
+	 * @property definePropertySupported
+	 * @type {Boolean}
+	 * @default true
+	 */
+	var t = Object.defineProperty ? true : false;
+
+	// IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
+	var foo = {};
+	try {
+		Object.defineProperty(foo, "bar", {
+			get: function() {
+				return this._bar;
+			},
+			set: function(value) {
+				this._bar = value;
+			}
+		});
+	} catch (e) {
+		t = false;
+	};
+
+	createjs.definePropertySupported = t;
 }());/*
  * Sound
  * Visit http://createjs.com/ for documentation, updates and examples.
@@ -920,8 +986,8 @@ this.createjs = this.createjs || {};
  *      }
  *
  * <h4>Browser Support</h4>
- * Audio will work in browsers which support HTMLAudioElement (<a href="http://caniuse.com/audio">http://caniuse.com/audio</a>)
- * or WebAudio (<a href="http://caniuse.com/audio-api">http://caniuse.com/audio-api</a>). A Flash fallback can be added
+ * Audio will work in browsers which support WebAudio (<a href="http://caniuse.com/audio-api">http://caniuse.com/audio-api</a>)
+ * or HTMLAudioElement (<a href="http://caniuse.com/audio">http://caniuse.com/audio</a>). A Flash fallback can be added
  * as well, which will work in any browser that supports the Flash player.
  * @module SoundJS
  * @main SoundJS
@@ -931,11 +997,6 @@ this.createjs = this.createjs || {};
 
 	"use strict";
 
-	//TODO: Interface to validate plugins and throw warnings
-	//TODO: Determine if methods exist on a plugin before calling  // OJR this is only an issue if something breaks or user changes something
-	//TODO: Interface to validate instances and throw warnings
-	//TODO: Surface errors on audio from all plugins
-	//TODO: Timeouts  // OJR for?
 	/**
 	 * The Sound class is the public API for creating sounds, controlling the overall sound levels, and managing plugins.
 	 * All Sound APIs on this class are static.
@@ -945,7 +1006,7 @@ this.createjs = this.createjs || {};
 	 * or register multiple sounds using {{#crossLink "Sound/registerManifest"}}{{/crossLink}}. If you don't register a
 	 * sound prior to attempting to play it using {{#crossLink "Sound/play"}}{{/crossLink}} or create it using {{#crossLink "Sound/createInstance"}}{{/crossLink}},
 	 * the sound source will be automatically registered but playback will fail as the source will not be ready. If you use
-	 * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, this is handled for you when the sound is
+	 * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, registration is handled for you when the sound is
 	 * preloaded. It is recommended to preload sounds either internally using the register functions or externally using
 	 * PreloadJS so they are ready when you want to use them.
 	 *
@@ -982,11 +1043,12 @@ this.createjs = this.createjs || {};
 	 *
 	 * Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
 	 * automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
-	 * load. As a result, it may not play immediately the first time play is called. Use the
+	 * load. As a result, it may fail to play the first time play is called if the audio is not finished loading. Use the
 	 * {{#crossLink "Sound/fileload"}}{{/crossLink}} event to determine when a sound has finished internally preloading.
 	 * It is recommended that all audio is preloaded before it is played.
 	 *
-	 *      createjs.PreloadJS.installPlugin(createjs.Sound);
+	 *      var queue = new createjs.LoadQueue();
+	 *		queue.installPlugin(createjs.Sound);
 	 *
 	 * <b>Mobile Safe Approach</b><br />
 	 * Mobile devices require sounds to be played inside of a user initiated event (touch/click) in varying degrees.
@@ -1009,6 +1071,7 @@ this.createjs = this.createjs || {};
 	 * when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
      * <li>MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
 	 * encoding with 64kbps works.</li>
+	 * <li>Occasionally very short samples will get cut off.</li>
 	 * <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by
 	 * hardware and browser settings.  See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate.</li></ul>
 	 *
@@ -1044,19 +1107,16 @@ this.createjs = this.createjs || {};
 
 	var s = Sound;
 
+	// TODO DEPRECATED
 	/**
-	 * 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.
+	 * REMOVED
+	 * Use {{#crossLink "Sound/alternateExtensions:property"}}{{/crossLink}} instead
 	 * @property DELIMITER
 	 * @type {String}
 	 * @default |
 	 * @static
 	 * @deprecated
 	 */
-	s.DELIMITER = "|";
 
 	/**
 	 * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
@@ -1159,7 +1219,7 @@ this.createjs = this.createjs || {};
 	 * @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
+	s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
 
 	/**
 	 * Some extensions use another type of extension support to play (one of them is a codex).  This allows you to map
@@ -1291,7 +1351,7 @@ this.createjs = this.createjs || {};
 	s._instances = [];
 
 	/**
-	 * An object hash storing sound sources via there corresponding ID.
+	 * An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
 	 * @property _idHash
 	 * @type {Object}
 	 * @protected
@@ -1346,16 +1406,6 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.1
 	 */
 
-	//TODO: Deprecated
-	/**
-	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "Sound/fileload:event"}}{{/crossLink}}
-	 * event.
-	 * @property onLoadComplete
-	 * @type {Function}
-	 * @deprecated Use addEventListener and the fileload event.
-	 * @since 0.4.0
-	 */
-
 	/**
 	 * Used by external plugins to dispatch file load events.
 	 * @method _sendFileLoadEvent
@@ -1365,9 +1415,8 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.1
 	 */
 	s._sendFileLoadEvent = function (src) {
-		if (!s._preloadHash[src]) {
-			return;
-		}
+		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;
@@ -1378,6 +1427,7 @@ this.createjs = this.createjs || {};
 			event.src = item.src;
 			event.id = item.id;
 			event.data = item.data;
+			event.sprite = item.sprite;
 
 			s.dispatchEvent(event);
 		}
@@ -1406,33 +1456,7 @@ this.createjs = this.createjs || {};
 	};
 
 	/**
-	 * Deprecated in favor of {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} with a single argument.
-	 *      createjs.Sound.registerPlugins([createjs.WebAudioPlugin]);
-	 *
-	 * @method registerPlugin
-	 * @param {Object} plugin The plugin class to install.
-	 * @return {Boolean} Whether the plugin was successfully initialized.
-	 * @static
-	 * @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);
-	};
-
-	/**
-	 * Register a Sound plugin. Plugins handle the actual playback of audio. The default plugins are
-	 * ({{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}),
-	 * and are installed if no other plugins are present when the user attempts to start playback or register sound.
-	 * <h4>Example</h4>
-	 *      createjs.FlashPlugin.swfPath = "../src/SoundJS/";
-	 *      createjs.Sound._registerPlugin(createjs.FlashPlugin);
-	 *
-	 * To register multiple plugins, use {{#crossLink "Sound/registerPlugins"}}{{/crossLink}}.
+	 * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
 	 *
 	 * @method _registerPlugin
 	 * @param {Object} plugin The plugin class to install.
@@ -1441,14 +1465,9 @@ this.createjs = this.createjs || {};
 	 * @private
 	 */
 	s._registerPlugin = function (plugin) {
-		s._pluginsRegistered = true;
-		if (plugin == null) {
-			return false;
-		}
 		// Note: Each plugin is passed in as a class reference, but we store the activePlugin as an instance
 		if (plugin.isSupported()) {
 			s.activePlugin = new plugin();
-			//TODO: Check error on initialization
 			return true;
 		}
 		return false;
@@ -1467,9 +1486,9 @@ this.createjs = this.createjs || {};
 	 * @static
 	 */
 	s.registerPlugins = function (plugins) {
+		s._pluginsRegistered = true;
 		for (var i = 0, l = plugins.length; i < l; i++) {
-			var plugin = plugins[i];
-			if (s._registerPlugin(plugin)) {
+			if (s._registerPlugin(plugins[i])) {
 				return true;
 			}
 		}
@@ -1489,15 +1508,9 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.0
 	 */
 	s.initializeDefaultPlugins = function () {
-		if (s.activePlugin != null) {
-			return true;
-		}
-		if (s._pluginsRegistered) {
-			return false;
-		}
-		if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {
-			return true;
-		}
+		if (s.activePlugin != null) {return true;}
+		if (s._pluginsRegistered) {return false;}
+		if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
 		return false;
 	};
 
@@ -1544,9 +1557,7 @@ this.createjs = this.createjs || {};
 	 * @static
 	 */
 	s.getCapabilities = function () {
-		if (s.activePlugin == null) {
-			return null;
-		}
+		if (s.activePlugin == null) {return null;}
 		return s.activePlugin._capabilities;
 	};
 
@@ -1564,9 +1575,7 @@ this.createjs = this.createjs || {};
 	 * @see getCapabilities
 	 */
 	s.getCapability = function (key) {
-		if (s.activePlugin == null) {
-			return null;
-		}
+		if (s.activePlugin == null) {return null;}
 		return s.activePlugin._capabilities[key];
 	};
 
@@ -1580,20 +1589,79 @@ this.createjs = this.createjs || {};
 	 * @param {String} [id] An optional user-specified id that is used to play sounds.
 	 * @param {Number|String|Boolean|Object} [data] Data associated with the item. Sound uses the data parameter as the
 	 * number of channels for an audio instance, however a "channels" property can be appended to the data object if
-	 * this property is used for other information. The audio channels will default to 1 if no value is found.
-	 * @param {String} [path] A combined basepath and subPath from PreloadJS that has already been prepended to src.
+	 * this property is used for other information. The audio channels will set a default based on plugin if no value is found.
 	 * @return {Boolean|Object} An object with the modified values of those that were passed in, or false if the active
 	 * plugin can not play the audio type.
 	 * @protected
 	 * @static
 	 */
-	s.initLoad = function (src, type, id, data, path) {
-		// remove path from src so we can continue to support "|" splitting of src files	// TODO remove this when "|" is removed
-		src = src.replace(path, "");
-		var details = s.registerSound(src, id, data, false, path);
-		if (details == null) {
-			return false;
+	s.initLoad = function (src, type, id, data) {
+		return s._registerSound(src, id, data);
+	};
+
+	/**
+	 * Internal method for loading sounds.  This should not be called directly.
+	 *
+	 * @method _registerSound
+	 * @param {String | Object} src The source to load.
+	 * @param {String} [id] An id specified by the user to play the sound later.
+	 * @param {Number | Object} [data] Data associated with the item. Sound uses the data parameter as the number of
+	 * 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.
+	 * Sound also uses the data property to hold an audioSprite array of objects in the following format {id, startTime, duration}.<br/>
+	 *   id used to play the sound later, in the same manner as a sound src with an id.<br/>
+	 *   startTime is the initial offset to start playback and loop from, in milliseconds.<br/>
+	 *   duration is the amount of time to play the clip for, in milliseconds.<br/>
+	 * This allows Sound to support audio sprites that are played back by id.
+	 * @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.
+	 * Returns true if the source is already loaded.
+	 * @static
+	 * @private
+	 * @since 0.5.3
+	 */
+
+	s._registerSound = function (src, id, data) {
+		if (!s.initializeDefaultPlugins()) {return false;}
+
+		var details = s._parsePath(src);
+		if (details == null) {return false;}
+		details.type = "sound";
+		details.id = id;
+		details.data = data;
+
+		var numChannels = s.activePlugin.defaultNumChannels || null;
+		if (data != null) {
+			if (!isNaN(data.channels)) {
+				numChannels = parseInt(data.channels);
+			} else if (!isNaN(data)) {
+				numChannels = parseInt(data);
+			}
+
+			if(data.audioSprite) {
+				var sp;
+				for(var i = data.audioSprite.length; i--; ) {
+					sp = data.audioSprite[i];
+					s._idHash[sp.id] = {src: details.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
+				}
+			}
 		}
+		if (id != null) {s._idHash[id] = {src: details.src}};
+		var loader = s.activePlugin.register(details.src, numChannels);  // Note only HTML audio uses numChannels
+
+		SoundChannel.create(details.src, numChannels);
+
+		// return the number of instances to the user.  This will also be returned in the load event.
+		if (data == null || !isNaN(data)) {
+			details.data = numChannels || SoundChannel.maxPerChannel();
+		} else {
+			details.data.channels = numChannels || SoundChannel.maxPerChannel();
+		}
+
+		details.tag = loader.tag;
+		if (loader.completeHandler) {details.completeHandler = loader.completeHandler;}
+		if (loader.type) {details.type = loader.type;}
+
 		return details;
 	};
 
@@ -1613,8 +1681,11 @@ this.createjs = this.createjs || {};
 	 * @param {Number | Object} [data] Data associated with the item. Sound uses the data parameter as the number of
 	 * 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.  This is currently used by PreloadJS when loading sounds to disable internal preloading.
+	 * Sound also uses the data property to hold an audioSprite array of objects in the following format {id, startTime, duration}.<br/>
+	 *   id used to play the sound later, in the same manner as a sound src with an id.<br/>
+	 *   startTime is the initial offset to start playback and loop from, in milliseconds.<br/>
+	 *   duration is the amount of time to play the clip for, in milliseconds.<br/>
+	 * This allows Sound to support audio sprites that are played back by id.
 	 * @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.
@@ -1622,90 +1693,26 @@ this.createjs = this.createjs || {};
 	 * @static
 	 * @since 0.4.0
 	 */
-	s.registerSound = function (src, id, data, preload, basePath) {
-		if (!s.initializeDefaultPlugins()) {
-			return false;
-		}
-
+	s.registerSound = function (src, id, data, basePath) {
 		if (src instanceof Object) {
-			basePath = id;	//this assumes preload has not be passed in as a property // OJR check if arguments == 3 would be less fragile
-			//?? preload = src.preload;
-			// OJR refactor how data is passed in to make the parameters work better
+			basePath = id;
 			id = src.id;
 			data = src.data;
 			src = src.src;
 		}
 
-		// branch to different parse based on alternate formats setting
-		if (s.alternateExtensions.length) {
-			var details = s._parsePath2(src, "sound", id, data);
+		if (basePath != null) {src = basePath + src;}
+
+		var details = s._registerSound(src, id, data);
+		if(!details) {return false;}
+
+		if (!s._preloadHash[details.src]) {	s._preloadHash[details.src] = [];}
+		s._preloadHash[details.src].push({src:src, id:id, data:details.data});
+		if (s._preloadHash[details.src].length == 1) {
+			// OJR note this will disallow reloading a sound if loading fails or the source changes
+			s.activePlugin.preload(details.src, details.tag);
 		} else {
-			var details = s._parsePath(src, "sound", id, data);
-		}
-		if (details == null) {
-			return false;
-		}
-		if (basePath != null) {
-			src = basePath + src;
-			details.src = basePath + details.src;
-		}
-
-		if (id != null) {
-			s._idHash[id] = details.src;
-		}
-
-		var numChannels = null; // null tells SoundChannel to set this to it's internal maxDefault
-		if (data != null) {
-			if (!isNaN(data.channels)) {
-				numChannels = parseInt(data.channels);
-			}
-			else if (!isNaN(data)) {
-				numChannels = parseInt(data);
-			}
-		}
-		var loader = s.activePlugin.register(details.src, numChannels);  // Note only HTML audio uses numChannels
-
-		if (loader != null) {	// all plugins currently return a loader
-			if (loader.numChannels != null) {
-				numChannels = loader.numChannels;
-			} // currently only HTMLAudio returns this
-			SoundChannel.create(details.src, numChannels);
-
-			// return the number of instances to the user.  This will also be returned in the load event.
-			if (data == null || !isNaN(data)) {
-				data = details.data = numChannels || SoundChannel.maxPerChannel();
-			} else {
-				data.channels = details.data.channels = numChannels || SoundChannel.maxPerChannel();
-			}
-
-			// 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) {
-				details.src = loader.src;
-			}
-			// If the loader returns a complete handler, pass it on to the prelaoder.
-			if (loader.completeHandler != null) {
-				details.completeHandler = loader.completeHandler;
-			}
-			if (loader.type) {
-				details.type = loader.type;
-			}
-		}
-
-		if (preload != false) {
-			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) {
-				// 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;}
 		}
 
 		return details;
@@ -1741,8 +1748,8 @@ this.createjs = this.createjs || {};
 	s.registerManifest = function (manifest, basePath) {
 		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
+			returnValues[i] = createjs.Sound.registerSound(manifest[i].src, manifest[i].id, manifest[i].data, basePath);
+		}
 		return returnValues;
 	};
 
@@ -1764,29 +1771,18 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.1
 	 */
 	s.removeSound = function(src, basePath) {
-		if (s.activePlugin == null) {
-			return false;
-		}
+		if (s.activePlugin == null) {return false;}
 
-		if (src instanceof Object) {
-			src = src.src;
-		}
-		src = s._getSrcById(src);
+		if (src instanceof Object) {src = src.src;}
+		src = s._getSrcById(src).src;
+		if (basePath != null) {src = basePath + src;}
 
-		if (s.alternateExtensions.length) {
-			var details = s._parsePath2(src);
-		} else {
-			var details = s._parsePath(src);
-		}
-		if (details == null) {
-			return false;
-		}
-		if (basePath != null) {details.src = basePath + details.src;}
+		var details = s._parsePath(src);
+		if (details == null) {return false;}
 		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) {
+			if(s._idHash[prop].src == src) {
 				delete(s._idHash[prop]);
 			}
 		}
@@ -1794,10 +1790,8 @@ this.createjs = this.createjs || {};
 		// clear from SoundChannel, which also stops and deletes all instances
 		SoundChannel.removeSrc(src);
 
-		// remove src from _preloadHash
 		delete(s._preloadHash[src]);
 
-		// activePlugin cleanup
 		s.activePlugin.removeSound(src);
 
 		return true;
@@ -1850,7 +1844,7 @@ this.createjs = this.createjs || {};
 		s._idHash = {};
 		s._preloadHash = {};
 		SoundChannel.removeAll();
-		s.activePlugin.removeAllSounds();
+		if (s.activePlugin) {s.activePlugin.removeAllSounds();}
 	};
 
 	/**
@@ -1869,89 +1863,41 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.0
 	 */
 	s.loadComplete = function (src) {
-		if (s.alternateExtensions.length) {
-			var details = s._parsePath2(src, "sound");
-		} else {
-			var details = s._parsePath(src, "sound");
-		}
+		var details = s._parsePath(src);
 		if (details) {
-			src = s._getSrcById(details.src);
+			src = s._getSrcById(details.src).src;
 		} else {
-			src = s._getSrcById(src);
+			src = s._getSrcById(src).src;
 		}
 		return (s._preloadHash[src][0] == true);  // src only loads once, so if it's true for the first it's true for all
 	};
 
 	/**
-	 * Parse the path of a sound, usually from a manifest item. Manifest items support single file paths, as well as
-	 * 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
+	 * Parse the path of a sound, usually from a manifest item. alternate extensions will be attempted in order if the
+	 * current extension is not supported
 	 * @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.
-	 * @param {Number | String | Boolean | Object} [data] Arbitrary data appended to the sound, usually denoting the
-	 * number of channels for the sound. This method doesn't currently do anything with the data property.
 	 * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
 	 * and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
 	 * @protected
 	 */
-	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++) {
-			var sound = sounds[i];
-
-			var match = sound.match(s.FILE_PATTERN);
-			if (match == null) {
-				return false;
-			}
-			var name = match[4];
-			var ext = match[5];
-
-			if (c[ext] && createjs.indexOf(s.SUPPORTED_EXTENSIONS, ext) > -1) {
-				ret.name = name;
-				ret.src = sound;
-				ret.extension = ext;
-				return ret;
-			}
-		}
-		return null;
-	};
-
-	// new approach, when old approach is deprecated this will become _parsePath
-	s._parsePath2 = function (value, type, id, data) {
+	s._parsePath = function (value) {
 		if (typeof(value) != "string") {value = value.toString();}
 
 		var match = value.match(s.FILE_PATTERN);
-		if (match == null) {
-			return false;
-		}
+		if (match == null) {return false;}
+
 		var name = match[4];
 		var ext = match[5];
-
 		var c = s.getCapabilities();
 		var i = 0;
 		while (!c[ext]) {
 			ext = s.alternateExtensions[i++];
 			if (i > s.alternateExtensions.length) { return null;}	// no extensions are supported
 		}
-
 		value = value.replace("."+match[5], "."+ext);
-		var ret = {type:type || "sound", id:id, data:data};
-		ret.name = name;
-		ret.src = value;
-		ret.extension = ext;
+
+		var ret = {name:name, src:value, extension:ext};
 		return ret;
 	};
 
@@ -1976,6 +1922,9 @@ this.createjs = this.createjs || {};
 	 *      	var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", createjs.Sound.INTERRUPT_ANY, 0, 0, -1, 1, 0);
 	 *      }
 	 *
+	 * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
+	 * This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
+	 *
 	 * @method play
 	 * @param {String} src The src or ID of the audio.
 	 * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
@@ -1983,7 +1932,7 @@ this.createjs = this.createjs || {};
 	 * constants on the Sound class, with the default defined by {{#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).
+	 * delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
 	 * @param {Number} [delay=0] The amount of time to delay the start of audio playback, in milliseconds.
 	 * @param {Number} [offset=0] The offset from the start of the audio to begin playback, in milliseconds.
 	 * @param {Number} [loop=0] How many times the audio loops when it reaches the end of playback. The default is 0 (no
@@ -1991,16 +1940,26 @@ this.createjs = this.createjs || {};
 	 * @param {Number} [volume=1] The volume of the sound, between 0 and 1. Note that the master volume is applied
 	 * against the individual volume.
 	 * @param {Number} [pan=0] The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
+	 * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
+	 * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
 	 * @return {SoundInstance} A {{#crossLink "SoundInstance"}}{{/crossLink}} that can be controlled after it is created.
 	 * @static
 	 */
-	s.play = function (src, interrupt, delay, offset, loop, volume, pan) {
-		var instance = s.createInstance(src);
+	s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) {
+		if (interrupt instanceof Object) {
+			delay = interrupt.delay;
+			offset = interrupt.offset;
+			loop = interrupt.loop;
+			volume = interrupt.volume;
+			pan = interrupt.pan;
+			startTime = interrupt.startTime;
+			duration = interrupt.duration;
+			interrupt = interrupt.interrupt;
 
-		var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan);
-		if (!ok) {
-			instance.playFailed();
 		}
+		var instance = s.createInstance(src, startTime, duration);
+		var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan);
+		if (!ok) {instance.playFailed();}
 		return instance;
 	};
 
@@ -2019,34 +1978,30 @@ this.createjs = this.createjs || {};
 	 *      	myInstance = createjs.Sound.createInstance("myAudioPath/mySound.mp3");
 	 *      }
 	 *
+	 * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
+	 * This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
+	 *
 	 * @method createInstance
 	 * @param {String} src The src or ID of the audio.
+	 * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
+	 * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
 	 * @return {SoundInstance} A {{#crossLink "SoundInstance"}}{{/crossLink}} that can be controlled after it is created.
 	 * Unsupported extensions will return the default SoundInstance.
 	 * @since 0.4.0
 	 */
-	s.createInstance = function (src) {
-		if (!s.initializeDefaultPlugins()) {
-			return s._defaultSoundInstance;
-		}
+	s.createInstance = function (src, startTime, duration) {
+		if (!s.initializeDefaultPlugins()) {return s._defaultSoundInstance;}
 
 		src = s._getSrcById(src);
 
-		if (s.alternateExtensions.length) {
-			var details = s._parsePath2(src, "sound");
-		} else {
-			var details = s._parsePath(src, "sound");
-		}
+		var details = s._parsePath(src.src);
 
 		var instance = null;
 		if (details != null && details.src != null) {
-			// make sure that we have a sound channel (sound is registered or previously played)
 			SoundChannel.create(details.src);
-			instance = s.activePlugin.create(details.src);
+			if (startTime == null) {startTime = src.startTime;}
+			instance = s.activePlugin.create(details.src, startTime, duration || src.duration);
 		} 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;
 		}
 
@@ -2068,13 +2023,11 @@ this.createjs = this.createjs || {};
 	 * @static
 	 */
 	s.setVolume = function (value) {
-		if (Number(value) == null) {
-			return false;
-		}
+		if (Number(value) == null) {return false;}
 		value = Math.max(0, Math.min(1, 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;
 			for (var i = 0, l = instances.length; i < l; i++) {
 				instances[i].setMasterVolume(value);
 			}
@@ -2096,14 +2049,6 @@ this.createjs = this.createjs || {};
 		return s._masterVolume;
 	};
 
-	/**
-	 * REMOVED. Please see {{#crossLink "Sound/setMute"}}{{/crossLink}}.
-	 * @method mute
-	 * @param {Boolean} value Whether the audio should be muted or not.
-	 * @static
-	 * @deprecated This function has been deprecated. Please use setMute instead.
-	 */
-
 	/**
 	 * Mute/Unmute all audio. Note that muted audio still plays at 0 volume. This global mute value is maintained
 	 * separately and when set will override, but not change the mute property of individual instances. To mute an individual
@@ -2119,9 +2064,7 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.0
 	 */
 	s.setMute = function (value) {
-		if (value == null || value == undefined) {
-			return false;
-		}
+		if (value == null) {return false;}
 
 		this._masterMute = value;
 		if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
@@ -2205,15 +2148,13 @@ this.createjs = this.createjs || {};
 		interrupt = interrupt || s.defaultInterruptBehavior;
 		if (delay == null) {delay = 0;}
 		if (offset == null) {offset = instance.getPosition();}
-		if (loop == null) {loop = 0;}
+		if (loop == null) {loop = 0;}	// OJR consider using instance._remainingLoops
 		if (volume == null) {volume = instance.volume;}
 		if (pan == null) {pan = instance.pan;}
 
 		if (delay == 0) {
 			var ok = s._beginPlaying(instance, interrupt, offset, loop, volume, pan);
-			if (!ok) {
-				return false;
-			}
+			if (!ok) {return false;}
 		} else {
 			//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
@@ -2251,11 +2192,8 @@ this.createjs = this.createjs || {};
 		}
 		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);
-			if (index > -1) {
-				this._instances.splice(index, 1);
-			}
+			if (index > -1) {this._instances.splice(index, 1);}
 			return false;
 		}
 		return true;
@@ -2266,15 +2204,12 @@ this.createjs = this.createjs || {};
 	 * instead.
 	 * @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.
+	 * @return {String} The source of the sound if it has been registered with this ID or the value that was passed in.
 	 * @protected
 	 * @static
 	 */
 	s._getSrcById = function (value) {
-		if (s._idHash == null || s._idHash[value] == null) {
-			return value;
-		}
-		return s._idHash[value];
+		return s._idHash[value] || {src: value};
 	};
 
 	/**
@@ -2289,9 +2224,7 @@ this.createjs = this.createjs || {};
 	s._playFinished = function (instance) {
 		SoundChannel.remove(instance);
 		var index = createjs.indexOf(this._instances, instance);
-		if (index > -1) {
-			this._instances.splice(index, 1);
-		}
+		if (index > -1) {this._instances.splice(index, 1);}	// OJR this will always be > -1, there is no way for an instance to exist without being added to this._instances
 	};
 
 	createjs.Sound = Sound;
@@ -2352,10 +2285,8 @@ this.createjs = this.createjs || {};
 	 */
 	SoundChannel.removeSrc = function (src) {
 		var channel = SoundChannel.get(src);
-		if (channel == null) {
-			return false;
-		}
-		channel.removeAll();	// this stops and removes all active instances
+		if (channel == null) {return false;}
+		channel._removeAll();	// this stops and removes all active instances
 		delete(SoundChannel.channels[src]);
 		return true;
 	};
@@ -2366,7 +2297,7 @@ this.createjs = this.createjs || {};
 	 */
 	SoundChannel.removeAll = function () {
 		for(var channel in SoundChannel.channels) {
-			SoundChannel.channels[channel].removeAll();	// this stops and removes all active instances
+			SoundChannel.channels[channel]._removeAll();	// this stops and removes all active instances
 		}
 		SoundChannel.channels = {};
 	};
@@ -2381,10 +2312,8 @@ this.createjs = this.createjs || {};
 	 */
 	SoundChannel.add = function (instance, interrupt) {
 		var channel = SoundChannel.get(instance.src);
-		if (channel == null) {
-			return false;
-		}
-		return channel.add(instance, interrupt);
+		if (channel == null) {return false;}
+		return channel._add(instance, interrupt);
 	};
 	/**
 	 * Remove an instance from the channel.
@@ -2395,10 +2324,8 @@ this.createjs = this.createjs || {};
 	 */
 	SoundChannel.remove = function (instance) {
 		var channel = SoundChannel.get(instance.src);
-		if (channel == null) {
-			return false;
-		}
-		channel.remove(instance);
+		if (channel == null) {return false;}
+		channel._remove(instance);
 		return true;
 	};
 	/**
@@ -2461,9 +2388,7 @@ this.createjs = this.createjs || {};
 	p.init = function (src, max) {
 		this.src = src;
 		this.max = max || this.maxDefault;
-		if (this.max == -1) {
-			this.max = this.maxDefault;
-		}
+		if (this.max == -1) {this.max = this.maxDefault;}
 		this._instances = [];
 	};
 
@@ -2473,7 +2398,7 @@ this.createjs = this.createjs || {};
 	 * @param {Number} index The index to return.
 	 * @return {SoundInstance} The SoundInstance at a specific instance.
 	 */
-	p.get = function (index) {
+	p._get = function (index) {
 		return this._instances[index];
 	};
 
@@ -2483,10 +2408,8 @@ this.createjs = this.createjs || {};
 	 * @param {SoundInstance} instance The instance to add.
 	 * @return {Boolean} The success of the method call. If the channel is full, it will return false.
 	 */
-	p.add = function (instance, interrupt) {
-		if (!this.getSlot(interrupt, instance)) {
-			return false;
-		}
+	p._add = function (instance, interrupt) {
+		if (!this._getSlot(interrupt, instance)) {return false;}
 		this._instances.push(instance);
 		this.length++;
 		return true;
@@ -2499,11 +2422,9 @@ this.createjs = this.createjs || {};
 	 * @return {Boolean} The success of the remove call. If the instance is not found in this channel, it will
 	 * return false.
 	 */
-	p.remove = function (instance) {
+	p._remove = function (instance) {
 		var index = createjs.indexOf(this._instances, instance);
-		if (index == -1) {
-			return false;
-		}
+		if (index == -1) {return false;}
 		this._instances.splice(index, 1);
 		this.length--;
 		return true;
@@ -2513,8 +2434,8 @@ this.createjs = this.createjs || {};
 	 * Stop playback and remove all instances from the channel.  Usually in response to a delete call.
 	 * #method removeAll
 	 */
-	p.removeAll = function () {
-		// Note that stop() removes the item from the list, but we don't want to assume that.
+	p._removeAll = function () {
+		// Note that stop() removes the item from the list
 		for (var i=this.length-1; i>=0; i--) {
 			this._instances[i].stop();
 		}
@@ -2528,11 +2449,11 @@ this.createjs = this.createjs || {};
 	 * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
 	 * an existing SoundInstance may be interrupted. If there are no slots, this method returns false.
 	 */
-	p.getSlot = function (interrupt, instance) {
+	p._getSlot = function (interrupt, instance) {
 		var target, replacement;
 
 		for (var i = 0, l = this.max; i < l; i++) {
-			target = this.get(i);
+			target = this._get(i);
 
 			// Available Space
 			if (target == null) {
@@ -2554,16 +2475,15 @@ this.createjs = this.createjs || {};
 				replacement = target;
 
 				// Audio is a better candidate than the current target, according to playhead
-			} else if (
-					(interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) ||
-							(interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) {
+			} else if (	(interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) ||
+						(interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) {
 				replacement = target;
 			}
 		}
 
 		if (replacement != null) {
 			replacement._interrupt();
-			this.remove(replacement);
+			this._remove(replacement);
 			return true;
 		}
 		return false;
@@ -2720,9 +2640,7 @@ this.createjs = this.createjs || {};
 		// 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;
-		}
+		if (s.context == null) {return false;}
 		return true;
 	};
 
@@ -2740,7 +2658,7 @@ this.createjs = this.createjs || {};
 
 		var xhr = new XMLHttpRequest();
 		try {
-			xhr.open("GET", "fail.fail", false); // loading non-existant file triggers 404 only if it could load (synchronous call)
+			xhr.open("GET", "WebAudioPluginTest.fail", false); // loading non-existant file triggers 404 only if it could load (synchronous call)
 		} catch (error) {
 			// catch errors in cases where the onerror is passed by
 			supported = false;
@@ -2767,28 +2685,19 @@ this.createjs = this.createjs || {};
 	 * @protected
 	 */
 	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,
-		// therefore tag is still required for the capabilities check
+		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
 		var t = document.createElement("audio");
+		if (t.canPlayType == null) {return null;}
 
-		if (t.canPlayType == null) {
-			return null;
-		}
-
-		// This check is first because it's what is currently used, but the spec calls for it to be AudioContext so this
-		//  will probably change in time
-		if (window.webkitAudioContext) {
-			s.context = new webkitAudioContext();
-		} else if (window.AudioContext) {
+		if (window.AudioContext) {
 			s.context = new AudioContext();
+		} else if (window.webkitAudioContext) {
+			s.context = new webkitAudioContext();
 		} else {
 			return null;
 		}
 
-		// this handles if only deprecated Web Audio API calls are supported
 		s._compatibilitySetUp();
 
 		// playing this inside of a touch event will enable audio on iOS, which starts muted
@@ -2814,12 +2723,6 @@ this.createjs = this.createjs || {};
 		if (s.context.destination.numberOfChannels < 2) {
 			s._capabilities.panning = false;
 		}
-
-		// set up AudioNodes that all of our source audio will connect to
-		s.dynamicsCompressorNode = s.context.createDynamicsCompressor();
-		s.dynamicsCompressorNode.connect(s.context.destination);
-		s.gainNode = s.context.createGain();
-		s.gainNode.connect(s.dynamicsCompressorNode);
 	};
 
 	/**
@@ -2829,10 +2732,12 @@ this.createjs = this.createjs || {};
 	 * don't support new calls.
 	 *
 	 * @method _compatibilitySetUp
+	 * @static
 	 * @protected
 	 * @since 0.4.2
 	 */
 	s._compatibilitySetUp = function() {
+		s._panningModel = "equalpower";
 		//assume that if one new call is supported, they all are
 		if (s.context.createGain) { return; }
 
@@ -2845,7 +2750,7 @@ this.createjs = this.createjs || {};
 		audioNode.__proto__.stop = audioNode.__proto__.noteOff;
 
 		// panningModel
-		this._panningModel = 0;
+		s._panningModel = 0;
 	};
 
 	/**
@@ -2855,24 +2760,18 @@ this.createjs = this.createjs || {};
 	 * for example).
 	 *
 	 * <h4>Example</h4>
-	 *
 	 *     function handleTouch(event) {
 	 *         createjs.WebAudioPlugin.playEmptySound();
 	 *     }
 	 *
 	 * @method playEmptySound
+	 * @static
 	 * @since 0.4.1
 	 */
 	s.playEmptySound = function() {
-		// create empty buffer
-		var buffer = this.context.createBuffer(1, 1, 22050);
-		var source = this.context.createBufferSource();
-		source.buffer = buffer;
-
-		// connect to output (your speakers)
-		source.connect(this.context.destination);
-
-		// play the file
+		var source = s.context.createBufferSource();
+		source.buffer = s.context.createBuffer(1, 1, 22050);
+		source.connect(s.context.destination);
 		source.start(0, 0, 0);
 	};
 
@@ -2888,7 +2787,6 @@ this.createjs = this.createjs || {};
 	 * @default 1
 	 * @protected
 	 */
-	// TODO refactor Sound.js so we can use getter setter for volume
 	p._volume = 1;
 
 	/**
@@ -2910,20 +2808,24 @@ this.createjs = this.createjs || {};
 	/**
 	 * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
 	 * It is connected to <code>context.destination</code>.
+	 *
+	 * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode.
 	 * @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}}.
+	 *
+	 * Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode.
 	 * @property gainNode
 	 * @type {AudioGainNode}
 	 */
 	p.gainNode = null;
 
 	/**
-	 * An object hash used internally to store ArrayBuffers, indexed by the source URI used  to load it. This
+	 * An object hash used internally to store ArrayBuffers, indexed by the source URI used to load it. This
 	 * 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.
@@ -2943,8 +2845,13 @@ this.createjs = this.createjs || {};
 		this._arrayBuffers = {};
 
 		this.context = s.context;
-		this.gainNode = s.gainNode;
-		this.dynamicsCompressorNode = s.dynamicsCompressorNode;
+		this._panningModel = s._panningModel;
+
+		// set up AudioNodes that all of our source audio will connect to
+		this.dynamicsCompressorNode = this.context.createDynamicsCompressor();
+		this.dynamicsCompressorNode.connect(this.context.destination);
+		this.gainNode = this.context.createGain();
+		this.gainNode.connect(this.dynamicsCompressorNode);
 	};
 
 	/**
@@ -2958,11 +2865,9 @@ 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
-		var tag = new createjs.WebAudioPlugin.Loader(src, this);
-		return {
-			tag:tag
-		};
+		this._arrayBuffers[src] = true;
+		var loader = {tag: new createjs.WebAudioPlugin.Loader(src, this)};
+		return loader;
 	};
 
 	/**
@@ -3021,19 +2926,18 @@ this.createjs = this.createjs || {};
 	 * @method _handlePreloadComplete
 	 * @protected
 	 */
-	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
-		// note "this" will reference Loader object
+	p._handlePreloadComplete = function (loader) {
+		createjs.Sound._sendFileLoadEvent(loader.src);
+		loader.cleanUp();
 	};
 
 	/**
 	 * Internally preload a sound. Loading uses XHR2 to load an array buffer for use with WebAudio.
 	 * @method preload
 	 * @param {String} src The sound URI to load.
-	 * @param {Object} instance Not used in this plugin.
+	 * @param {Object} tag Not used in this plugin.
 	 */
-	p.preload = function (src, instance) {
+	p.preload = function (src, tag) {
 		this._arrayBuffers[src] = true;
 		var loader = new createjs.WebAudioPlugin.Loader(src, this);
 		loader.onload = this._handlePreloadComplete;
@@ -3044,13 +2948,13 @@ this.createjs = this.createjs || {};
 	 * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here.
 	 * @method create
 	 * @param {String} src The sound source to use.
+	 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
+	 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
 	 * @return {SoundInstance} A sound instance for playback and control.
 	 */
-	p.create = function (src) {
-		if (!this.isPreloadStarted(src)) {
-			this.preload(src);
-		}
-		return new createjs.WebAudioPlugin.SoundInstance(src, this);
+	p.create = function (src, startTime, duration) {
+		if (!this.isPreloadStarted(src)) {this.preload(src);}
+		return new createjs.WebAudioPlugin.SoundInstance(src, startTime, duration, this);
 	};
 
 	/**
@@ -3116,7 +3020,6 @@ this.createjs = this.createjs || {};
 	 * for control by the user.
 	 *
 	 * <h4>Example</h4>
-	 *
 	 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
 	 *
 	 * A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound
@@ -3144,12 +3047,14 @@ this.createjs = this.createjs || {};
 	 *
 	 * @class SoundInstance
 	 * @param {String} src The path to and file name of the sound.
+	 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
+	 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
 	 * @param {Object} owner The plugin instance that created this SoundInstance.
 	 * @extends EventDispatcher
 	 * @constructor
 	 */
-	function SoundInstance(src, owner) {
-		this._init(src, owner);
+	function SoundInstance(src, startTime, duration, owner) {
+		this._init(src, startTime, duration, owner);
 	}
 
 	var p = SoundInstance.prototype = new createjs.EventDispatcher();
@@ -3199,14 +3104,12 @@ this.createjs = this.createjs || {};
 	p._offset = 0;
 
 	/**
-	 * The time in milliseconds before the sound starts.
-	 * Note this is handled by {{#crossLink "Sound"}}{{/crossLink}}.
-	 * @property _delay
+	 * Audio sprite property used to determine the starting offset.
 	 * @type {Number}
-	 * @default 0
+	 * @default null
 	 * @protected
 	 */
-	p._delay = 0;	// OJR remove this property from SoundInstance as it is not used here?
+	p._startTime = 0;
 
 	/**
 	 * The volume of the sound, between 0 and 1.
@@ -3221,8 +3124,7 @@ this.createjs = this.createjs || {};
 	 * @default 1
 	 */
 	p._volume =  1;
-	// IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
-	try {
+	if (createjs.definePropertySupported) {
 		Object.defineProperty(p, "volume", {
 		get: function() {
 			return this._volume;
@@ -3234,9 +3136,7 @@ this.createjs = this.createjs || {};
 			this._updateVolume();
 		}
 		});
-	} catch (e) {
-		// dispatch message or error?
-	};
+	}
 
 	/**
 	 * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
@@ -3250,8 +3150,7 @@ this.createjs = this.createjs || {};
 	 * @default 0
 	 */
 	p._pan =  0;
-	// IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
-	try {
+	if (createjs.definePropertySupported) {
 		Object.defineProperty(p, "pan", {
 			get: function() {
 				return this._pan;
@@ -3265,10 +3164,7 @@ this.createjs = this.createjs || {};
 				this.panNode.setPosition(value, 0, -0.5);  // z need to be -0.5 otherwise the sound only plays in left, right, or center
 			}
 		});
-	} catch (e) {
-		// dispatch message or error?
-	};
-
+	}
 
 /**
 	 * The length of the audio clip, in milliseconds.
@@ -3291,7 +3187,7 @@ this.createjs = this.createjs || {};
 
 	/**
 	 * 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.
+	 * This allows SoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins.
 	 * @property _delayTimeoutId
 	 * @type {timeoutVariable}
 	 * @default null
@@ -3376,13 +3272,13 @@ this.createjs = this.createjs || {};
 	/**
 	 * WebAudioPlugin only.
 	 * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
-	 * @property _startTime
+	 * @property _playbackStartTime
 	 * @type {Number}
 	 * @default 0
 	 * @protected
 	 * @since 0.4.0
 	 */
-	p._startTime = 0;
+	p._playbackStartTime = 0;
 
 	// Proxies, make removing listeners easier.
 	p._endedHandler = null;
@@ -3432,43 +3328,6 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.0
 	 */
 
-	//TODO: Deprecated
-	/**
-	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/succeeded:event"}}{{/crossLink}}
-	 * event.
-	 * @property onPlaySucceeded
-	 * @type {Function}
-	 * @deprecated Use addEventListener and the "succeeded" event.
-	 */
-	/**
-	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/interrupted:event"}}{{/crossLink}}
-	 * event.
-	 * @property onPlayInterrupted
-	 * @type {Function}
-	 * @deprecated Use addEventListener and the "interrupted" event.
-	 */
-	/**
-	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/failed:event"}}{{/crossLink}}
-	 * event.
-	 * @property onPlayFailed
-	 * @type {Function}
-	 * @deprecated Use addEventListener and the "failed" event.
-	 */
-	/**
-	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/complete:event"}}{{/crossLink}}
-	 * event.
-	 * @property onComplete
-	 * @type {Function}
-	 * @deprecated Use addEventListener and the "complete" event.
-	 */
-	/**
-	 * REMOVED. Use {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}} and the {{#crossLink "SoundInstance/loop:event"}}{{/crossLink}}
-	 * event.
-	 * @property onLoop
-	 * @type {Function}
-	 * @deprecated Use addEventListener and the "loop" event.
-	 */
-
 	/**
 	 * A helper method that dispatches all events for SoundInstance.
 	 * @method _sendEvent
@@ -3485,22 +3344,24 @@ this.createjs = this.createjs || {};
 	 * Initialize the SoundInstance. This is called from the constructor.
 	 * @method _init
 	 * @param {string} src The source of the audio.
+	 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
+	 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
 	 * @param {Class} owner The plugin that created this instance.
 	 * @protected
 	 */
-	p._init = function (src, owner) {
-		this._owner = owner;
+	p._init = function (src, startTime, duration, owner) {
 		this.src = src;
+		this._startTime = startTime * 0.001 || 0;	// convert ms to s as web audio handles everything in seconds
+		this._duration = duration || 0;
+		this._owner = owner;
 
 		this.gainNode = this._owner.context.createGain();
 
-		this.panNode = this._owner.context.createPanner();  //TODO test how this affects when we have mono audio
+		this.panNode = this._owner.context.createPanner();
 		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._duration = this._owner._arrayBuffers[this.src].duration * 1000;}
 
 		this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
 	};
@@ -3516,19 +3377,14 @@ this.createjs = this.createjs || {};
 			this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
 		}
 
-		if (this.gainNode.numberOfOutputs != 0) {
-			this.gainNode.disconnect(0);
-		}  // this works because we only have one connection, and it returns 0 if we've already disconnected it.
+		if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
 		// 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
 
-		this._startTime = 0;	// This is used by getPosition
+		this._playbackStartTime = 0;	// This is used by getPosition
 
-		if (window.createjs == null) {
-			return;
-		}
 		createjs.Sound._playFinished(this);
 	};
 
@@ -3544,7 +3400,7 @@ this.createjs = this.createjs || {};
 		if(audioNode) {
 			audioNode.stop(0);
 			audioNode.disconnect(this.panNode);
-			audioNode = null;	// release reference so Web Audio can handle removing references and garbage collection
+			audioNode = null;
 		}
 		return audioNode;
 	};
@@ -3567,11 +3423,8 @@ this.createjs = this.createjs || {};
 	 * @protected
  	 */
 	p._handleSoundReady = function (event) {
-		if (window.createjs == null) {
-			return;
-		}
-
-		if ((this._offset*1000) > this.getDuration()) {	// converting offset to ms
+		if (!this._duration) {this._duration = this._owner._arrayBuffers[this.src].duration * 1000;} // NOTE *1000 because WebAudio reports everything in seconds but js uses milliseconds
+		if ((this._offset*1000) > this._duration) {
 			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
@@ -3583,15 +3436,14 @@ this.createjs = this.createjs || {};
 
 		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;
+		var dur = this._duration * 0.001;
 		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._playbackStartTime = this.sourceNode.startTime - this._offset;
 
 		this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - this._offset) * 1000);
 
 		if(this._remainingLoops != 0) {
-			this._sourceNodeNext = this._createAndPlayAudioNode(this._startTime, 0);
+			this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
 		}
 	};
 
@@ -3608,9 +3460,9 @@ this.createjs = this.createjs || {};
 		var audioNode = this._owner.context.createBufferSource();
 		audioNode.buffer = this._owner._arrayBuffers[this.src];
 		audioNode.connect(this.panNode);
-		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);
+		var dur = this._duration * 0.001;
+		audioNode.startTime = startTime + dur;
+		audioNode.start(audioNode.startTime, offset+this._startTime, dur - offset);
 		return audioNode;
 	};
 
@@ -3654,15 +3506,7 @@ this.createjs = this.createjs || {};
 	 * @protected
 	 */
 	p._beginPlaying = function (offset, loop, volume, pan) {
-		if (window.createjs == null) {
-			return;
-		}
-
-		if (!this.src) {
-			return;
-		}
-
-		this._offset = offset / 1000;  //convert ms to sec
+		this._offset = offset * 0.001;  //convert ms to sec
 		this._remainingLoops = loop;
 		this.volume = volume;
 		this.pan = pan;
@@ -3689,22 +3533,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 = this._paused = true;
+		if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;}
 
-			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);
+		this.paused = this._paused = true;
 
-			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.
+		this._offset = this._owner.context.currentTime - this._playbackStartTime;  // 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);
 
-			clearTimeout(this._delayTimeoutId); // clear timeout that plays delayed sound
-			clearTimeout(this._soundCompleteTimeout);  // clear timeout that triggers sound complete
-			return true;
-		}
-		return false;
+		if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect();}
+
+		clearTimeout(this._delayTimeoutId);
+		clearTimeout(this._soundCompleteTimeout);
+		return true;
 	};
 
 	/**
@@ -3721,10 +3562,8 @@ 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) {
-			return false;
-		}
-		this._handleSoundReady(null);
+		if (!this._paused) {return false;}
+		this._handleSoundReady();
 		return true;
 	};
 
@@ -3764,23 +3603,20 @@ this.createjs = this.createjs || {};
 	 */
 	p.setVolume = function (value) {
 		this.volume = value;
-		return true;  // This is always true because even if the volume is not updated, the value is set
+		return true;
 	};
 
 	/**
 	 * Internal function used to update the volume based on the instance volume, master volume, instance mute value,
 	 * and master mute value.
 	 * @method _updateVolume
-	 * @return {Boolean} if the volume was updated.
 	 * @protected
 	 */
 	p._updateVolume = function () {
 		var newVolume = this._muted ? 0 : this._volume;
 		if (newVolume != this.gainNode.gain.value) {
 			this.gainNode.gain.value = newVolume;
-			return true;
 		}
-		return false;
 	};
 
 	/**
@@ -3810,9 +3646,7 @@ this.createjs = this.createjs || {};
 	 * @since 0.4.0
 	 */
 	p.setMute = function (value) {
-		if (value == null || value == undefined) {
-			return false;
-		}
+		if (value == null) {return false;}
 
 		this._muted = value;
 		this._updateVolume();
@@ -3852,6 +3686,7 @@ this.createjs = this.createjs || {};
 	p.setPan = function (value) {
 		this.pan = value;  // Unfortunately panner does not give us a way to access this after it is set http://www.w3.org/TR/webaudio/#AudioPannerNode
 		if(this.pan != value) {return false;}
+		return true;
 	};
 
 	/**
@@ -3885,7 +3720,7 @@ this.createjs = this.createjs || {};
 		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._playbackStartTime;
 		}
 
 		return pos * 1000; // pos in seconds * 1000 to give milliseconds
@@ -3903,7 +3738,7 @@ 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 * 0.001; // 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
@@ -3912,9 +3747,7 @@ this.createjs = this.createjs || {};
 			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();}
 
 		return true;
 	};
@@ -3955,21 +3788,18 @@ this.createjs = this.createjs || {};
 			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._playbackStartTime = this.sourceNode.startTime;
+				this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
 				this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
 			}
 			else {
-				this._handleSoundReady(null);
+				this._handleSoundReady();
 			}
 
 			this._sendEvent("loop");
 			return;
 		}
 
-		if (window.createjs == null) {
-			return;
-		}
 		this._cleanUp();
 		this.playState = createjs.Sound.PLAY_FINISHED;
 		this._sendEvent("complete");
@@ -3977,9 +3807,6 @@ this.createjs = this.createjs || {};
 
 	// Play has failed, which can happen for a variety of reasons.
 	p.playFailed = function () {
-		if (window.createjs == null) {
-			return;
-		}
 		this._cleanUp();
 		this.playState = createjs.Sound.PLAY_FAILED;
 		this._sendEvent("failed");
@@ -4023,13 +3850,6 @@ this.createjs = this.createjs || {};
 	 */
 	p.src = null;
 
-	/**
-	 * The original source of the sound, before it is altered with a basePath.
-	 * #property src
-	 * @type {String}
-	 */
-	p.originalSrc = null;
-
 	/**
 	 * The decoded AudioBuffer array that is returned when loading is complete.
 	 * #property result
@@ -4054,17 +3874,16 @@ this.createjs = this.createjs || {};
 	p.onprogress = null;
 
 	/**
-	 * The callback that fires if the load hits an error.
-	 * #property onError
+	 * The callback that fires if the load hits an error.  This follows HTML tag naming.
+	 * #property onerror
 	 * @type {Method}
 	 * @protected
 	 */
-	p.onError = null;
+	p.onerror = null;
 
 	// constructor
 	p._init = function (src, owner) {
 		this.src = src;
-		this.originalSrc = src;
 		this.owner = owner;
 	};
 
@@ -4074,16 +3893,13 @@ this.createjs = this.createjs || {};
 	 * @param {String} src The path to the sound.
 	 */
 	p.load = function (src) {
-		if (src != null) {
-			// TODO does this need to set this.originalSrc
-			this.src = src;
-		}
+		if (src != null) {this.src = src;}
 
 		this.request = new XMLHttpRequest();
 		this.request.open("GET", this.src, true);
 		this.request.responseType = "arraybuffer";
 		this.request.onload = createjs.proxy(this.handleLoad, this);
-		this.request.onError = createjs.proxy(this.handleError, this);
+		this.request.onerror = createjs.proxy(this.handleError, this);
 		this.request.onprogress = createjs.proxy(this.handleProgress, this);
 
 		this.request.send();
@@ -4101,7 +3917,7 @@ this.createjs = this.createjs || {};
 	 */
 	p.handleProgress = function (loaded, total) {
 		this.progress = loaded / total;
-		this.onprogress != null && this.onprogress({loaded:loaded, total:total, progress:this.progress});
+		this.onprogress && this.onprogress({loaded:loaded, total:total, progress:this.progress});
 	};
 
 	/**
@@ -4123,9 +3939,8 @@ this.createjs = this.createjs || {};
 	p.handleAudioDecoded = function (decodedAudio) {
 		this.progress = 1;
 		this.result = decodedAudio;
-		this.src = this.originalSrc;
 		this.owner.addPreloadResults(this.src, this.result);
-		this.onload && this.onload();
+		this.onload && this.onload(this);
 	};
 
 	/**
@@ -4138,6 +3953,23 @@ this.createjs = this.createjs || {};
 		this.onerror && this.onerror(evt);
 	};
 
+	/**
+	 * Remove all external references from loader
+	 * #method cleanUp
+	 */
+	p.cleanUp = function () {
+		if(!this.request) {return;}
+		this.src = null;
+		this.owner = null;
+		this.request.onload = null;
+		this.request.onerror = null;
+		this.request.onprogress = null;
+		this.request = null;
+		this.onload = null;
+		this.onprogress = null;
+		this.onerror = null;
+	};
+
 	p.toString = function () {
 		return "[WebAudioPlugin Loader]";
 	};
@@ -4197,12 +4029,13 @@ this.createjs = this.createjs || {};
 	 * 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 />
+     * <b>IE html limitations</b><br />
      * <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
      * muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
      * when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
      * <li>MP3 encoding will not always work for audio tags if it's not default.  We've found default encoding with
      * 64kbps works.</li>
+	 * <li>Occasionally very short samples will get cut off.</li>
 	 * <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by
 	 * hardware and browser settings.  See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate.</li></ul>
 	 *
@@ -4291,6 +4124,17 @@ this.createjs = this.createjs || {};
 	 */
 	s._AUDIO_STALLED = "stalled";
 
+	/**
+	 * Event constant for the "timeupdate" event for cleaner code.  Utilized for looping audio sprites.
+	 * This event callsback ever 15 to 250ms and can be dropped by the browser for performance.
+	 * @property _TIME_UPDATE
+	 * @type {String}
+	 * @default timeupdate
+	 * @static
+	 * @protected
+	 */
+	s._TIME_UPDATE = "timeupdate";
+
 	/**
 	 * 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
@@ -4325,14 +4169,9 @@ this.createjs = this.createjs || {};
 	 * @static
 	 */
 	s.isSupported = function () {
-		if (createjs.Sound.BrowserDetect.isIOS && !s.enableIOS) {
-			return false;
-		}
+		if (createjs.Sound.BrowserDetect.isIOS && !s.enableIOS) {return false;}
 		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) {
-			return false;
-		}
+		if (s._capabilities == null) {return false;}
 		return true;
 	};
 
@@ -4344,13 +4183,9 @@ this.createjs = this.createjs || {};
 	 * @protected
 	 */
 	s._generateCapabilities = function () {
-		if (s._capabilities != null) {
-			return;
-		}
-		var t = s.tag = document.createElement("audio");
-		if (t.canPlayType == null) {
-			return null;
-		}
+		if (s._capabilities != null) {return;}
+		var t = document.createElement("audio");
+		if (t.canPlayType == null) {return null;}
 
 		s._capabilities = {
 			panning:true,
@@ -4383,7 +4218,7 @@ this.createjs = this.createjs || {};
 	p._audioSources = null;
 
 	/**
-	 * The default number of instances to allow.  Passed back to {{#crossLink "Sound"}}{{/crossLink}} when a source
+	 * The default number of instances to allow.  Used by {{#crossLink "Sound"}}{{/crossLink}} when a source
 	 * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method.  This is only used if
 	 * a value is not provided.
 	 *
@@ -4395,9 +4230,6 @@ this.createjs = this.createjs || {};
 	 */
 	p.defaultNumChannels = 2;
 
-	// Proxies, make removing listeners easier.
-	p.loadedHandler = null;
-
 	/**
 	 * An initialization function run by the constructor
 	 * @method _init
@@ -4422,52 +4254,17 @@ this.createjs = this.createjs || {};
 		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?
+		var l = instances;
+		for (var i = 0; i < l; i++) {
 			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
-		tag.addEventListener && tag.addEventListener("canplaythrough", this.loadedHandler);
-		if(tag.onreadystatechange == null) {
-			tag.onreadystatechange = this.loadedHandler;
-		} else {
-			var f = tag.onreadystatechange;
-			// OJR will this lose scope?
-			tag.onreadystatechange = function() {
-				f();
-				this.loadedHandler();
-			}
-		}
-
 		return {
-			tag:tag, // Return one instance for preloading purposes
-			numChannels:l  // The default number of channels to make for this Sound or the passed in value
+			tag:tag // Return one instance for preloading purposes
 		};
 	};
 
-	// 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
-	 * @param event
-	 * @protected
-	 * @deprecated
-	 */
-	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;
-
-		if (event.target.src == event.target.id) { return; }
-		// else src has changed before loading, and we need to make the change to TagPool because we pre create tags
-		createjs.HTMLAudioPlugin.TagPool.checkSrc(event.target.id);
-	};
-
 	/**
 	 * Create an HTML audio tag.
 	 * @method _createTag
@@ -4503,7 +4300,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 = {};
 		createjs.HTMLAudioPlugin.TagPool.removeAll();
 	};
 
@@ -4511,9 +4308,11 @@ this.createjs = this.createjs || {};
 	 * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here.
 	 * @method create
 	 * @param {String} src The sound source to use.
+	 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
+	 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
 	 * @return {SoundInstance} A sound instance for playback and control.
 	 */
-	p.create = function (src) {
+	p.create = function (src, startTime, duration) {
 		// if this sound has not be registered, create a tag and preload it
 		if (!this.isPreloadStarted(src)) {
 			var channel = createjs.HTMLAudioPlugin.TagPool.get(src);
@@ -4523,7 +4322,7 @@ this.createjs = this.createjs || {};
 			this.preload(src, {tag:tag});
 		}
 
-		return new createjs.HTMLAudioPlugin.SoundInstance(src, this);
+		return new createjs.HTMLAudioPlugin.SoundInstance(src, startTime, duration, this);
 	};
 
 	/**
@@ -4541,12 +4340,12 @@ this.createjs = this.createjs || {};
 	 * Internally preload a sound.
 	 * @method preload
 	 * @param {String} src The sound URI to load.
-	 * @param {Object} instance An object containing a tag property that is an HTML audio tag used to load src.
+	 * @param {Object} tag An HTML audio tag used to load src.
 	 * @since 0.4.0
 	 */
-	p.preload = function (src, instance) {
+	p.preload = function (src, tag) {
 		this._audioSources[src] = true;
-		new createjs.HTMLAudioPlugin.Loader(src, instance.tag);
+		new createjs.HTMLAudioPlugin.Loader(src, tag);
 	};
 
 	p.toString = function () {
@@ -4563,8 +4362,8 @@ 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);
+	function SoundInstance(src, startTime, duration, owner) {
+		this._init(src, startTime, duration, owner);
 	}
 
 	var p = SoundInstance.prototype = new createjs.EventDispatcher();
@@ -4575,10 +4374,9 @@ this.createjs = this.createjs || {};
 	p._owner = null;
 	p.loaded = false;
 	p._offset = 0;
-	p._delay = 0;
+	p._startTime = 0;
 	p._volume =  1;
-	// IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
-	try {
+	if (createjs.definePropertySupported) {
 		Object.defineProperty(p, "volume", {
 			get: function() {
 				return this._volume;
@@ -4590,11 +4388,10 @@ this.createjs = this.createjs || {};
 				this._updateVolume();
 			}
 		});
-	} catch (e) {
-		// dispatch message or error?
-	};
+	}
 	p.pan = 0;
 	p._duration = 0;
+	p._audioSpriteStopTime = null;	// HTMLAudioPlugin only
 	p._remainingLoops = 0;
 	p._delayTimeoutId = null;
 	p.tag = null;
@@ -4606,16 +4403,25 @@ this.createjs = this.createjs || {};
 	p._endedHandler = null;
 	p._readyHandler = null;
 	p._stalledHandler = null;
+	p._audioSpriteEndHandler = null;
 	p.loopHandler = null;
 
 // Constructor
-	p._init = function (src, owner) {
+	p._init = function (src, startTime, duration, owner) {
 		this.src = src;
+		this._startTime = startTime || 0;	// convert ms to s as web audio handles everything in seconds
+		if (duration) {
+			this._duration = duration;
+			this._audioSpriteStopTime = (startTime + duration) * 0.001;
+		} else {
+			this._duration = createjs.HTMLAudioPlugin.TagPool.getDuration(this.src);
+		}
 		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.__audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
 		this.loopHandler = createjs.proxy(this.handleSoundLoop, this);
 	};
 
@@ -4628,11 +4434,14 @@ this.createjs = this.createjs || {};
 		var tag = this.tag;
 		if (tag != null) {
 			tag.pause();
+			this.tag.loop = 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);
+			tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this.__audioSpriteEndHandler, false);
+
 			try {
-				tag.currentTime = 0;
+				tag.currentTime = this._startTime;
 			} catch (e) {
 			} // Reset Position
 			createjs.HTMLAudioPlugin.TagPool.setInstance(this.src, tag);
@@ -4640,16 +4449,11 @@ this.createjs = this.createjs || {};
 		}
 
 		clearTimeout(this._delayTimeoutId);
-		if (window.createjs == null) {
-			return;
-		}
 		createjs.Sound._playFinished(this);
 	};
 
 	p._interrupt = function () {
-		if (this.tag == null) {
-			return;
-		}
+		if (this.tag == null) {return;}
 		this.playState = createjs.Sound.PLAY_INTERRUPTED;
 		this._cleanUp();
 		this.paused = this._paused = false;
@@ -4658,14 +4462,11 @@ this.createjs = this.createjs || {};
 
 // Public API
 	p.play = function (interrupt, delay, offset, loop, volume, pan) {
-		this._cleanUp(); //LM: Is this redundant?
+		this._cleanUp();
 		createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan);
 	};
 
 	p._beginPlaying = function (offset, loop, volume, pan) {
-		if (window.createjs == null) {
-			return -1;
-		}
 		var tag = this.tag = createjs.HTMLAudioPlugin.TagPool.getInstance(this.src);
 		if (tag == null) {
 			this.playFailed();
@@ -4677,8 +4478,7 @@ this.createjs = this.createjs || {};
 		// Reset this instance.
 		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._updateVolume();
 		this._remainingLoops = loop;
 
 		if (tag.readyState !== 4) {
@@ -4697,55 +4497,49 @@ this.createjs = this.createjs || {};
 	// 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._cleanUp();  // OJR this will stop playback, we could remove this and let the developer decide how to handle stalled instances
 		this._sendEvent("failed");
 	};
 
 	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
-
 		this.playState = createjs.Sound.PLAY_SUCCEEDED;
 		this.paused = this._paused = false;
 		this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
+		this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false);
 
 		if (this._offset >= this.getDuration()) {
-			this.playFailed();  // OJR: throw error?
+			this.playFailed();
 			return;
-		} else if (this._offset > 0) {
-			this.tag.currentTime = this._offset * 0.001;
 		}
-		if (this._remainingLoops == -1) {
-			this.tag.loop = true;
-		}
-		if(this._remainingLoops != 0) {
-			this.tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false);
-			this.tag.loop = true;
+		this.tag.currentTime = (this._startTime + this._offset) * 0.001;
+
+		if (this._audioSpriteStopTime) {
+			this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
+			this.tag.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this.__audioSpriteEndHandler, false);
+		} else {
+			if (this._remainingLoops == -1) {
+				this.tag.loop = true;
+			} else 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 = 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);
-
 			return true;
 		}
 		return false;
 	};
 
 	p.resume = function () {
-		if (!this._paused || this.tag == null) {
-			return false;
-		}
+		if (!this._paused || this.tag == null) {return false;}
 		this.paused = this._paused = false;
 		this.tag.play();
 		return true;
@@ -4761,7 +4555,6 @@ this.createjs = this.createjs || {};
 
 	p.setMasterVolume = function (value) {
 		this._updateVolume();
-		return true;
 	};
 
 	p.setVolume = function (value) {
@@ -4772,12 +4565,7 @@ this.createjs = this.createjs || {};
 	p._updateVolume = function () {
 		if (this.tag != null) {
 			var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
-			if (newVolume != this.tag.volume) {
-				this.tag.volume = newVolume;
-			}
-			return true;
-		} else {
-			return false;
+			if (newVolume != this.tag.volume) {this.tag.volume = newVolume;}
 		}
 	};
 
@@ -4787,14 +4575,10 @@ this.createjs = this.createjs || {};
 
 	p.setMasterMute = function (isMuted) {
 		this._updateVolume();
-		return true;
 	};
 
 	p.setMute = function (isMuted) {
-		if (isMuted == null || isMuted == undefined) {
-			return false;
-		}
-
+		if (isMuted == null) {return false;}
 		this._muted = isMuted;
 		this._updateVolume();
 		return true;
@@ -4804,7 +4588,7 @@ this.createjs = this.createjs || {};
 		return this._muted;
 	};
 
-	// Can not set pan in HTML
+	// Can not set pan in HTML audio
 	p.setPan = function (value) {
 		return false;
 	};
@@ -4814,10 +4598,8 @@ this.createjs = this.createjs || {};
 	};
 
 	p.getPosition = function () {
-		if (this.tag == null) {
-			return this._offset;
-		}
-		return this.tag.currentTime * 1000;
+		if (this.tag == null) {return this._offset;}
+		return (this.tag.currentTime * 1000) - this._startTime;
 	};
 
 	p.setPosition = function (value) {
@@ -4826,6 +4608,7 @@ this.createjs = this.createjs || {};
 		} else {
 			this.tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this.loopHandler, false);
 			try {
+				value = value + this._startTime;
 				this.tag.currentTime = value * 0.001;
 			} catch (error) { // Out of range
 				return false;
@@ -4835,27 +4618,37 @@ this.createjs = this.createjs || {};
 		return true;
 	};
 
-	p.getDuration = function () {  // NOTE this will always return 0 until sound has been played.
+	p.getDuration = function () {  // NOTE this will always return 0 until sound has been played unless it is set
 		return this._duration;
 	};
 
 	p._handleSoundComplete = function (event) {
 		this._offset = 0;
-
-		if (window.createjs == null) {
-			return;
-		}
 		this.playState = createjs.Sound.PLAY_FINISHED;
 		this._cleanUp();
 		this._sendEvent("complete");
 	};
 
-	// handles looping functionality
+	// NOTE because of the inaccuracies in the timeupdate event (15 - 250ms) and in setting the tag to the desired timed
+	// (up to 300ms), it is strongly recommended not to loop audio sprites with HTML Audio if smooth looping is desired
+	p._handleAudioSpriteLoop = function (event) {
+		if(this.tag.currentTime <= this._audioSpriteStopTime) {return;}
+		this.tag.pause();
+		if(this._remainingLoops == 0) {
+			this._handleSoundComplete(null);
+		} else {
+			this._offset = 0;
+			this._remainingLoops--;
+			this.tag.currentTime = this._startTime * 0.001;
+			if(!this._paused) {this.tag.play();}
+			this._sendEvent("loop");
+		}
+	};
+
 	// 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._remainingLoops--;
 		if(this._remainingLoops == 0) {
 			this.tag.loop = false;
@@ -4865,9 +4658,6 @@ this.createjs = this.createjs || {};
 	};
 
 	p.playFailed = function () {
-		if (window.createjs == null) {
-			return;
-		}
 		this.playState = createjs.Sound.PLAY_FAILED;
 		this._cleanUp();
 		this._sendEvent("failed");
@@ -4946,12 +4736,12 @@ this.createjs = this.createjs || {};
 		this.loadedHandler = createjs.proxy(this.sendLoadedEvent, this);  // we need this bind to be able to remove event listeners
 		this.tag.addEventListener && this.tag.addEventListener("canplaythrough", this.loadedHandler);
 		if(this.tag.onreadystatechange == null) {
-			this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this);  // OJR not 100% sure we need this, just copied from PreloadJS
+			this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this);
 		} else {
 			var f = this.tag.onreadystatechange;
 			this.tag.onreadystatechange = function() {
 				f();
-				this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this);  // OJR not 100% sure we need this, just copied from PreloadJS
+				this.tag.onreadystatechange = createjs.proxy(this.sendLoadedEvent, this);
 			}
 		}
 
@@ -4994,6 +4784,7 @@ this.createjs = this.createjs || {};
 		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
+
 	};
 
 	// used for debugging
@@ -5056,9 +4847,7 @@ this.createjs = this.createjs || {};
 	 */
 	s.remove = function (src) {
 		var channel = s.tags[src];
-		if (channel == null) {
-			return false;
-		}
+		if (channel == null) {return false;}
 		channel.removeAll();
 		delete(s.tags[src]);
 		return true;
@@ -5085,9 +4874,7 @@ this.createjs = this.createjs || {};
 	 */
 	s.getInstance = function (src) {
 		var channel = s.tags[src];
-		if (channel == null) {
-			return null;
-		}
+		if (channel == null) {return null;}
 		return channel.get();
 	};
 
@@ -5101,27 +4888,20 @@ this.createjs = this.createjs || {};
 	 */
 	s.setInstance = function (src, tag) {
 		var channel = s.tags[src];
-		if (channel == null) {
-			return null;
-		}
+		if (channel == null) {return null;}
 		return channel.set(tag);
 	};
 
 	/**
-	 * A function to check if src has changed in the loaded audio tag.
-	 * This is required because PreloadJS appends a basePath to the src before loading.
-	 * Note this is currently only called when a change is detected
-	 * #method checkSrc
-	 * @param src the unaltered src that is used to store the channel.
-	 * @static
-	 * @protected
+	 * Gets the duration of the src audio in milliseconds
+	 * #method getDuration
+	 * @param {String} src The source file used by the audio tag.
+	 * @return {Number} Duration of src in milliseconds
 	 */
-	s.checkSrc = function (src) {
+	s.getDuration= function (src) {
 		var channel = s.tags[src];
-		if (channel == null) {
-			return null;
-		}
-		channel.checkSrcChange();
+		if (channel == null) {return 0;}
+		return channel.getDuration();
 	};
 
 	var p = TagPool.prototype;
@@ -5161,6 +4941,15 @@ this.createjs = this.createjs || {};
 	 */
 	p.tags = null;
 
+	/**
+	 * The duration property of all audio tags, converted to milliseconds, which originally is only available on the
+	 * last tag in the tags array because that is the one that is loaded.
+	 * #property
+	 * @type {Number}
+	 * @protected
+	 */
+	p.duration = 0;
+
 	// constructor
 	p._init = function (src) {
 		this.src = src;
@@ -5183,8 +4972,12 @@ this.createjs = this.createjs || {};
 	 * #method removeAll
 	 */
 	p.removeAll = function () {
-		// This may not be neccessary
+		var tag;
 		while(this.length--) {
+			tag = this.tags[this.length];
+			if(tag.parentNode) {
+				tag.parentNode.removeChild(tag);
+			}
 			delete(this.tags[this.length]);	// NOTE that the audio playback is already stopped by this point
 		}
 		this.src = null;
@@ -5197,14 +4990,10 @@ this.createjs = this.createjs || {};
 	 * @return {HTMLAudioElement} An HTML audio tag.
 	 */
 	p.get = function () {
-		if (this.tags.length == 0) {
-			return null;
-		}
+		if (this.tags.length == 0) {return null;}
 		this.available = this.tags.length;
 		var tag = this.tags.pop();
-		if (tag.parentNode == null) {
-			document.body.appendChild(tag);
-		}
+		if (tag.parentNode == null) {document.body.appendChild(tag);}
 		return tag;
 	};
 
@@ -5215,26 +5004,19 @@ this.createjs = this.createjs || {};
 	 */
 	p.set = function (tag) {
 		var index = createjs.indexOf(this.tags, tag);
-		if (index == -1) {
-			this.tags.push(tag);
-		}
+		if (index == -1) {this.tags.push(tag);}
 		this.available = this.tags.length;
 	};
 
 	/**
-	 * Make sure the src of all other tags is correct after load.
-	 * This is needed because PreloadJS appends a basePath to src before loading.
-	 * #method checkSrcChange
+	 * Gets the duration for the src audio and on first call stores it to this.duration
+	 * #method getDuration
+	 * @return {Number} Duration of the src in milliseconds
 	 */
-	p.checkSrcChange = function () {
-		// the last tag always has the latest src after loading
-		//var i = this.length-1;	// this breaks in Firefox because it is not correctly removing an event listener
-		var i = this.tags.length - 1;
-        if(i == -1) return;  // CodeCombat addition; sometimes errors in IE without this...
-		var newSrc = this.tags[i].src;
-		while(i--) {
-			this.tags[i].src = newSrc;
-		}
+	p.getDuration = function () {
+		// this will work because this will be only be run the first time a sound instance is created and before any tags are taken from the pool
+		if (!this.duration) {this.duration = this.tags[this.tags.length - 1].duration * 1000;}
+		return this.duration;
 	};
 
 	p.toString = function () {
diff --git a/vendor/scripts/tweenjs-NEXT.combined.js b/vendor/scripts/tweenjs-NEXT.combined.js
index 179548b27..851373235 100644
--- a/vendor/scripts/tweenjs-NEXT.combined.js
+++ b/vendor/scripts/tweenjs-NEXT.combined.js
@@ -909,9 +909,10 @@ var p = Tween.prototype = new createjs.EventDispatcher();
 		if (!target.tweenjs_count) { return; }
 		var tweens = Tween._tweens;
 		for (var i=tweens.length-1; i>=0; i--) {
-			if (tweens[i]._target == target) {
-				tweens[i]._paused = true;
-				tweens.splice(i,1);
+			var tween = tweens[i];
+			if (tween._target == target) {
+				tween._paused = true;
+				tweens.splice(i, 1);
 			}
 		}
 		target.tweenjs_count = 0;
@@ -927,7 +928,7 @@ var p = Tween.prototype = new createjs.EventDispatcher();
 		var tweens = Tween._tweens;
 		for (var i= 0, l=tweens.length; i<l; i++) {
 			var tween = tweens[i];
-			tween.paused = true;
+			tween._paused = true;
 			tween.target.tweenjs_count = 0;
 		}
 		tweens.length = 0;
@@ -1376,6 +1377,7 @@ var p = Tween.prototype = new createjs.EventDispatcher();
 	 * @return {Tween} This tween instance (for chaining calls)
 	 */
 	p.setPaused = function(value) {
+		if (this._paused === !!value) { return this; }
 		this._paused = !!value;
 		Tween._register(this, !value);
 		return this;
@@ -2755,6 +2757,6 @@ this.createjs = this.createjs || {};
 	 * @type String
 	 * @static
 	 **/
-	s.buildDate = /*date*/"Thu, 12 Dec 2013 23:37:07 GMT"; // injected by build process
+	s.buildDate = /*date*/"Wed, 02 Apr 2014 20:57:09 GMT"; // injected by build process
 
 })();

From 09a47cc5d822c5ecd582c8edaaa3c9a03c35ec1e Mon Sep 17 00:00:00 2001
From: Scott Erickson <sderickson@gmail.com>
Date: Wed, 7 May 2014 11:11:22 -0700
Subject: [PATCH 2/2] Fixed #951. The parser was removing the shadow shape but
 not the tween of the shadow.

---
 app/lib/sprites/SpriteParser.coffee | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/app/lib/sprites/SpriteParser.coffee b/app/lib/sprites/SpriteParser.coffee
index 3138bb47b..0de48c013 100644
--- a/app/lib/sprites/SpriteParser.coffee
+++ b/app/lib/sprites/SpriteParser.coffee
@@ -379,7 +379,9 @@ module.exports = class SpriteParser
         argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5)
 
         args = eval "[#{argsSource}]"
-        if args[0]?.state?[0]?.t?.search?("shape") is 0 and not _.find(localShapes, bn: args[0].state[0].t)
+        shadowTween = args[0]?.search?('shape') is 0 and not _.find(localShapes, bn: args[0])
+        shadowTween = shadowTween or args[0]?.state?[0]?.t?.search?("shape") is 0 and not _.find(localShapes, bn: args[0].state[0].t)
+        if shadowTween
           console.log "Skipping tween", name, argsSource, args, "from localShapes", localShapes, "presumably because it's a shadow we skipped."
           return
         callExpressions.push {n: name, a: args}