diff --git a/docs/soundjs_docs-NEXT.zip b/docs/soundjs_docs-NEXT.zip
index b7abc0e3..924a11d7 100644
Binary files a/docs/soundjs_docs-NEXT.zip and b/docs/soundjs_docs-NEXT.zip differ
diff --git a/lib/cordovaaudioplugin-NEXT.combined.js b/lib/cordovaaudioplugin-NEXT.combined.js
index efdf8e9c..99d6f87f 100644
--- a/lib/cordovaaudioplugin-NEXT.combined.js
+++ b/lib/cordovaaudioplugin-NEXT.combined.js
@@ -1,3 +1,30 @@
+/*!
+* SoundJS
+* 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.
+*/
//##############################################################################
@@ -538,6 +565,6 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.buildDate = /*=date*/"Tue, 19 May 2015 17:26:59 GMT"; // injected by build process
+ s.buildDate = /*=date*/"Wed, 27 May 2015 18:12:38 GMT"; // injected by build process
})();
\ No newline at end of file
diff --git a/lib/cordovaaudioplugin-NEXT.min.js b/lib/cordovaaudioplugin-NEXT.min.js
index fdb9fb49..0cb6d230 100644
--- a/lib/cordovaaudioplugin-NEXT.min.js
+++ b/lib/cordovaaudioplugin-NEXT.min.js
@@ -2,7 +2,7 @@
* @license SoundJS
* Visit http://createjs.com/ for documentation, updates and examples.
*
-* Copyright (c) 2011-2013 gskinner.com, inc.
+* Copyright (c) 2011-2015 gskinner.com, inc.
*
* Distributed under the terms of the MIT license.
* http://www.opensource.org/licenses/mit-license.html
@@ -14,4 +14,4 @@
* SoundJS FlashAudioPlugin also includes swfobject (http://code.google.com/p/swfobject/)
*/
-this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.SOUND),this._media=null,this._loadTime=0,this._TIMER_FREQUENCY=100}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._media=new Media(this._item.src,null,createjs.proxy(this._mediaErrorHandler,this)),this._media.seekTo(0),this._getMediaDuration()},b.toString=function(){return"[CordovaAudioLoader]"},b._mediaErrorHandler=function(){this._media.release(),this._sendError()},b._getMediaDuration=function(){this._result=1e3*this._media.getDuration(),this._result<0?(this._loadTime+=this._TIMER_FREQUENCY,this._loadTime>this._item.loadTimeout?this.handleEvent({type:"timeout"}):setTimeout(createjs.proxy(this._getMediaDuration,this),this._TIMER_FREQUENCY)):(this._media.release(),this._sendComplete())},createjs.CordovaAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function CordovaAudioSoundInstance(a,b,c,d){this.AbstractSoundInstance_constructor(a,b,c,d),this.playWhenScreenLocked=null,this._playStartTime=null,this._audioSpriteTimeout=null,this._audioSprite=!1,this._audioSpriteEndHandler=createjs.proxy(this._handleAudioSpriteComplete,this),this._mediaPlayFinishedHandler=createjs.proxy(this._handleSoundComplete,this),this._mediaErrorHandler=createjs.proxy(this._handleMediaError,this),this._mediaProgressHandler=createjs.proxy(this._handleMediaProgress,this),this._playbackResource=new Media(a,this._mediaPlayFinishedHandler,this._mediaErrorHandler,this._mediaProgressHandler),c?this._audioSprite=!0:this._setDurationFromSource()}var a=createjs.extend(CordovaAudioSoundInstance,createjs.AbstractSoundInstance);a.setMasterVolume=function(){this._updateVolume()},a.setMasterMute=function(){this._updateVolume()},a.destroy=function(){this.AbstractSoundInstance_destroy(),this._playbackResource.release()},a.getCurrentPosition=function(a,b){this._playbackResource.getCurrentPosition(a,b)},a.toString=function(){return"[CordovaAudioSoundInstance]"},a._handleMediaError=function(){clearTimeout(this.delayTimeoutId),this.playState=createjs.Sound.PLAY_FAILED,this._sendEvent("failed")},a._handleMediaProgress=function(){},a._handleAudioSpriteComplete=function(){this._playbackResource.pause(),this._handleSoundComplete()},a._handleCleanUp=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause()},a._handleSoundReady=function(){this._playbackResource.seekTo(this._startTime+this._position),this._audioSprite&&(this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position)),this._playbackResource.play({playAudioWhenScreenIsLocked:this.playWhenScreenLocked}),this._playStartTime=Date.now()},a._pause=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause(),this._playStartTime&&(this._position=Date.now()-this._playStartTime,this._playStartTime=null),this._playbackResource.getCurrentPosition(createjs.proxy(this._updatePausePos,this))},a._updatePausePos=function(a){this._position=1e3*a-this._startTime,this._playStartTime&&(this._playStartTime=Date.now())},a._resume=function(){this._audioSprite&&(this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position)),this._playbackResource.play({playAudioWhenScreenIsLocked:this.playWhenScreenLocked}),this._playStartTime=Date.now()},a._handleStop=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause(),this._playbackResource.seekTo(this._startTime),this._playStartTime&&(this._position=0,this._playStartTime=null)},a._updateVolume=function(){var a=this._muted||createjs.Sound._masterMute?0:this._volume*createjs.Sound._masterVolume;this._playbackResource.setVolume(a)},a._calculateCurrentPosition=function(){return this._playStartTime&&(this._position=Date.now()-this._playStartTime+this._position,this._playStartTime=Date.now()),this._position},a._updatePosition=function(){this._playbackResource.seekTo(this._startTime+this._position),this._playStartTime=Date.now(),this._audioSprite&&(clearTimeout(this._audioSpriteTimeout),this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position))},a._handleLoop=function(){this._handleSoundReady()},a._updateStartTime=function(){this._audioSprite=!0,this.playState==createjs.Sound.PLAY_SUCCEEDED},a._updateDuration=function(){this._audioSprite,this.playState==createjs.Sound.PLAY_SUCCEEDED&&(clearTimeout(this._audioSpriteTimeout),this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this.position))},a._setDurationFromSource=function(){this._duration=createjs.Sound.activePlugin.getSrcDuration(this.src)},createjs.CordovaAudioSoundInstance=createjs.promote(CordovaAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function CordovaAudioPlugin(){this.AbstractPlugin_constructor(),this._capabilities=b._capabilities,this._loaderClass=createjs.CordovaAudioLoader,this._soundInstanceClass=createjs.CordovaAudioSoundInstance,this._srcDurationHash={}}var a=createjs.extend(CordovaAudioPlugin,createjs.AbstractPlugin),b=CordovaAudioPlugin;b.playWhenScreenLocked=!1,b._capabilities=null,b.isSupported=function(){return b._generateCapabilities(),null!=b._capabilities},b._generateCapabilities=function(){if(null==b._capabilities&&(window.cordova||window.PhoneGap||window.phonegap)&&window.Media){var a=document.createElement("audio");if(null==a.canPlayType)return null;b._capabilities={panning:!1,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},a.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.playWhenScreenLocked=this.playWhenScreenLocked,d},a.toString=function(){return"[CordovaAudioPlugin]"},a.setVolume=a.getVolume=a.setMute=null,a.getSrcDuration=function(a){return this._srcDurationHash[a]},a._handlePreloadComplete=function(a){var b=a.target.getItem().src;this._srcDurationHash[b]=a.result,this._audioSources[b]=a.result},a.removeSound=function(a){delete this._srcDurationHash[a],this.AbstractPlugin_removeSound(a)},createjs.CordovaAudioPlugin=createjs.promote(CordovaAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){var a=createjs.CordovaAudioPlugin=createjs.CordovaAudioPlugin||{};a.version="NEXT",a.buildDate="Tue, 19 May 2015 17:26:59 GMT"}();
\ No newline at end of file
+this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.SOUND),this._media=null,this._loadTime=0,this._TIMER_FREQUENCY=100}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._media=new Media(this._item.src,null,createjs.proxy(this._mediaErrorHandler,this)),this._media.seekTo(0),this._getMediaDuration()},b.toString=function(){return"[CordovaAudioLoader]"},b._mediaErrorHandler=function(){this._media.release(),this._sendError()},b._getMediaDuration=function(){this._result=1e3*this._media.getDuration(),this._result<0?(this._loadTime+=this._TIMER_FREQUENCY,this._loadTime>this._item.loadTimeout?this.handleEvent({type:"timeout"}):setTimeout(createjs.proxy(this._getMediaDuration,this),this._TIMER_FREQUENCY)):(this._media.release(),this._sendComplete())},createjs.CordovaAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function CordovaAudioSoundInstance(a,b,c,d){this.AbstractSoundInstance_constructor(a,b,c,d),this.playWhenScreenLocked=null,this._playStartTime=null,this._audioSpriteTimeout=null,this._audioSprite=!1,this._audioSpriteEndHandler=createjs.proxy(this._handleAudioSpriteComplete,this),this._mediaPlayFinishedHandler=createjs.proxy(this._handleSoundComplete,this),this._mediaErrorHandler=createjs.proxy(this._handleMediaError,this),this._mediaProgressHandler=createjs.proxy(this._handleMediaProgress,this),this._playbackResource=new Media(a,this._mediaPlayFinishedHandler,this._mediaErrorHandler,this._mediaProgressHandler),c?this._audioSprite=!0:this._setDurationFromSource()}var a=createjs.extend(CordovaAudioSoundInstance,createjs.AbstractSoundInstance);a.setMasterVolume=function(){this._updateVolume()},a.setMasterMute=function(){this._updateVolume()},a.destroy=function(){this.AbstractSoundInstance_destroy(),this._playbackResource.release()},a.getCurrentPosition=function(a,b){this._playbackResource.getCurrentPosition(a,b)},a.toString=function(){return"[CordovaAudioSoundInstance]"},a._handleMediaError=function(){clearTimeout(this.delayTimeoutId),this.playState=createjs.Sound.PLAY_FAILED,this._sendEvent("failed")},a._handleMediaProgress=function(){},a._handleAudioSpriteComplete=function(){this._playbackResource.pause(),this._handleSoundComplete()},a._handleCleanUp=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause()},a._handleSoundReady=function(){this._playbackResource.seekTo(this._startTime+this._position),this._audioSprite&&(this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position)),this._playbackResource.play({playAudioWhenScreenIsLocked:this.playWhenScreenLocked}),this._playStartTime=Date.now()},a._pause=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause(),this._playStartTime&&(this._position=Date.now()-this._playStartTime,this._playStartTime=null),this._playbackResource.getCurrentPosition(createjs.proxy(this._updatePausePos,this))},a._updatePausePos=function(a){this._position=1e3*a-this._startTime,this._playStartTime&&(this._playStartTime=Date.now())},a._resume=function(){this._audioSprite&&(this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position)),this._playbackResource.play({playAudioWhenScreenIsLocked:this.playWhenScreenLocked}),this._playStartTime=Date.now()},a._handleStop=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause(),this._playbackResource.seekTo(this._startTime),this._playStartTime&&(this._position=0,this._playStartTime=null)},a._updateVolume=function(){var a=this._muted||createjs.Sound._masterMute?0:this._volume*createjs.Sound._masterVolume;this._playbackResource.setVolume(a)},a._calculateCurrentPosition=function(){return this._playStartTime&&(this._position=Date.now()-this._playStartTime+this._position,this._playStartTime=Date.now()),this._position},a._updatePosition=function(){this._playbackResource.seekTo(this._startTime+this._position),this._playStartTime=Date.now(),this._audioSprite&&(clearTimeout(this._audioSpriteTimeout),this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position))},a._handleLoop=function(){this._handleSoundReady()},a._updateStartTime=function(){this._audioSprite=!0,this.playState==createjs.Sound.PLAY_SUCCEEDED},a._updateDuration=function(){this._audioSprite,this.playState==createjs.Sound.PLAY_SUCCEEDED&&(clearTimeout(this._audioSpriteTimeout),this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this.position))},a._setDurationFromSource=function(){this._duration=createjs.Sound.activePlugin.getSrcDuration(this.src)},createjs.CordovaAudioSoundInstance=createjs.promote(CordovaAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function CordovaAudioPlugin(){this.AbstractPlugin_constructor(),this._capabilities=b._capabilities,this._loaderClass=createjs.CordovaAudioLoader,this._soundInstanceClass=createjs.CordovaAudioSoundInstance,this._srcDurationHash={}}var a=createjs.extend(CordovaAudioPlugin,createjs.AbstractPlugin),b=CordovaAudioPlugin;b.playWhenScreenLocked=!1,b._capabilities=null,b.isSupported=function(){return b._generateCapabilities(),null!=b._capabilities},b._generateCapabilities=function(){if(null==b._capabilities&&(window.cordova||window.PhoneGap||window.phonegap)&&window.Media){var a=document.createElement("audio");if(null==a.canPlayType)return null;b._capabilities={panning:!1,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},a.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.playWhenScreenLocked=this.playWhenScreenLocked,d},a.toString=function(){return"[CordovaAudioPlugin]"},a.setVolume=a.getVolume=a.setMute=null,a.getSrcDuration=function(a){return this._srcDurationHash[a]},a._handlePreloadComplete=function(a){var b=a.target.getItem().src;this._srcDurationHash[b]=a.result,this._audioSources[b]=a.result},a.removeSound=function(a){delete this._srcDurationHash[a],this.AbstractPlugin_removeSound(a)},createjs.CordovaAudioPlugin=createjs.promote(CordovaAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){var a=createjs.CordovaAudioPlugin=createjs.CordovaAudioPlugin||{};a.version="NEXT",a.buildDate="Wed, 27 May 2015 18:12:38 GMT"}();
\ No newline at end of file
diff --git a/lib/flashaudioplugin-NEXT.combined.js b/lib/flashaudioplugin-NEXT.combined.js
index 48bd7ada..34dbf1e3 100644
--- a/lib/flashaudioplugin-NEXT.combined.js
+++ b/lib/flashaudioplugin-NEXT.combined.js
@@ -1,3 +1,30 @@
+/*!
+* SoundJS
+* 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.
+*/
//##############################################################################
@@ -1572,6 +1599,6 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.buildDate = /*=date*/"Tue, 19 May 2015 17:26:59 GMT"; // injected by build process
+ s.buildDate = /*=date*/"Wed, 27 May 2015 18:12:38 GMT"; // injected by build process
})();
\ No newline at end of file
diff --git a/lib/flashaudioplugin-NEXT.min.js b/lib/flashaudioplugin-NEXT.min.js
index ae130998..2817f596 100644
--- a/lib/flashaudioplugin-NEXT.min.js
+++ b/lib/flashaudioplugin-NEXT.min.js
@@ -2,7 +2,7 @@
* @license SoundJS
* Visit http://createjs.com/ for documentation, updates and examples.
*
-* Copyright (c) 2011-2013 gskinner.com, inc.
+* Copyright (c) 2011-2015 gskinner.com, inc.
*
* Distributed under the terms of the MIT license.
* http://www.opensource.org/licenses/mit-license.html
@@ -17,4 +17,4 @@
/*! SWFObject v2.2
is released under the MIT License
*/
-var swfobject=function(){function a(){if(!R){try{var a=K.getElementsByTagName("body")[0].appendChild(q("span"));a.parentNode.removeChild(a)}catch(b){return}R=!0;for(var c=N.length,d=0;c>d;d++)N[d]()}}function b(a){R?a():N[N.length]=a}function c(a){if(typeof J.addEventListener!=C)J.addEventListener("load",a,!1);else if(typeof K.addEventListener!=C)K.addEventListener("load",a,!1);else if(typeof J.attachEvent!=C)r(J,"onload",a);else if("function"==typeof J.onload){var b=J.onload;J.onload=function(){b(),a()}}else J.onload=a}function d(){M?e():f()}function e(){var a=K.getElementsByTagName("body")[0],b=q(D);b.setAttribute("type",G);var c=a.appendChild(b);if(c){var d=0;!function(){if(typeof c.GetVariable!=C){var e=c.GetVariable("$version");e&&(e=e.split(" ")[1].split(","),U.pv=[parseInt(e[0],10),parseInt(e[1],10),parseInt(e[2],10)])}else if(10>d)return d++,void setTimeout(arguments.callee,10);a.removeChild(b),c=null,f()}()}else f()}function f(){var a=O.length;if(a>0)for(var b=0;a>b;b++){var c=O[b].id,d=O[b].callbackFn,e={success:!1,id:c};if(U.pv[0]>0){var f=p(c);if(f)if(!s(O[b].swfVersion)||U.wk&&U.wk<312)if(O[b].expressInstall&&h()){var k={};k.data=O[b].expressInstall,k.width=f.getAttribute("width")||"0",k.height=f.getAttribute("height")||"0",f.getAttribute("class")&&(k.styleclass=f.getAttribute("class")),f.getAttribute("align")&&(k.align=f.getAttribute("align"));for(var l={},m=f.getElementsByTagName("param"),n=m.length,o=0;n>o;o++)"movie"!=m[o].getAttribute("name").toLowerCase()&&(l[m[o].getAttribute("name")]=m[o].getAttribute("value"));i(k,l,c,d)}else j(f),d&&d(e);else u(c,!0),d&&(e.success=!0,e.ref=g(c),d(e))}else if(u(c,!0),d){var q=g(c);q&&typeof q.SetVariable!=C&&(e.success=!0,e.ref=q),d(e)}}}function g(a){var b=null,c=p(a);if(c&&"OBJECT"==c.nodeName)if(typeof c.SetVariable!=C)b=c;else{var d=c.getElementsByTagName(D)[0];d&&(b=d)}return b}function h(){return!S&&s("6.0.65")&&(U.win||U.mac)&&!(U.wk&&U.wk<312)}function i(a,b,c,d){S=!0,y=d||null,z={success:!1,id:c};var e=p(c);if(e){"OBJECT"==e.nodeName?(w=k(e),x=null):(w=e,x=c),a.id=H,(typeof a.width==C||!/%$/.test(a.width)&&parseInt(a.width,10)<310)&&(a.width="310"),(typeof a.height==C||!/%$/.test(a.height)&&parseInt(a.height,10)<137)&&(a.height="137"),K.title=K.title.slice(0,47)+" - Flash Player Installation";var f=U.ie&&U.win?"ActiveX":"PlugIn",g="MMredirectURL="+encodeURI(window.location).toString().replace(/&/g,"%26")+"&MMplayerType="+f+"&MMdoctitle="+K.title;if(typeof b.flashvars!=C?b.flashvars+="&"+g:b.flashvars=g,U.ie&&U.win&&4!=e.readyState){var h=q("div");c+="SWFObjectNew",h.setAttribute("id",c),e.parentNode.insertBefore(h,e),e.style.display="none",function(){4==e.readyState?e.parentNode.removeChild(e):setTimeout(arguments.callee,10)}()}l(a,b,c)}}function j(a){if(U.ie&&U.win&&4!=a.readyState){var b=q("div");a.parentNode.insertBefore(b,a),b.parentNode.replaceChild(k(a),b),a.style.display="none",function(){4==a.readyState?a.parentNode.removeChild(a):setTimeout(arguments.callee,10)}()}else a.parentNode.replaceChild(k(a),a)}function k(a){var b=q("div");if(U.win&&U.ie)b.innerHTML=a.innerHTML;else{var c=a.getElementsByTagName(D)[0];if(c){var d=c.childNodes;if(d)for(var e=d.length,f=0;e>f;f++)1==d[f].nodeType&&"PARAM"==d[f].nodeName||8==d[f].nodeType||b.appendChild(d[f].cloneNode(!0))}}return b}function l(a,b,c){var d,e=p(c);if(U.wk&&U.wk<312)return d;if(e)if(typeof a.id==C&&(a.id=c),U.ie&&U.win){var f="";for(var g in a)a[g]!=Object.prototype[g]&&("data"==g.toLowerCase()?b.movie=a[g]:"styleclass"==g.toLowerCase()?f+=' class="'+a[g]+'"':"classid"!=g.toLowerCase()&&(f+=" "+g+'="'+a[g]+'"'));var h="";for(var i in b)b[i]!=Object.prototype[i]&&(h+='');e.outerHTML='",P[P.length]=a.id,d=p(a.id)}else{var j=q(D);j.setAttribute("type",G);for(var k in a)a[k]!=Object.prototype[k]&&("styleclass"==k.toLowerCase()?j.setAttribute("class",a[k]):"classid"!=k.toLowerCase()&&j.setAttribute(k,a[k]));for(var l in b)b[l]!=Object.prototype[l]&&"movie"!=l.toLowerCase()&&m(j,l,b[l]);e.parentNode.replaceChild(j,e),d=j}return d}function m(a,b,c){var d=q("param");d.setAttribute("name",b),d.setAttribute("value",c),a.appendChild(d)}function n(a){var b=p(a);b&&"OBJECT"==b.nodeName&&(U.ie&&U.win?(b.style.display="none",function(){4==b.readyState?o(a):setTimeout(arguments.callee,10)}()):b.parentNode.removeChild(b))}function o(a){var b=p(a);if(b){for(var c in b)"function"==typeof b[c]&&(b[c]=null);b.parentNode.removeChild(b)}}function p(a){var b=null;try{b=K.getElementById(a)}catch(c){}return b}function q(a){return K.createElement(a)}function r(a,b,c){a.attachEvent(b,c),Q[Q.length]=[a,b,c]}function s(a){var b=U.pv,c=a.split(".");return c[0]=parseInt(c[0],10),c[1]=parseInt(c[1],10)||0,c[2]=parseInt(c[2],10)||0,b[0]>c[0]||b[0]==c[0]&&b[1]>c[1]||b[0]==c[0]&&b[1]==c[1]&&b[2]>=c[2]?!0:!1}function t(a,b,c,d){if(!U.ie||!U.mac){var e=K.getElementsByTagName("head")[0];if(e){var f=c&&"string"==typeof c?c:"screen";if(d&&(A=null,B=null),!A||B!=f){var g=q("style");g.setAttribute("type","text/css"),g.setAttribute("media",f),A=e.appendChild(g),U.ie&&U.win&&typeof K.styleSheets!=C&&K.styleSheets.length>0&&(A=K.styleSheets[K.styleSheets.length-1]),B=f}U.ie&&U.win?A&&typeof A.addRule==D&&A.addRule(a,b):A&&typeof K.createTextNode!=C&&A.appendChild(K.createTextNode(a+" {"+b+"}"))}}}function u(a,b){if(T){var c=b?"visible":"hidden";R&&p(a)?p(a).style.visibility=c:t("#"+a,"visibility:"+c)}}function v(a){var b=/[\\\"<>\.;]/,c=null!=b.exec(a);return c&&typeof encodeURIComponent!=C?encodeURIComponent(a):a}{var w,x,y,z,A,B,C="undefined",D="object",E="Shockwave Flash",F="ShockwaveFlash.ShockwaveFlash",G="application/x-shockwave-flash",H="SWFObjectExprInst",I="onreadystatechange",J=window,K=document,L=navigator,M=!1,N=[d],O=[],P=[],Q=[],R=!1,S=!1,T=!0,U=function(){var a=typeof K.getElementById!=C&&typeof K.getElementsByTagName!=C&&typeof K.createElement!=C,b=L.userAgent.toLowerCase(),c=L.platform.toLowerCase(),d=/win/.test(c?c:b),e=/mac/.test(c?c:b),f=/webkit/.test(b)?parseFloat(b.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,g=!1,h=[0,0,0],i=null;if(typeof L.plugins!=C&&typeof L.plugins[E]==D)i=L.plugins[E].description,!i||typeof L.mimeTypes!=C&&L.mimeTypes[G]&&!L.mimeTypes[G].enabledPlugin||(M=!0,g=!1,i=i.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),h[0]=parseInt(i.replace(/^(.*)\..*$/,"$1"),10),h[1]=parseInt(i.replace(/^.*\.(.*)\s.*$/,"$1"),10),h[2]=/[a-zA-Z]/.test(i)?parseInt(i.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0);else if(typeof J.ActiveXObject!=C)try{var j=new ActiveXObject(F);j&&(i=j.GetVariable("$version"),i&&(g=!0,i=i.split(" ")[1].split(","),h=[parseInt(i[0],10),parseInt(i[1],10),parseInt(i[2],10)]))}catch(k){}return{w3:a,pv:h,wk:f,ie:g,win:d,mac:e}}();!function(){U.w3&&((typeof K.readyState!=C&&"complete"==K.readyState||typeof K.readyState==C&&(K.getElementsByTagName("body")[0]||K.body))&&a(),R||(typeof K.addEventListener!=C&&K.addEventListener("DOMContentLoaded",a,!1),U.ie&&U.win&&(K.attachEvent(I,function(){"complete"==K.readyState&&(K.detachEvent(I,arguments.callee),a())}),J==top&&!function(){if(!R){try{K.documentElement.doScroll("left")}catch(b){return void setTimeout(arguments.callee,0)}a()}}()),U.wk&&!function(){return R?void 0:/loaded|complete/.test(K.readyState)?void a():void setTimeout(arguments.callee,0)}(),c(a)))}(),function(){U.ie&&U.win&&window.attachEvent("onunload",function(){for(var a=Q.length,b=0;a>b;b++)Q[b][0].detachEvent(Q[b][1],Q[b][2]);for(var c=P.length,d=0;c>d;d++)n(P[d]);for(var e in U)U[e]=null;U=null;for(var f in swfobject)swfobject[f]=null;swfobject=null})}()}return{registerObject:function(a,b,c,d){if(U.w3&&a&&b){var e={};e.id=a,e.swfVersion=b,e.expressInstall=c,e.callbackFn=d,O[O.length]=e,u(a,!1)}else d&&d({success:!1,id:a})},getObjectById:function(a){return U.w3?g(a):void 0},embedSWF:function(a,c,d,e,f,g,j,k,m,n){var o={success:!1,id:c};U.w3&&!(U.wk&&U.wk<312)&&a&&c&&d&&e&&f?(u(c,!1),b(function(){d+="",e+="";var b={};if(m&&typeof m===D)for(var p in m)b[p]=m[p];b.data=a,b.width=d,b.height=e;var q={};if(k&&typeof k===D)for(var r in k)q[r]=k[r];if(j&&typeof j===D)for(var t in j)typeof q.flashvars!=C?q.flashvars+="&"+t+"="+j[t]:q.flashvars=t+"="+j[t];if(s(f)){var v=l(b,q,c);b.id==c&&u(c,!0),o.success=!0,o.ref=v}else{if(g&&h())return b.data=g,void i(b,q,c,n);u(c,!0)}n&&n(o)})):n&&n(o)},switchOffAutoHideShow:function(){T=!1},ua:U,getFlashPlayerVersion:function(){return{major:U.pv[0],minor:U.pv[1],release:U.pv[2]}},hasFlashPlayerVersion:s,createSWF:function(a,b,c){return U.w3?l(a,b,c):void 0},showExpressInstall:function(a,b,c,d){U.w3&&h()&&i(a,b,c,d)},removeSWF:function(a){U.w3&&n(a)},createCSS:function(a,b,c,d){U.w3&&t(a,b,c,d)},addDomLoadEvent:b,addLoadEvent:c,getQueryParamValue:function(a){var b=K.location.search||K.location.hash;if(b){if(/\?/.test(b)&&(b=b.split("?")[1]),null==a)return v(b);for(var c=b.split("&"),d=0;de;e++)d.push(arguments[e]);try{0==d.length?c[b]():c[b].apply(c,d)}catch(g){}}},a.handlePreloadEvent=function(a,b){var c=this._flashPreloadInstances[a];if(null!=c){for(var d=[],e=2,f=arguments.length;f>e;e++)d.push(arguments[e]);try{0==d.length?c[b]():c[b].apply(c,d)}catch(g){}}},a.handleEvent=function(a){switch(a){case"ready":this._handleFlashReady()}},a.handleErrorEvent=function(){},createjs.FlashAudioPlugin=createjs.promote(FlashAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){var a=createjs.FlashAudioPlugin=createjs.FlashAudioPlugin||{};a.version="NEXT",a.buildDate="Tue, 19 May 2015 17:26:59 GMT"}();
\ No newline at end of file
+var swfobject=function(){function a(){if(!R){try{var a=K.getElementsByTagName("body")[0].appendChild(q("span"));a.parentNode.removeChild(a)}catch(b){return}R=!0;for(var c=N.length,d=0;c>d;d++)N[d]()}}function b(a){R?a():N[N.length]=a}function c(a){if(typeof J.addEventListener!=C)J.addEventListener("load",a,!1);else if(typeof K.addEventListener!=C)K.addEventListener("load",a,!1);else if(typeof J.attachEvent!=C)r(J,"onload",a);else if("function"==typeof J.onload){var b=J.onload;J.onload=function(){b(),a()}}else J.onload=a}function d(){M?e():f()}function e(){var a=K.getElementsByTagName("body")[0],b=q(D);b.setAttribute("type",G);var c=a.appendChild(b);if(c){var d=0;!function(){if(typeof c.GetVariable!=C){var e=c.GetVariable("$version");e&&(e=e.split(" ")[1].split(","),U.pv=[parseInt(e[0],10),parseInt(e[1],10),parseInt(e[2],10)])}else if(10>d)return d++,void setTimeout(arguments.callee,10);a.removeChild(b),c=null,f()}()}else f()}function f(){var a=O.length;if(a>0)for(var b=0;a>b;b++){var c=O[b].id,d=O[b].callbackFn,e={success:!1,id:c};if(U.pv[0]>0){var f=p(c);if(f)if(!s(O[b].swfVersion)||U.wk&&U.wk<312)if(O[b].expressInstall&&h()){var k={};k.data=O[b].expressInstall,k.width=f.getAttribute("width")||"0",k.height=f.getAttribute("height")||"0",f.getAttribute("class")&&(k.styleclass=f.getAttribute("class")),f.getAttribute("align")&&(k.align=f.getAttribute("align"));for(var l={},m=f.getElementsByTagName("param"),n=m.length,o=0;n>o;o++)"movie"!=m[o].getAttribute("name").toLowerCase()&&(l[m[o].getAttribute("name")]=m[o].getAttribute("value"));i(k,l,c,d)}else j(f),d&&d(e);else u(c,!0),d&&(e.success=!0,e.ref=g(c),d(e))}else if(u(c,!0),d){var q=g(c);q&&typeof q.SetVariable!=C&&(e.success=!0,e.ref=q),d(e)}}}function g(a){var b=null,c=p(a);if(c&&"OBJECT"==c.nodeName)if(typeof c.SetVariable!=C)b=c;else{var d=c.getElementsByTagName(D)[0];d&&(b=d)}return b}function h(){return!S&&s("6.0.65")&&(U.win||U.mac)&&!(U.wk&&U.wk<312)}function i(a,b,c,d){S=!0,y=d||null,z={success:!1,id:c};var e=p(c);if(e){"OBJECT"==e.nodeName?(w=k(e),x=null):(w=e,x=c),a.id=H,(typeof a.width==C||!/%$/.test(a.width)&&parseInt(a.width,10)<310)&&(a.width="310"),(typeof a.height==C||!/%$/.test(a.height)&&parseInt(a.height,10)<137)&&(a.height="137"),K.title=K.title.slice(0,47)+" - Flash Player Installation";var f=U.ie&&U.win?"ActiveX":"PlugIn",g="MMredirectURL="+encodeURI(window.location).toString().replace(/&/g,"%26")+"&MMplayerType="+f+"&MMdoctitle="+K.title;if(typeof b.flashvars!=C?b.flashvars+="&"+g:b.flashvars=g,U.ie&&U.win&&4!=e.readyState){var h=q("div");c+="SWFObjectNew",h.setAttribute("id",c),e.parentNode.insertBefore(h,e),e.style.display="none",function(){4==e.readyState?e.parentNode.removeChild(e):setTimeout(arguments.callee,10)}()}l(a,b,c)}}function j(a){if(U.ie&&U.win&&4!=a.readyState){var b=q("div");a.parentNode.insertBefore(b,a),b.parentNode.replaceChild(k(a),b),a.style.display="none",function(){4==a.readyState?a.parentNode.removeChild(a):setTimeout(arguments.callee,10)}()}else a.parentNode.replaceChild(k(a),a)}function k(a){var b=q("div");if(U.win&&U.ie)b.innerHTML=a.innerHTML;else{var c=a.getElementsByTagName(D)[0];if(c){var d=c.childNodes;if(d)for(var e=d.length,f=0;e>f;f++)1==d[f].nodeType&&"PARAM"==d[f].nodeName||8==d[f].nodeType||b.appendChild(d[f].cloneNode(!0))}}return b}function l(a,b,c){var d,e=p(c);if(U.wk&&U.wk<312)return d;if(e)if(typeof a.id==C&&(a.id=c),U.ie&&U.win){var f="";for(var g in a)a[g]!=Object.prototype[g]&&("data"==g.toLowerCase()?b.movie=a[g]:"styleclass"==g.toLowerCase()?f+=' class="'+a[g]+'"':"classid"!=g.toLowerCase()&&(f+=" "+g+'="'+a[g]+'"'));var h="";for(var i in b)b[i]!=Object.prototype[i]&&(h+='');e.outerHTML='",P[P.length]=a.id,d=p(a.id)}else{var j=q(D);j.setAttribute("type",G);for(var k in a)a[k]!=Object.prototype[k]&&("styleclass"==k.toLowerCase()?j.setAttribute("class",a[k]):"classid"!=k.toLowerCase()&&j.setAttribute(k,a[k]));for(var l in b)b[l]!=Object.prototype[l]&&"movie"!=l.toLowerCase()&&m(j,l,b[l]);e.parentNode.replaceChild(j,e),d=j}return d}function m(a,b,c){var d=q("param");d.setAttribute("name",b),d.setAttribute("value",c),a.appendChild(d)}function n(a){var b=p(a);b&&"OBJECT"==b.nodeName&&(U.ie&&U.win?(b.style.display="none",function(){4==b.readyState?o(a):setTimeout(arguments.callee,10)}()):b.parentNode.removeChild(b))}function o(a){var b=p(a);if(b){for(var c in b)"function"==typeof b[c]&&(b[c]=null);b.parentNode.removeChild(b)}}function p(a){var b=null;try{b=K.getElementById(a)}catch(c){}return b}function q(a){return K.createElement(a)}function r(a,b,c){a.attachEvent(b,c),Q[Q.length]=[a,b,c]}function s(a){var b=U.pv,c=a.split(".");return c[0]=parseInt(c[0],10),c[1]=parseInt(c[1],10)||0,c[2]=parseInt(c[2],10)||0,b[0]>c[0]||b[0]==c[0]&&b[1]>c[1]||b[0]==c[0]&&b[1]==c[1]&&b[2]>=c[2]?!0:!1}function t(a,b,c,d){if(!U.ie||!U.mac){var e=K.getElementsByTagName("head")[0];if(e){var f=c&&"string"==typeof c?c:"screen";if(d&&(A=null,B=null),!A||B!=f){var g=q("style");g.setAttribute("type","text/css"),g.setAttribute("media",f),A=e.appendChild(g),U.ie&&U.win&&typeof K.styleSheets!=C&&K.styleSheets.length>0&&(A=K.styleSheets[K.styleSheets.length-1]),B=f}U.ie&&U.win?A&&typeof A.addRule==D&&A.addRule(a,b):A&&typeof K.createTextNode!=C&&A.appendChild(K.createTextNode(a+" {"+b+"}"))}}}function u(a,b){if(T){var c=b?"visible":"hidden";R&&p(a)?p(a).style.visibility=c:t("#"+a,"visibility:"+c)}}function v(a){var b=/[\\\"<>\.;]/,c=null!=b.exec(a);return c&&typeof encodeURIComponent!=C?encodeURIComponent(a):a}{var w,x,y,z,A,B,C="undefined",D="object",E="Shockwave Flash",F="ShockwaveFlash.ShockwaveFlash",G="application/x-shockwave-flash",H="SWFObjectExprInst",I="onreadystatechange",J=window,K=document,L=navigator,M=!1,N=[d],O=[],P=[],Q=[],R=!1,S=!1,T=!0,U=function(){var a=typeof K.getElementById!=C&&typeof K.getElementsByTagName!=C&&typeof K.createElement!=C,b=L.userAgent.toLowerCase(),c=L.platform.toLowerCase(),d=/win/.test(c?c:b),e=/mac/.test(c?c:b),f=/webkit/.test(b)?parseFloat(b.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,g=!1,h=[0,0,0],i=null;if(typeof L.plugins!=C&&typeof L.plugins[E]==D)i=L.plugins[E].description,!i||typeof L.mimeTypes!=C&&L.mimeTypes[G]&&!L.mimeTypes[G].enabledPlugin||(M=!0,g=!1,i=i.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),h[0]=parseInt(i.replace(/^(.*)\..*$/,"$1"),10),h[1]=parseInt(i.replace(/^.*\.(.*)\s.*$/,"$1"),10),h[2]=/[a-zA-Z]/.test(i)?parseInt(i.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0);else if(typeof J.ActiveXObject!=C)try{var j=new ActiveXObject(F);j&&(i=j.GetVariable("$version"),i&&(g=!0,i=i.split(" ")[1].split(","),h=[parseInt(i[0],10),parseInt(i[1],10),parseInt(i[2],10)]))}catch(k){}return{w3:a,pv:h,wk:f,ie:g,win:d,mac:e}}();!function(){U.w3&&((typeof K.readyState!=C&&"complete"==K.readyState||typeof K.readyState==C&&(K.getElementsByTagName("body")[0]||K.body))&&a(),R||(typeof K.addEventListener!=C&&K.addEventListener("DOMContentLoaded",a,!1),U.ie&&U.win&&(K.attachEvent(I,function(){"complete"==K.readyState&&(K.detachEvent(I,arguments.callee),a())}),J==top&&!function(){if(!R){try{K.documentElement.doScroll("left")}catch(b){return void setTimeout(arguments.callee,0)}a()}}()),U.wk&&!function(){return R?void 0:/loaded|complete/.test(K.readyState)?void a():void setTimeout(arguments.callee,0)}(),c(a)))}(),function(){U.ie&&U.win&&window.attachEvent("onunload",function(){for(var a=Q.length,b=0;a>b;b++)Q[b][0].detachEvent(Q[b][1],Q[b][2]);for(var c=P.length,d=0;c>d;d++)n(P[d]);for(var e in U)U[e]=null;U=null;for(var f in swfobject)swfobject[f]=null;swfobject=null})}()}return{registerObject:function(a,b,c,d){if(U.w3&&a&&b){var e={};e.id=a,e.swfVersion=b,e.expressInstall=c,e.callbackFn=d,O[O.length]=e,u(a,!1)}else d&&d({success:!1,id:a})},getObjectById:function(a){return U.w3?g(a):void 0},embedSWF:function(a,c,d,e,f,g,j,k,m,n){var o={success:!1,id:c};U.w3&&!(U.wk&&U.wk<312)&&a&&c&&d&&e&&f?(u(c,!1),b(function(){d+="",e+="";var b={};if(m&&typeof m===D)for(var p in m)b[p]=m[p];b.data=a,b.width=d,b.height=e;var q={};if(k&&typeof k===D)for(var r in k)q[r]=k[r];if(j&&typeof j===D)for(var t in j)typeof q.flashvars!=C?q.flashvars+="&"+t+"="+j[t]:q.flashvars=t+"="+j[t];if(s(f)){var v=l(b,q,c);b.id==c&&u(c,!0),o.success=!0,o.ref=v}else{if(g&&h())return b.data=g,void i(b,q,c,n);u(c,!0)}n&&n(o)})):n&&n(o)},switchOffAutoHideShow:function(){T=!1},ua:U,getFlashPlayerVersion:function(){return{major:U.pv[0],minor:U.pv[1],release:U.pv[2]}},hasFlashPlayerVersion:s,createSWF:function(a,b,c){return U.w3?l(a,b,c):void 0},showExpressInstall:function(a,b,c,d){U.w3&&h()&&i(a,b,c,d)},removeSWF:function(a){U.w3&&n(a)},createCSS:function(a,b,c,d){U.w3&&t(a,b,c,d)},addDomLoadEvent:b,addLoadEvent:c,getQueryParamValue:function(a){var b=K.location.search||K.location.hash;if(b){if(/\?/.test(b)&&(b=b.split("?")[1]),null==a)return v(b);for(var c=b.split("&"),d=0;de;e++)d.push(arguments[e]);try{0==d.length?c[b]():c[b].apply(c,d)}catch(g){}}},a.handlePreloadEvent=function(a,b){var c=this._flashPreloadInstances[a];if(null!=c){for(var d=[],e=2,f=arguments.length;f>e;e++)d.push(arguments[e]);try{0==d.length?c[b]():c[b].apply(c,d)}catch(g){}}},a.handleEvent=function(a){switch(a){case"ready":this._handleFlashReady()}},a.handleErrorEvent=function(){},createjs.FlashAudioPlugin=createjs.promote(FlashAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){var a=createjs.FlashAudioPlugin=createjs.FlashAudioPlugin||{};a.version="NEXT",a.buildDate="Wed, 27 May 2015 18:12:38 GMT"}();
\ No newline at end of file
diff --git a/lib/soundjs-NEXT.combined.js b/lib/soundjs-NEXT.combined.js
index df5d171b..40d36302 100644
--- a/lib/soundjs-NEXT.combined.js
+++ b/lib/soundjs-NEXT.combined.js
@@ -1,268 +1,295 @@
+/*!
+* SoundJS
+* 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.
+*/
//##############################################################################
// version.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
-
- /**
- * Static class holding library specific information such as the version and buildDate of the library.
- * The SoundJS class has been renamed {{#crossLink "Sound"}}{{/crossLink}}. Please see {{#crossLink "Sound"}}{{/crossLink}}
- * for information on using sound.
- * @class SoundJS
- **/
- var s = createjs.SoundJS = createjs.SoundJS || {};
-
- /**
- * The version string for this release.
- * @property version
- * @type String
- * @static
- **/
- s.version = /*=version*/"NEXT"; // injected by build process
-
- /**
- * The build date for this release in UTC format.
- * @property buildDate
- * @type String
- * @static
- **/
- s.buildDate = /*=date*/"Tue, 19 May 2015 17:26:59 GMT"; // injected by build process
-
+this.createjs = this.createjs || {};
+
+(function () {
+
+ /**
+ * Static class holding library specific information such as the version and buildDate of the library.
+ * The SoundJS class has been renamed {{#crossLink "Sound"}}{{/crossLink}}. Please see {{#crossLink "Sound"}}{{/crossLink}}
+ * for information on using sound.
+ * @class SoundJS
+ **/
+ var s = createjs.SoundJS = createjs.SoundJS || {};
+
+ /**
+ * The version string for this release.
+ * @property version
+ * @type String
+ * @static
+ **/
+ s.version = /*=version*/"NEXT"; // injected by build process
+
+ /**
+ * The build date for this release in UTC format.
+ * @property buildDate
+ * @type String
+ * @static
+ **/
+ s.buildDate = /*=date*/"Wed, 27 May 2015 18:12:38 GMT"; // injected by build process
+
})();
//##############################################################################
// extend.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Sets up the prototype chain and constructor property for a new class.
- *
- * This should be called right after creating the class constructor.
- *
- * function MySubClass() {}
- * createjs.extend(MySubClass, MySuperClass);
- * ClassB.prototype.doSomething = function() { }
- *
- * var foo = new MySubClass();
- * console.log(foo instanceof MySuperClass); // true
- * console.log(foo.prototype.constructor === MySubClass); // true
- *
- * @method extend
- * @param {Function} subclass The subclass.
- * @param {Function} superclass The superclass to extend.
- * @return {Function} Returns the subclass's new prototype.
- */
-createjs.extend = function(subclass, superclass) {
- "use strict";
-
- function o() { this.constructor = subclass; }
- o.prototype = superclass.prototype;
- return (subclass.prototype = new o());
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Sets up the prototype chain and constructor property for a new class.
+ *
+ * This should be called right after creating the class constructor.
+ *
+ * function MySubClass() {}
+ * createjs.extend(MySubClass, MySuperClass);
+ * ClassB.prototype.doSomething = function() { }
+ *
+ * var foo = new MySubClass();
+ * console.log(foo instanceof MySuperClass); // true
+ * console.log(foo.prototype.constructor === MySubClass); // true
+ *
+ * @method extend
+ * @param {Function} subclass The subclass.
+ * @param {Function} superclass The superclass to extend.
+ * @return {Function} Returns the subclass's new prototype.
+ */
+createjs.extend = function(subclass, superclass) {
+ "use strict";
+
+ function o() { this.constructor = subclass; }
+ o.prototype = superclass.prototype;
+ return (subclass.prototype = new o());
};
//##############################################################################
// promote.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
- * It is recommended to use the super class's name as the prefix.
- * An alias to the super class's constructor is always added in the format `prefix_constructor`.
- * This allows the subclass to call super class methods without using `function.call`, providing better performance.
- *
- * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
- * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
- * prototype of `MySubClass` as `MySuperClass_draw`.
- *
- * This should be called after the class's prototype is fully defined.
- *
- * function ClassA(name) {
- * this.name = name;
- * }
- * ClassA.prototype.greet = function() {
- * return "Hello "+this.name;
- * }
- *
- * function ClassB(name, punctuation) {
- * this.ClassA_constructor(name);
- * this.punctuation = punctuation;
- * }
- * createjs.extend(ClassB, ClassA);
- * ClassB.prototype.greet = function() {
- * return this.ClassA_greet()+this.punctuation;
- * }
- * createjs.promote(ClassB, "ClassA");
- *
- * var foo = new ClassB("World", "!?!");
- * console.log(foo.greet()); // Hello World!?!
- *
- * @method promote
- * @param {Function} subclass The class to promote super class methods on.
- * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
- * @return {Function} Returns the subclass.
- */
-createjs.promote = function(subclass, prefix) {
- "use strict";
-
- var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
- if (supP) {
- subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
- for (var n in supP) {
- if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
- }
- }
- return subclass;
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
+ * It is recommended to use the super class's name as the prefix.
+ * An alias to the super class's constructor is always added in the format `prefix_constructor`.
+ * This allows the subclass to call super class methods without using `function.call`, providing better performance.
+ *
+ * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
+ * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
+ * prototype of `MySubClass` as `MySuperClass_draw`.
+ *
+ * This should be called after the class's prototype is fully defined.
+ *
+ * function ClassA(name) {
+ * this.name = name;
+ * }
+ * ClassA.prototype.greet = function() {
+ * return "Hello "+this.name;
+ * }
+ *
+ * function ClassB(name, punctuation) {
+ * this.ClassA_constructor(name);
+ * this.punctuation = punctuation;
+ * }
+ * createjs.extend(ClassB, ClassA);
+ * ClassB.prototype.greet = function() {
+ * return this.ClassA_greet()+this.punctuation;
+ * }
+ * createjs.promote(ClassB, "ClassA");
+ *
+ * var foo = new ClassB("World", "!?!");
+ * console.log(foo.greet()); // Hello World!?!
+ *
+ * @method promote
+ * @param {Function} subclass The class to promote super class methods on.
+ * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
+ * @return {Function} Returns the subclass.
+ */
+createjs.promote = function(subclass, prefix) {
+ "use strict";
+
+ var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
+ if (supP) {
+ subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
+ for (var n in supP) {
+ if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
+ }
+ }
+ return subclass;
};
//##############################################################################
// IndexOf.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-
-/**
- * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
- * that value. Returns -1 if value is not found.
- *
- * var i = createjs.indexOf(myArray, myElementToFind);
- *
- * @method indexOf
- * @param {Array} array Array to search for searchElement
- * @param searchElement Element to find in array.
- * @return {Number} The first index of searchElement in array.
- */
-createjs.indexOf = function (array, searchElement){
- "use strict";
-
- for (var i = 0,l=array.length; i < l; i++) {
- if (searchElement === array[i]) {
- return i;
- }
- }
- return -1;
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
+ * that value. Returns -1 if value is not found.
+ *
+ * var i = createjs.indexOf(myArray, myElementToFind);
+ *
+ * @method indexOf
+ * @param {Array} array Array to search for searchElement
+ * @param searchElement Element to find in array.
+ * @return {Number} The first index of searchElement in array.
+ */
+createjs.indexOf = function (array, searchElement){
+ "use strict";
+
+ for (var i = 0,l=array.length; i < l; i++) {
+ if (searchElement === array[i]) {
+ return i;
+ }
+ }
+ return -1;
};
//##############################################################################
// Proxy.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-/**
- * Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the
- * createjs namespace directly.
- *
- *
Example
- *
- * myObject.addEventListener("change", createjs.proxy(myMethod, scope));
- *
- * @class Utility Methods
- * @main Utility Methods
- */
-
-(function() {
- "use strict";
-
- /**
- * A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a
- * callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the
- * method gets called in the correct scope.
- *
- * Additional arguments can be passed that will be applied to the function when it is called.
- *
- *
Example
- *
- * myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2));
- *
- * function myHandler(arg1, arg2) {
- * // This gets called when myObject.myCallback is executed.
- * }
- *
- * @method proxy
- * @param {Function} method The function to call
- * @param {Object} scope The scope to call the method name on
- * @param {mixed} [arg] * Arguments that are appended to the callback for additional params.
- * @public
- * @static
- */
- createjs.proxy = function (method, scope) {
- var aArgs = Array.prototype.slice.call(arguments, 2);
- return function () {
- return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs));
- };
- }
-
+this.createjs = this.createjs||{};
+
+/**
+ * Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the
+ * createjs namespace directly.
+ *
+ *
Example
+ *
+ * myObject.addEventListener("change", createjs.proxy(myMethod, scope));
+ *
+ * @class Utility Methods
+ * @main Utility Methods
+ */
+
+(function() {
+ "use strict";
+
+ /**
+ * A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a
+ * callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the
+ * method gets called in the correct scope.
+ *
+ * Additional arguments can be passed that will be applied to the function when it is called.
+ *
+ *
Example
+ *
+ * myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2));
+ *
+ * function myHandler(arg1, arg2) {
+ * // This gets called when myObject.myCallback is executed.
+ * }
+ *
+ * @method proxy
+ * @param {Function} method The function to call
+ * @param {Object} scope The scope to call the method name on
+ * @param {mixed} [arg] * Arguments that are appended to the callback for additional params.
+ * @public
+ * @static
+ */
+ createjs.proxy = function (method, scope) {
+ var aArgs = Array.prototype.slice.call(arguments, 2);
+ return function () {
+ return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs));
+ };
+ }
+
}());
//##############################################################################
// BrowserDetect.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-(function() {
- "use strict";
-
- /**
- * An object that determines the current browser, version, operating system, and other environment
- * variables via user agent string.
- *
- * Used for audio because feature detection is unable to detect the many limitations of mobile devices.
- *
- *
Example
- *
- * if (createjs.BrowserDetect.isIOS) { // do stuff }
- *
- * @property BrowserDetect
- * @type {Object}
- * @param {Boolean} isFirefox True if our browser is Firefox.
- * @param {Boolean} isOpera True if our browser is opera.
- * @param {Boolean} isChrome True if our browser is Chrome. Note that Chrome for Android returns true, but is a
- * completely different browser with different abilities.
- * @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPod).
- * @param {Boolean} isAndroid True if our browser is Android.
- * @param {Boolean} isBlackberry True if our browser is Blackberry.
- * @constructor
- * @static
- */
- function BrowserDetect() {
- throw "BrowserDetect cannot be instantiated";
- };
-
- var agent = BrowserDetect.agent = window.navigator.userAgent;
- BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1);
- BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1);
- BrowserDetect.isOpera = (window.opera != null);
- BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); // NOTE that Chrome on Android returns true but is a completely different browser with different abilities
- BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone;
- BrowserDetect.isAndroid = (agent.indexOf("Android") > -1) && !BrowserDetect.isWindowPhone;
- BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1);
-
- createjs.BrowserDetect = BrowserDetect;
-
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+(function() {
+ "use strict";
+
+ /**
+ * An object that determines the current browser, version, operating system, and other environment
+ * variables via user agent string.
+ *
+ * Used for audio because feature detection is unable to detect the many limitations of mobile devices.
+ *
+ *
Example
+ *
+ * if (createjs.BrowserDetect.isIOS) { // do stuff }
+ *
+ * @property BrowserDetect
+ * @type {Object}
+ * @param {Boolean} isFirefox True if our browser is Firefox.
+ * @param {Boolean} isOpera True if our browser is opera.
+ * @param {Boolean} isChrome True if our browser is Chrome. Note that Chrome for Android returns true, but is a
+ * completely different browser with different abilities.
+ * @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPod).
+ * @param {Boolean} isAndroid True if our browser is Android.
+ * @param {Boolean} isBlackberry True if our browser is Blackberry.
+ * @constructor
+ * @static
+ */
+ function BrowserDetect() {
+ throw "BrowserDetect cannot be instantiated";
+ };
+
+ var agent = BrowserDetect.agent = window.navigator.userAgent;
+ BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1);
+ BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1);
+ BrowserDetect.isOpera = (window.opera != null);
+ BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); // NOTE that Chrome on Android returns true but is a completely different browser with different abilities
+ BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone;
+ BrowserDetect.isAndroid = (agent.indexOf("Android") > -1) && !BrowserDetect.isWindowPhone;
+ BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1);
+
+ createjs.BrowserDetect = BrowserDetect;
+
}());
//##############################################################################
@@ -648,7056 +675,7056 @@ this.createjs = this.createjs||{};
// Event.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-// constructor:
- /**
- * Contains properties and methods shared by all events for use with
- * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
- *
- * Note that Event objects are often reused, so you should never
- * rely on an event object's state outside of the call stack it was received in.
- * @class Event
- * @param {String} type The event type.
- * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
- * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
- * @constructor
- **/
- function Event(type, bubbles, cancelable) {
-
-
- // public properties:
- /**
- * The type of event.
- * @property type
- * @type String
- **/
- this.type = type;
-
- /**
- * The object that generated an event.
- * @property target
- * @type Object
- * @default null
- * @readonly
- */
- this.target = null;
-
- /**
- * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
- * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
- * is generated from childObj, then a listener on parentObj would receive the event with
- * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
- * @property currentTarget
- * @type Object
- * @default null
- * @readonly
- */
- this.currentTarget = null;
-
- /**
- * For bubbling events, this indicates the current event phase:
- *
capture phase: starting from the top parent to the target
- *
at target phase: currently being dispatched from the target
- *
bubbling phase: from the target to the top parent
- *
- * @property eventPhase
- * @type Number
- * @default 0
- * @readonly
- */
- this.eventPhase = 0;
-
- /**
- * Indicates whether the event will bubble through the display list.
- * @property bubbles
- * @type Boolean
- * @default false
- * @readonly
- */
- this.bubbles = !!bubbles;
-
- /**
- * Indicates whether the default behaviour of this event can be cancelled via
- * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
- * @property cancelable
- * @type Boolean
- * @default false
- * @readonly
- */
- this.cancelable = !!cancelable;
-
- /**
- * The epoch time at which this event was created.
- * @property timeStamp
- * @type Number
- * @default 0
- * @readonly
- */
- this.timeStamp = (new Date()).getTime();
-
- /**
- * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
- * on this event.
- * @property defaultPrevented
- * @type Boolean
- * @default false
- * @readonly
- */
- this.defaultPrevented = false;
-
- /**
- * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
- * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
- * @property propagationStopped
- * @type Boolean
- * @default false
- * @readonly
- */
- this.propagationStopped = false;
-
- /**
- * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
- * on this event.
- * @property immediatePropagationStopped
- * @type Boolean
- * @default false
- * @readonly
- */
- this.immediatePropagationStopped = false;
-
- /**
- * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
- * @property removed
- * @type Boolean
- * @default false
- * @readonly
- */
- this.removed = false;
- }
- var p = Event.prototype;
-
- /**
- * REMOVED. Removed in favor of using `MySuperClass_constructor`.
- * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
- * for details.
- *
- * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
- *
- * @method initialize
- * @protected
- * @deprecated
- */
- // p.initialize = function() {}; // searchable for devs wondering where it is.
-
-
-// public methods:
- /**
- * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true.
- * Mirrors the DOM event standard.
- * @method preventDefault
- **/
- p.preventDefault = function() {
- this.defaultPrevented = this.cancelable&&true;
- };
-
- /**
- * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
- * Mirrors the DOM event standard.
- * @method stopPropagation
- **/
- p.stopPropagation = function() {
- this.propagationStopped = true;
- };
-
- /**
- * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
- * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
- * Mirrors the DOM event standard.
- * @method stopImmediatePropagation
- **/
- p.stopImmediatePropagation = function() {
- this.immediatePropagationStopped = this.propagationStopped = true;
- };
-
- /**
- * Causes the active listener to be removed via removeEventListener();
- *
- * myBtn.addEventListener("click", function(evt) {
- * // do stuff...
- * evt.remove(); // removes this listener.
- * });
- *
- * @method remove
- **/
- p.remove = function() {
- this.removed = true;
- };
-
- /**
- * Returns a clone of the Event instance.
- * @method clone
- * @return {Event} a clone of the Event instance.
- **/
- p.clone = function() {
- return new Event(this.type, this.bubbles, this.cancelable);
- };
-
- /**
- * Provides a chainable shortcut method for setting a number of properties on the instance.
- *
- * @method set
- * @param {Object} props A generic object containing properties to copy to the instance.
- * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
- * @chainable
- */
- p.set = function(props) {
- for (var n in props) { this[n] = props[n]; }
- return this;
- };
-
- /**
- * Returns a string representation of this object.
- * @method toString
- * @return {String} a string representation of the instance.
- **/
- p.toString = function() {
- return "[Event (type="+this.type+")]";
- };
-
- createjs.Event = Event;
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+// constructor:
+ /**
+ * Contains properties and methods shared by all events for use with
+ * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
+ *
+ * Note that Event objects are often reused, so you should never
+ * rely on an event object's state outside of the call stack it was received in.
+ * @class Event
+ * @param {String} type The event type.
+ * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
+ * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
+ * @constructor
+ **/
+ function Event(type, bubbles, cancelable) {
+
+
+ // public properties:
+ /**
+ * The type of event.
+ * @property type
+ * @type String
+ **/
+ this.type = type;
+
+ /**
+ * The object that generated an event.
+ * @property target
+ * @type Object
+ * @default null
+ * @readonly
+ */
+ this.target = null;
+
+ /**
+ * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
+ * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
+ * is generated from childObj, then a listener on parentObj would receive the event with
+ * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
+ * @property currentTarget
+ * @type Object
+ * @default null
+ * @readonly
+ */
+ this.currentTarget = null;
+
+ /**
+ * For bubbling events, this indicates the current event phase:
+ *
capture phase: starting from the top parent to the target
+ *
at target phase: currently being dispatched from the target
+ *
bubbling phase: from the target to the top parent
+ *
+ * @property eventPhase
+ * @type Number
+ * @default 0
+ * @readonly
+ */
+ this.eventPhase = 0;
+
+ /**
+ * Indicates whether the event will bubble through the display list.
+ * @property bubbles
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.bubbles = !!bubbles;
+
+ /**
+ * Indicates whether the default behaviour of this event can be cancelled via
+ * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
+ * @property cancelable
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.cancelable = !!cancelable;
+
+ /**
+ * The epoch time at which this event was created.
+ * @property timeStamp
+ * @type Number
+ * @default 0
+ * @readonly
+ */
+ this.timeStamp = (new Date()).getTime();
+
+ /**
+ * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
+ * on this event.
+ * @property defaultPrevented
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.defaultPrevented = false;
+
+ /**
+ * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
+ * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
+ * @property propagationStopped
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.propagationStopped = false;
+
+ /**
+ * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
+ * on this event.
+ * @property immediatePropagationStopped
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.immediatePropagationStopped = false;
+
+ /**
+ * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
+ * @property removed
+ * @type Boolean
+ * @default false
+ * @readonly
+ */
+ this.removed = false;
+ }
+ var p = Event.prototype;
+
+ /**
+ * REMOVED. Removed in favor of using `MySuperClass_constructor`.
+ * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
+ * for details.
+ *
+ * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
+ *
+ * @method initialize
+ * @protected
+ * @deprecated
+ */
+ // p.initialize = function() {}; // searchable for devs wondering where it is.
+
+
+// public methods:
+ /**
+ * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true.
+ * Mirrors the DOM event standard.
+ * @method preventDefault
+ **/
+ p.preventDefault = function() {
+ this.defaultPrevented = this.cancelable&&true;
+ };
+
+ /**
+ * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
+ * Mirrors the DOM event standard.
+ * @method stopPropagation
+ **/
+ p.stopPropagation = function() {
+ this.propagationStopped = true;
+ };
+
+ /**
+ * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
+ * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
+ * Mirrors the DOM event standard.
+ * @method stopImmediatePropagation
+ **/
+ p.stopImmediatePropagation = function() {
+ this.immediatePropagationStopped = this.propagationStopped = true;
+ };
+
+ /**
+ * Causes the active listener to be removed via removeEventListener();
+ *
+ * myBtn.addEventListener("click", function(evt) {
+ * // do stuff...
+ * evt.remove(); // removes this listener.
+ * });
+ *
+ * @method remove
+ **/
+ p.remove = function() {
+ this.removed = true;
+ };
+
+ /**
+ * Returns a clone of the Event instance.
+ * @method clone
+ * @return {Event} a clone of the Event instance.
+ **/
+ p.clone = function() {
+ return new Event(this.type, this.bubbles, this.cancelable);
+ };
+
+ /**
+ * Provides a chainable shortcut method for setting a number of properties on the instance.
+ *
+ * @method set
+ * @param {Object} props A generic object containing properties to copy to the instance.
+ * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
+ * @chainable
+ */
+ p.set = function(props) {
+ for (var n in props) { this[n] = props[n]; }
+ return this;
+ };
+
+ /**
+ * Returns a string representation of this object.
+ * @method toString
+ * @return {String} a string representation of the instance.
+ **/
+ p.toString = function() {
+ return "[Event (type="+this.type+")]";
+ };
+
+ createjs.Event = Event;
}());
//##############################################################################
// ErrorEvent.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
- /**
- * A general error {{#crossLink "Event"}}{{/crossLink}}, that describes an error that occurred, as well as any details.
- * @class ErrorEvent
- * @param {String} [title] The error title
- * @param {String} [message] The error description
- * @param {Object} [data] Additional error data
- * @constructor
- */
- function ErrorEvent(title, message, data) {
- this.Event_constructor("error");
-
- /**
- * The short error title, which indicates the type of error that occurred.
- * @property title
- * @type String
- */
- this.title = title;
-
- /**
- * The verbose error message, containing details about the error.
- * @property message
- * @type String
- */
- this.message = message;
-
- /**
- * Additional data attached to an error.
- * @property data
- * @type {Object}
- */
- this.data = data;
- }
-
- var p = createjs.extend(ErrorEvent, createjs.Event);
-
- p.clone = function() {
- return new createjs.ErrorEvent(this.title, this.message, this.data);
- };
-
- createjs.ErrorEvent = createjs.promote(ErrorEvent, "Event");
-
+this.createjs = this.createjs||{};
+
+(function() {
+ "use strict";
+
+ /**
+ * A general error {{#crossLink "Event"}}{{/crossLink}}, that describes an error that occurred, as well as any details.
+ * @class ErrorEvent
+ * @param {String} [title] The error title
+ * @param {String} [message] The error description
+ * @param {Object} [data] Additional error data
+ * @constructor
+ */
+ function ErrorEvent(title, message, data) {
+ this.Event_constructor("error");
+
+ /**
+ * The short error title, which indicates the type of error that occurred.
+ * @property title
+ * @type String
+ */
+ this.title = title;
+
+ /**
+ * The verbose error message, containing details about the error.
+ * @property message
+ * @type String
+ */
+ this.message = message;
+
+ /**
+ * Additional data attached to an error.
+ * @property data
+ * @type {Object}
+ */
+ this.data = data;
+ }
+
+ var p = createjs.extend(ErrorEvent, createjs.Event);
+
+ p.clone = function() {
+ return new createjs.ErrorEvent(this.title, this.message, this.data);
+ };
+
+ createjs.ErrorEvent = createjs.promote(ErrorEvent, "Event");
+
}());
//##############################################################################
// ProgressEvent.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function (scope) {
- "use strict";
-
- // constructor
- /**
- * A CreateJS {{#crossLink "Event"}}{{/crossLink}} that is dispatched when progress changes.
- * @class ProgressEvent
- * @param {Number} loaded The amount that has been loaded. This can be any number relative to the total.
- * @param {Number} [total=1] The total amount that will load. This will default to 1, so if the `loaded` value is
- * a percentage (between 0 and 1), it can be omitted.
- * @todo Consider having this event be a "fileprogress" event as well
- * @constructor
- */
- function ProgressEvent(loaded, total) {
- this.Event_constructor("progress");
-
- /**
- * The amount that has been loaded (out of a total amount)
- * @property loaded
- * @type {Number}
- */
- this.loaded = loaded;
-
- /**
- * The total "size" of the load.
- * @property total
- * @type {Number}
- * @default 1
- */
- this.total = (total == null) ? 1 : total;
-
- /**
- * The percentage (out of 1) that the load has been completed. This is calculated using `loaded/total`.
- * @property progress
- * @type {Number}
- * @default 0
- */
- this.progress = (total == 0) ? 0 : this.loaded / this.total;
- };
-
- var p = createjs.extend(ProgressEvent, createjs.Event);
-
- /**
- * Returns a clone of the ProgressEvent instance.
- * @method clone
- * @return {ProgressEvent} a clone of the Event instance.
- **/
- p.clone = function() {
- return new createjs.ProgressEvent(this.loaded, this.total);
- };
-
- createjs.ProgressEvent = createjs.promote(ProgressEvent, "Event");
-
+this.createjs = this.createjs || {};
+
+(function (scope) {
+ "use strict";
+
+ // constructor
+ /**
+ * A CreateJS {{#crossLink "Event"}}{{/crossLink}} that is dispatched when progress changes.
+ * @class ProgressEvent
+ * @param {Number} loaded The amount that has been loaded. This can be any number relative to the total.
+ * @param {Number} [total=1] The total amount that will load. This will default to 1, so if the `loaded` value is
+ * a percentage (between 0 and 1), it can be omitted.
+ * @todo Consider having this event be a "fileprogress" event as well
+ * @constructor
+ */
+ function ProgressEvent(loaded, total) {
+ this.Event_constructor("progress");
+
+ /**
+ * The amount that has been loaded (out of a total amount)
+ * @property loaded
+ * @type {Number}
+ */
+ this.loaded = loaded;
+
+ /**
+ * The total "size" of the load.
+ * @property total
+ * @type {Number}
+ * @default 1
+ */
+ this.total = (total == null) ? 1 : total;
+
+ /**
+ * The percentage (out of 1) that the load has been completed. This is calculated using `loaded/total`.
+ * @property progress
+ * @type {Number}
+ * @default 0
+ */
+ this.progress = (total == 0) ? 0 : this.loaded / this.total;
+ };
+
+ var p = createjs.extend(ProgressEvent, createjs.Event);
+
+ /**
+ * Returns a clone of the ProgressEvent instance.
+ * @method clone
+ * @return {ProgressEvent} a clone of the Event instance.
+ **/
+ p.clone = function() {
+ return new createjs.ProgressEvent(this.loaded, this.total);
+ };
+
+ createjs.ProgressEvent = createjs.promote(ProgressEvent, "Event");
+
}(window));
//##############################################################################
// LoadItem.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- /**
- * All loaders accept an item containing the properties defined in this class. If a raw object is passed instead,
- * it will not be affected, but it must contain at least a {{#crossLink "src:property"}}{{/crossLink}} property. A
- * string path or HTML tag is also acceptable, but it will be automatically converted to a LoadItem using the
- * {{#crossLink "create"}}{{/crossLink}} method by {{#crossLink "AbstractLoader"}}{{/crossLink}}
- * @class LoadItem
- * @constructor
- * @since 0.6.0
- */
- function LoadItem() {
- /**
- * The source of the file that is being loaded. This property is required. The source can either be a
- * string (recommended), or an HTML tag.
- * This can also be an object, but in that case it has to include a type and be handled by a plugin.
- * @property src
- * @type {String}
- * @default null
- */
- this.src = null;
-
- /**
- * The type file that is being loaded. The type of the file is usually inferred by the extension, but can also
- * be set manually. This is helpful in cases where a file does not have an extension.
- * @property type
- * @type {String}
- * @default null
- */
- this.type = null;
-
- /**
- * A string identifier which can be used to reference the loaded object. If none is provided, this will be
- * automatically set to the {{#crossLink "src:property"}}{{/crossLink}}.
- * @property id
- * @type {String}
- * @default null
- */
- this.id = null;
-
- /**
- * Determines if a manifest will maintain the order of this item, in relation to other items in the manifest
- * that have also set the `maintainOrder` property to `true`. This only applies when the max connections has
- * been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}). Everything with this
- * property set to `false` will finish as it is loaded. Ordered items are combined with script tags loading in
- * order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} is set to `true`.
- * @property maintainOrder
- * @type {Boolean}
- * @default false
- */
- this.maintainOrder = false;
-
- /**
- * A callback used by JSONP requests that defines what global method to call when the JSONP content is loaded.
- * @property callback
- * @type {String}
- * @default null
- */
- this.callback = null;
-
- /**
- * An arbitrary data object, which is included with the loaded object.
- * @property data
- * @type {Object}
- * @default null
- */
- this.data = null;
-
- /**
- * The request method used for HTTP calls. Both {{#crossLink "AbstractLoader/GET:property"}}{{/crossLink}} or
- * {{#crossLink "AbstractLoader/POST:property"}}{{/crossLink}} request types are supported, and are defined as
- * constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
- * @property method
- * @type {String}
- * @default get
- */
- this.method = createjs.LoadItem.GET;
-
- /**
- * An object hash of name/value pairs to send to the server.
- * @property values
- * @type {Object}
- * @default null
- */
- this.values = null;
-
- /**
- * An object hash of headers to attach to an XHR request. PreloadJS will automatically attach some default
- * headers when required, including "Origin", "Content-Type", and "X-Requested-With". You may override the
- * default headers by including them in your headers object.
- * @property headers
- * @type {Object}
- * @default null
- */
- this.headers = null;
-
- /**
- * Enable credentials for XHR requests.
- * @property withCredentials
- * @type {Boolean}
- * @default false
- */
- this.withCredentials = false;
-
- /**
- * Set the mime type of XHR-based requests. This is automatically set to "text/plain; charset=utf-8" for text
- * based files (json, xml, text, css, js).
- * @property mimeType
- * @type {String}
- * @default null
- */
- this.mimeType = null;
-
- /**
- * Sets the crossOrigin attribute for CORS-enabled images loading cross-domain.
- * @property crossOrigin
- * @type {boolean}
- * @default Anonymous
- */
- this.crossOrigin = null;
-
- /**
- * The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
- * (level one) loading, as XHR (level 2) provides its own timeout event.
- * @property loadTimeout
- * @type {Number}
- * @default 8000 (8 seconds)
- */
- this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
- };
-
- var p = LoadItem.prototype = {};
- var s = LoadItem;
-
- /**
- * Default duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
- * (level one) loading, as XHR (level 2) provides its own timeout event.
- * @property LOAD_TIMEOUT_DEFAULT
- * @type {number}
- * @static
- */
- s.LOAD_TIMEOUT_DEFAULT = 8000;
-
- /**
- * Create a LoadItem.
- *
- *
String-based items are converted to a LoadItem with a populated {{#crossLink "src:property"}}{{/crossLink}}.
- *
LoadItem instances are returned as-is
- *
Objects are returned with any needed properties added
- *
- * @method create
- * @param {LoadItem|String|Object} value The load item value
- * @returns {LoadItem|Object}
- * @static
- */
- s.create = function (value) {
- if (typeof value == "string") {
- var item = new LoadItem();
- item.src = value;
- return item;
- } else if (value instanceof s) {
- return value;
- } else if (value instanceof Object && value.src) {
- if (value.loadTimeout == null) {
- value.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
- }
- return value;
- } else {
- throw new Error("Type not recognized.");
- }
- };
-
- /**
- * Provides a chainable shortcut method for setting a number of properties on the instance.
- *
- *
Example
- *
- * var loadItem = new createjs.LoadItem().set({src:"image.png", maintainOrder:true});
- *
- * @method set
- * @param {Object} props A generic object containing properties to copy to the LoadItem instance.
- * @return {LoadItem} 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;
- };
-
- createjs.LoadItem = s;
-
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * All loaders accept an item containing the properties defined in this class. If a raw object is passed instead,
+ * it will not be affected, but it must contain at least a {{#crossLink "src:property"}}{{/crossLink}} property. A
+ * string path or HTML tag is also acceptable, but it will be automatically converted to a LoadItem using the
+ * {{#crossLink "create"}}{{/crossLink}} method by {{#crossLink "AbstractLoader"}}{{/crossLink}}
+ * @class LoadItem
+ * @constructor
+ * @since 0.6.0
+ */
+ function LoadItem() {
+ /**
+ * The source of the file that is being loaded. This property is required. The source can either be a
+ * string (recommended), or an HTML tag.
+ * This can also be an object, but in that case it has to include a type and be handled by a plugin.
+ * @property src
+ * @type {String}
+ * @default null
+ */
+ this.src = null;
+
+ /**
+ * The type file that is being loaded. The type of the file is usually inferred by the extension, but can also
+ * be set manually. This is helpful in cases where a file does not have an extension.
+ * @property type
+ * @type {String}
+ * @default null
+ */
+ this.type = null;
+
+ /**
+ * A string identifier which can be used to reference the loaded object. If none is provided, this will be
+ * automatically set to the {{#crossLink "src:property"}}{{/crossLink}}.
+ * @property id
+ * @type {String}
+ * @default null
+ */
+ this.id = null;
+
+ /**
+ * Determines if a manifest will maintain the order of this item, in relation to other items in the manifest
+ * that have also set the `maintainOrder` property to `true`. This only applies when the max connections has
+ * been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}). Everything with this
+ * property set to `false` will finish as it is loaded. Ordered items are combined with script tags loading in
+ * order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} is set to `true`.
+ * @property maintainOrder
+ * @type {Boolean}
+ * @default false
+ */
+ this.maintainOrder = false;
+
+ /**
+ * A callback used by JSONP requests that defines what global method to call when the JSONP content is loaded.
+ * @property callback
+ * @type {String}
+ * @default null
+ */
+ this.callback = null;
+
+ /**
+ * An arbitrary data object, which is included with the loaded object.
+ * @property data
+ * @type {Object}
+ * @default null
+ */
+ this.data = null;
+
+ /**
+ * The request method used for HTTP calls. Both {{#crossLink "AbstractLoader/GET:property"}}{{/crossLink}} or
+ * {{#crossLink "AbstractLoader/POST:property"}}{{/crossLink}} request types are supported, and are defined as
+ * constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
+ * @property method
+ * @type {String}
+ * @default get
+ */
+ this.method = createjs.LoadItem.GET;
+
+ /**
+ * An object hash of name/value pairs to send to the server.
+ * @property values
+ * @type {Object}
+ * @default null
+ */
+ this.values = null;
+
+ /**
+ * An object hash of headers to attach to an XHR request. PreloadJS will automatically attach some default
+ * headers when required, including "Origin", "Content-Type", and "X-Requested-With". You may override the
+ * default headers by including them in your headers object.
+ * @property headers
+ * @type {Object}
+ * @default null
+ */
+ this.headers = null;
+
+ /**
+ * Enable credentials for XHR requests.
+ * @property withCredentials
+ * @type {Boolean}
+ * @default false
+ */
+ this.withCredentials = false;
+
+ /**
+ * Set the mime type of XHR-based requests. This is automatically set to "text/plain; charset=utf-8" for text
+ * based files (json, xml, text, css, js).
+ * @property mimeType
+ * @type {String}
+ * @default null
+ */
+ this.mimeType = null;
+
+ /**
+ * Sets the crossOrigin attribute for CORS-enabled images loading cross-domain.
+ * @property crossOrigin
+ * @type {boolean}
+ * @default Anonymous
+ */
+ this.crossOrigin = null;
+
+ /**
+ * The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
+ * (level one) loading, as XHR (level 2) provides its own timeout event.
+ * @property loadTimeout
+ * @type {Number}
+ * @default 8000 (8 seconds)
+ */
+ this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
+ };
+
+ var p = LoadItem.prototype = {};
+ var s = LoadItem;
+
+ /**
+ * Default duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
+ * (level one) loading, as XHR (level 2) provides its own timeout event.
+ * @property LOAD_TIMEOUT_DEFAULT
+ * @type {number}
+ * @static
+ */
+ s.LOAD_TIMEOUT_DEFAULT = 8000;
+
+ /**
+ * Create a LoadItem.
+ *
+ *
String-based items are converted to a LoadItem with a populated {{#crossLink "src:property"}}{{/crossLink}}.
+ *
LoadItem instances are returned as-is
+ *
Objects are returned with any needed properties added
+ *
+ * @method create
+ * @param {LoadItem|String|Object} value The load item value
+ * @returns {LoadItem|Object}
+ * @static
+ */
+ s.create = function (value) {
+ if (typeof value == "string") {
+ var item = new LoadItem();
+ item.src = value;
+ return item;
+ } else if (value instanceof s) {
+ return value;
+ } else if (value instanceof Object && value.src) {
+ if (value.loadTimeout == null) {
+ value.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
+ }
+ return value;
+ } else {
+ throw new Error("Type not recognized.");
+ }
+ };
+
+ /**
+ * Provides a chainable shortcut method for setting a number of properties on the instance.
+ *
+ *
Example
+ *
+ * var loadItem = new createjs.LoadItem().set({src:"image.png", maintainOrder:true});
+ *
+ * @method set
+ * @param {Object} props A generic object containing properties to copy to the LoadItem instance.
+ * @return {LoadItem} 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;
+ };
+
+ createjs.LoadItem = s;
+
}());
//##############################################################################
// RequestUtils.js
//##############################################################################
-(function () {
-
- /**
- * Utilities that assist with parsing load items, and determining file types, etc.
- * @class RequestUtils
- */
- var s = {};
-
- /**
- * The Regular Expression used to test file URLS for an absolute path.
- * @property ABSOLUTE_PATH
- * @type {RegExp}
- * @static
- */
- s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
-
- /**
- * The Regular Expression used to test file URLS for a relative path.
- * @property RELATIVE_PATH
- * @type {RegExp}
- * @static
- */
- 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
- * @type {RegExp}
- * @static
- */
- s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
-
- /**
- * Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
- *
- *
If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
- * `//networkPath`)
- *
If the path is relative. Relative paths start with `../` or `/path` (or similar)
- *
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`.
- *
- * @method parseURI
- * @param {String} path
- * @returns {Object} An Object with an `absolute` and `relative` Boolean values, as well as an optional 'extension`
- * property, which is the lowercase extension.
- * @static
- */
- s.parseURI = function (path) {
- var info = {absolute: false, relative: false};
- if (path == null) { return info; }
-
- // 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;
- };
-
- /**
- * Formats an object into a query string for either a POST or GET request.
- * @method formatQueryString
- * @param {Object} data The data to convert to a query string.
- * @param {Array} [query] Existing name/value pairs to append on to this query.
- * @static
- */
- s.formatQueryString = function (data, query) {
- if (data == null) {
- throw new Error('You must specify data.');
- }
- var params = [];
- for (var n in data) {
- params.push(n + '=' + escape(data[n]));
- }
- if (query) {
- params = params.concat(query);
- }
- return params.join('&');
- };
-
- /**
- * A utility method that builds a file path using a source and a data object, and formats it into a new path.
- * @method buildPath
- * @param {String} src The source path to add values to.
- * @param {Object} [data] Object used to append values to this request as a query string. Existing parameters on the
- * path will be preserved.
- * @returns {string} A formatted string that contains the path and the supplied parameters.
- * @static
- */
- s.buildPath = function (src, data) {
- if (data == null) {
- return src;
- }
-
- var query = [];
- var idx = src.indexOf('?');
-
- if (idx != -1) {
- var q = src.slice(idx + 1);
- query = query.concat(q.split('&'));
- }
-
- if (idx != -1) {
- return src.slice(0, idx) + '?' + this._formatQueryString(data, query);
- } else {
- return src + '?' + this._formatQueryString(data, query);
- }
- };
-
- /**
- * @method isCrossDomain
- * @param {LoadItem|Object} item A load item with a `src` property.
- * @return {Boolean} If the load item is loading from a different domain than the current location.
- * @static
- */
- s.isCrossDomain = function (item) {
- var target = document.createElement("a");
- target.href = item.src;
-
- var host = document.createElement("a");
- host.href = location.href;
-
- var crossdomain = (target.hostname != "") &&
- (target.port != host.port ||
- target.protocol != host.protocol ||
- target.hostname != host.hostname);
- return crossdomain;
- };
-
- /**
- * @method isLocal
- * @param {LoadItem|Object} item A load item with a `src` property
- * @return {Boolean} If the load item is loading from the "file:" protocol. Assume that the host must be local as
- * well.
- * @static
- */
- s.isLocal = function (item) {
- var target = document.createElement("a");
- target.href = item.src;
- return target.hostname == "" && target.protocol == "file:";
- };
-
- /**
- * Determine if a specific type should be loaded as a binary file. Currently, only images and items marked
- * specifically as "binary" are loaded as binary. Note that audio is not a binary type, as we can not play
- * back using an audio tag if it is loaded as binary. Plugins can change the item type to binary to ensure they get
- * a binary result to work with. Binary files are loaded using XHR2. Types are defined as static constants on
- * {{#crossLink "AbstractLoader"}}{{/crossLink}}.
- * @method isBinary
- * @param {String} type The item type.
- * @return {Boolean} If the specified type is binary.
- * @static
- */
- s.isBinary = function (type) {
- switch (type) {
- case createjs.AbstractLoader.IMAGE:
- case createjs.AbstractLoader.BINARY:
- return true;
- default:
- return false;
- }
- };
-
- /**
- * Check if item is a valid HTMLImageElement
- * @method isImageTag
- * @param {Object} item
- * @returns {Boolean}
- * @static
- */
- s.isImageTag = function(item) {
- return item instanceof HTMLImageElement;
- };
-
- /**
- * Check if item is a valid HTMLAudioElement
- * @method isAudioTag
- * @param {Object} item
- * @returns {Boolean}
- * @static
- */
- s.isAudioTag = function(item) {
- if (window.HTMLAudioElement) {
- return item instanceof HTMLAudioElement;
- } else {
- return false;
- }
- };
-
- /**
- * Check if item is a valid HTMLVideoElement
- * @method isVideoTag
- * @param {Object} item
- * @returns {Boolean}
- * @static
- */
- s.isVideoTag = function(item) {
- if (window.HTMLVideoElement) {
- return item instanceof HTMLVideoElement;
- } else {
- return false;
- }
- };
-
- /**
- * Determine if a specific type is a text-based asset, and should be loaded as UTF-8.
- * @method isText
- * @param {String} type The item type.
- * @return {Boolean} If the specified type is text.
- * @static
- */
- s.isText = function (type) {
- switch (type) {
- case createjs.AbstractLoader.TEXT:
- case createjs.AbstractLoader.JSON:
- case createjs.AbstractLoader.MANIFEST:
- case createjs.AbstractLoader.XML:
- case createjs.AbstractLoader.CSS:
- case createjs.AbstractLoader.SVG:
- case createjs.AbstractLoader.JAVASCRIPT:
- case createjs.AbstractLoader.SPRITESHEET:
- return true;
- default:
- return false;
- }
- };
-
- /**
- * Determine the type of the object using common extensions. Note that the type can be passed in with the load item
- * if it is an unusual extension.
- * @method getTypeByExtension
- * @param {String} extension The file extension to use to determine the load type.
- * @return {String} The determined load type (for example, AbstractLoader.IMAGE). Will return `null` if
- * the type can not be determined by the extension.
- * @static
- */
- s.getTypeByExtension = function (extension) {
- if (extension == null) {
- return createjs.AbstractLoader.TEXT;
- }
-
- switch (extension.toLowerCase()) {
- case "jpeg":
- case "jpg":
- case "gif":
- case "png":
- case "webp":
- case "bmp":
- return createjs.AbstractLoader.IMAGE;
- case "ogg":
- case "mp3":
- case "webm":
- return createjs.AbstractLoader.SOUND;
- case "mp4":
- case "webm":
- case "ts":
- return createjs.AbstractLoader.VIDEO;
- case "json":
- return createjs.AbstractLoader.JSON;
- case "xml":
- return createjs.AbstractLoader.XML;
- case "css":
- return createjs.AbstractLoader.CSS;
- case "js":
- return createjs.AbstractLoader.JAVASCRIPT;
- case 'svg':
- return createjs.AbstractLoader.SVG;
- default:
- return createjs.AbstractLoader.TEXT;
- }
- };
-
- createjs.RequestUtils = s;
-
+(function () {
+
+ /**
+ * Utilities that assist with parsing load items, and determining file types, etc.
+ * @class RequestUtils
+ */
+ var s = {};
+
+ /**
+ * The Regular Expression used to test file URLS for an absolute path.
+ * @property ABSOLUTE_PATH
+ * @type {RegExp}
+ * @static
+ */
+ s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
+
+ /**
+ * The Regular Expression used to test file URLS for a relative path.
+ * @property RELATIVE_PATH
+ * @type {RegExp}
+ * @static
+ */
+ 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
+ * @type {RegExp}
+ * @static
+ */
+ s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
+
+ /**
+ * Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
+ *
+ *
If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
+ * `//networkPath`)
+ *
If the path is relative. Relative paths start with `../` or `/path` (or similar)
+ *
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`.
+ *
+ * @method parseURI
+ * @param {String} path
+ * @returns {Object} An Object with an `absolute` and `relative` Boolean values, as well as an optional 'extension`
+ * property, which is the lowercase extension.
+ * @static
+ */
+ s.parseURI = function (path) {
+ var info = {absolute: false, relative: false};
+ if (path == null) { return info; }
+
+ // 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;
+ };
+
+ /**
+ * Formats an object into a query string for either a POST or GET request.
+ * @method formatQueryString
+ * @param {Object} data The data to convert to a query string.
+ * @param {Array} [query] Existing name/value pairs to append on to this query.
+ * @static
+ */
+ s.formatQueryString = function (data, query) {
+ if (data == null) {
+ throw new Error('You must specify data.');
+ }
+ var params = [];
+ for (var n in data) {
+ params.push(n + '=' + escape(data[n]));
+ }
+ if (query) {
+ params = params.concat(query);
+ }
+ return params.join('&');
+ };
+
+ /**
+ * A utility method that builds a file path using a source and a data object, and formats it into a new path.
+ * @method buildPath
+ * @param {String} src The source path to add values to.
+ * @param {Object} [data] Object used to append values to this request as a query string. Existing parameters on the
+ * path will be preserved.
+ * @returns {string} A formatted string that contains the path and the supplied parameters.
+ * @static
+ */
+ s.buildPath = function (src, data) {
+ if (data == null) {
+ return src;
+ }
+
+ var query = [];
+ var idx = src.indexOf('?');
+
+ if (idx != -1) {
+ var q = src.slice(idx + 1);
+ query = query.concat(q.split('&'));
+ }
+
+ if (idx != -1) {
+ return src.slice(0, idx) + '?' + this._formatQueryString(data, query);
+ } else {
+ return src + '?' + this._formatQueryString(data, query);
+ }
+ };
+
+ /**
+ * @method isCrossDomain
+ * @param {LoadItem|Object} item A load item with a `src` property.
+ * @return {Boolean} If the load item is loading from a different domain than the current location.
+ * @static
+ */
+ s.isCrossDomain = function (item) {
+ var target = document.createElement("a");
+ target.href = item.src;
+
+ var host = document.createElement("a");
+ host.href = location.href;
+
+ var crossdomain = (target.hostname != "") &&
+ (target.port != host.port ||
+ target.protocol != host.protocol ||
+ target.hostname != host.hostname);
+ return crossdomain;
+ };
+
+ /**
+ * @method isLocal
+ * @param {LoadItem|Object} item A load item with a `src` property
+ * @return {Boolean} If the load item is loading from the "file:" protocol. Assume that the host must be local as
+ * well.
+ * @static
+ */
+ s.isLocal = function (item) {
+ var target = document.createElement("a");
+ target.href = item.src;
+ return target.hostname == "" && target.protocol == "file:";
+ };
+
+ /**
+ * Determine if a specific type should be loaded as a binary file. Currently, only images and items marked
+ * specifically as "binary" are loaded as binary. Note that audio is not a binary type, as we can not play
+ * back using an audio tag if it is loaded as binary. Plugins can change the item type to binary to ensure they get
+ * a binary result to work with. Binary files are loaded using XHR2. Types are defined as static constants on
+ * {{#crossLink "AbstractLoader"}}{{/crossLink}}.
+ * @method isBinary
+ * @param {String} type The item type.
+ * @return {Boolean} If the specified type is binary.
+ * @static
+ */
+ s.isBinary = function (type) {
+ switch (type) {
+ case createjs.AbstractLoader.IMAGE:
+ case createjs.AbstractLoader.BINARY:
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ /**
+ * Check if item is a valid HTMLImageElement
+ * @method isImageTag
+ * @param {Object} item
+ * @returns {Boolean}
+ * @static
+ */
+ s.isImageTag = function(item) {
+ return item instanceof HTMLImageElement;
+ };
+
+ /**
+ * Check if item is a valid HTMLAudioElement
+ * @method isAudioTag
+ * @param {Object} item
+ * @returns {Boolean}
+ * @static
+ */
+ s.isAudioTag = function(item) {
+ if (window.HTMLAudioElement) {
+ return item instanceof HTMLAudioElement;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Check if item is a valid HTMLVideoElement
+ * @method isVideoTag
+ * @param {Object} item
+ * @returns {Boolean}
+ * @static
+ */
+ s.isVideoTag = function(item) {
+ if (window.HTMLVideoElement) {
+ return item instanceof HTMLVideoElement;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Determine if a specific type is a text-based asset, and should be loaded as UTF-8.
+ * @method isText
+ * @param {String} type The item type.
+ * @return {Boolean} If the specified type is text.
+ * @static
+ */
+ s.isText = function (type) {
+ switch (type) {
+ case createjs.AbstractLoader.TEXT:
+ case createjs.AbstractLoader.JSON:
+ case createjs.AbstractLoader.MANIFEST:
+ case createjs.AbstractLoader.XML:
+ case createjs.AbstractLoader.CSS:
+ case createjs.AbstractLoader.SVG:
+ case createjs.AbstractLoader.JAVASCRIPT:
+ case createjs.AbstractLoader.SPRITESHEET:
+ return true;
+ default:
+ return false;
+ }
+ };
+
+ /**
+ * Determine the type of the object using common extensions. Note that the type can be passed in with the load item
+ * if it is an unusual extension.
+ * @method getTypeByExtension
+ * @param {String} extension The file extension to use to determine the load type.
+ * @return {String} The determined load type (for example, AbstractLoader.IMAGE). Will return `null` if
+ * the type can not be determined by the extension.
+ * @static
+ */
+ s.getTypeByExtension = function (extension) {
+ if (extension == null) {
+ return createjs.AbstractLoader.TEXT;
+ }
+
+ switch (extension.toLowerCase()) {
+ case "jpeg":
+ case "jpg":
+ case "gif":
+ case "png":
+ case "webp":
+ case "bmp":
+ return createjs.AbstractLoader.IMAGE;
+ case "ogg":
+ case "mp3":
+ case "webm":
+ return createjs.AbstractLoader.SOUND;
+ case "mp4":
+ case "webm":
+ case "ts":
+ return createjs.AbstractLoader.VIDEO;
+ case "json":
+ return createjs.AbstractLoader.JSON;
+ case "xml":
+ return createjs.AbstractLoader.XML;
+ case "css":
+ return createjs.AbstractLoader.CSS;
+ case "js":
+ return createjs.AbstractLoader.JAVASCRIPT;
+ case 'svg':
+ return createjs.AbstractLoader.SVG;
+ default:
+ return createjs.AbstractLoader.TEXT;
+ }
+ };
+
+ createjs.RequestUtils = s;
+
}());
//##############################################################################
// AbstractLoader.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
-// constructor
- /**
- * The base loader, which defines all the generic methods, properties, and events. All loaders extend this class,
- * including the {{#crossLink "LoadQueue"}}{{/crossLink}}.
- * @class AbstractLoader
- * @param {LoadItem|object|string} loadItem The item to be loaded.
- * @param {Boolean} [preferXHR] Determines if the LoadItem should try and load using XHR, or take a
- * tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the
- * other, so this is a suggested directive.
- * @param {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
- * such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc.
- * @extends EventDispatcher
- */
- function AbstractLoader(loadItem, preferXHR, type) {
- this.EventDispatcher_constructor();
-
- // public properties
- /**
- * If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
- * used for loading do not pile up resulting in more than one `complete` {{#crossLink "Event"}}{{/crossLink}}.
- * @property loaded
- * @type {Boolean}
- * @default false
- */
- this.loaded = false;
-
- /**
- * Determine if the loader was canceled. Canceled loads will not fire complete events. Note that this property
- * is readonly, so {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "LoadQueue/close"}}{{/crossLink}}
- * instead.
- * @property canceled
- * @type {Boolean}
- * @default false
- */
- this.canceled = false;
-
- /**
- * The current load progress (percentage) for this item. This will be a number between 0 and 1.
- *
- *
Example
- *
- * var queue = new createjs.LoadQueue();
- * queue.loadFile("largeImage.png");
- * queue.on("progress", function() {
- * console.log("Progress:", queue.progress, event.progress);
- * });
- *
- * @property progress
- * @type {Number}
- * @default 0
- */
- this.progress = 0;
-
- /**
- * The type of item this loader will load. See {{#crossLink "AbstractLoader"}}{{/crossLink}} for a full list of
- * supported types.
- * @property type
- * @type {String}
- */
- this.type = type;
-
- /**
- * A formatter function that converts the loaded raw result into the final result. For example, the JSONLoader
- * converts a string of text into a JavaScript object. Not all loaders have a resultFormatter, and this property
- * can be overridden to provide custom formatting.
- *
- * Optionally, a resultFormatter can return a callback function in cases where the formatting needs to be
- * asynchronous, such as creating a new image.
- * @property resultFormatter
- * @type {Function}
- * @default null
- */
- this.resultFormatter = null;
-
- // protected properties
- /**
- * The {{#crossLink "LoadItem"}}{{/crossLink}} this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}},
- * but will be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}.
- * @property _item
- * @type {LoadItem|Object}
- * @private
- */
- if (loadItem) {
- this._item = createjs.LoadItem.create(loadItem);
- } else {
- this._item = null;
- }
-
- /**
- * Whether the loader will try and load content using XHR (true) or HTML tags (false).
- * @property _preferXHR
- * @type {Boolean}
- * @private
- */
- this._preferXHR = preferXHR;
-
- /**
- * The loaded result after it is formatted by an optional {{#crossLink "resultFormatter"}}{{/crossLink}}. For
- * items that are not formatted, this will be the same as the {{#crossLink "_rawResult:property"}}{{/crossLink}}.
- * The result is accessed using the {{#crossLink "getResult"}}{{/crossLink}} method.
- * @property _result
- * @type {Object|String}
- * @private
- */
- this._result = null;
-
- /**
- * The loaded result before it is formatted. The rawResult is accessed using the {{#crossLink "getResult"}}{{/crossLink}}
- * method, and passing `true`.
- * @property _rawResult
- * @type {Object|String}
- * @private
- */
- this._rawResult = null;
-
- /**
- * A list of items that loaders load behind the scenes. This does not include the main item the loader is
- * responsible for loading. Examples of loaders that have sub-items include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and
- * {{#crossLink "ManifestLoader"}}{{/crossLink}}.
- * @property _loadItems
- * @type {null}
- * @protected
- */
- this._loadedItems = null;
-
- /**
- * The attribute the items loaded using tags use for the source.
- * @type {string}
- * @default null
- * @private
- */
- this._tagSrcAttribute = null;
-
- /**
- * An HTML tag (or similar) that a loader may use to load HTML content, such as images, scripts, etc.
- * @property _tag
- * @type {Object}
- * @private
- */
- this._tag = null;
- };
-
- var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
- var s = AbstractLoader;
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
- /**
- * Defines a POST request, use for a method value when loading data.
- * @property POST
- * @type {string}
- * @default post
- * @static
- */
- s.POST = "POST";
-
- /**
- * Defines a GET request, use for a method value when loading data.
- * @property GET
- * @type {string}
- * @default get
- * @static
- */
- s.GET = "GET";
-
- /**
- * The preload type for generic binary types. Note that images are loaded as binary files when using XHR.
- * @property BINARY
- * @type {String}
- * @default binary
- * @static
- * @since 0.6.0
- */
- s.BINARY = "binary";
-
- /**
- * The preload type for css files. CSS files are loaded using a <link> when loaded with XHR, or a
- * <style> tag when loaded with tags.
- * @property CSS
- * @type {String}
- * @default css
- * @static
- * @since 0.6.0
- */
- s.CSS = "css";
-
- /**
- * The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an <image> tag.
- * @property IMAGE
- * @type {String}
- * @default image
- * @static
- * @since 0.6.0
- */
- s.IMAGE = "image";
-
- /**
- * The preload type for javascript files, usually with the "js" file extension. JavaScript files are loaded into a
- * <script> tag.
- *
- * Since version 0.4.1+, due to how tag-loaded scripts work, all JavaScript files are automatically injected into
- * the body of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier,
- * only tag-loaded scripts are injected.
- * @property JAVASCRIPT
- * @type {String}
- * @default javascript
- * @static
- * @since 0.6.0
- */
- s.JAVASCRIPT = "javascript";
-
- /**
- * The preload type for json files, usually with the "json" file extension. JSON data is loaded and parsed into a
- * JavaScript object. Note that if a `callback` is present on the load item, the file will be loaded with JSONP,
- * no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to, and the JSON
- * must contain a matching wrapper function.
- * @property JSON
- * @type {String}
- * @default json
- * @static
- * @since 0.6.0
- */
- s.JSON = "json";
-
- /**
- * The preload type for jsonp files, usually with the "json" file extension. JSON data is loaded and parsed into a
- * JavaScript object. You are required to pass a callback parameter that matches the function wrapper in the JSON.
- * Note that JSONP will always be used if there is a callback present, no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}}
- * property is set to.
- * @property JSONP
- * @type {String}
- * @default jsonp
- * @static
- * @since 0.6.0
- */
- s.JSONP = "jsonp";
-
- /**
- * The preload type for json-based manifest files, usually with the "json" file extension. The JSON data is loaded
- * and parsed into a JavaScript object. PreloadJS will then look for a "manifest" property in the JSON, which is an
- * Array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
- * method. If a "callback" is specified on the manifest object, then it will be loaded using JSONP instead,
- * regardless of what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to.
- * @property MANIFEST
- * @type {String}
- * @default manifest
- * @static
- * @since 0.6.0
- */
- s.MANIFEST = "manifest";
-
- /**
- * The preload type for sound files, usually mp3, ogg, or wav. When loading via tags, audio is loaded into an
- * <audio> tag.
- * @property SOUND
- * @type {String}
- * @default sound
- * @static
- * @since 0.6.0
- */
- s.SOUND = "sound";
-
- /**
- * The preload type for video files, usually mp4, ts, or ogg. When loading via tags, video is loaded into an
- * <video> tag.
- * @property VIDEO
- * @type {String}
- * @default video
- * @static
- * @since 0.6.0
- */
- s.VIDEO = "video";
-
- /**
- * The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths.
- * @property SPRITESHEET
- * @type {String}
- * @default spritesheet
- * @static
- * @since 0.6.0
- */
- s.SPRITESHEET = "spritesheet";
-
- /**
- * The preload type for SVG files.
- * @property SVG
- * @type {String}
- * @default svg
- * @static
- * @since 0.6.0
- */
- s.SVG = "svg";
-
- /**
- * The preload type for text files, which is also the default file type if the type can not be determined. Text is
- * loaded as raw text.
- * @property TEXT
- * @type {String}
- * @default text
- * @static
- * @since 0.6.0
- */
- s.TEXT = "text";
-
- /**
- * The preload type for xml files. XML is loaded into an XML document.
- * @property XML
- * @type {String}
- * @default xml
- * @static
- * @since 0.6.0
- */
- s.XML = "xml";
-
-// Events
- /**
- * The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to
- * version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}.
- * @event progress
- * @since 0.3.0
- */
-
- /**
- * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts.
- * @event loadstart
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.3.1
- */
-
- /**
- * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded.
- * @event complete
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.3.0
- */
-
- /**
- * The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was
- * encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was
- * just a regular {{#crossLink "Event"}}{{/crossLink}}.
- * @event error
- * @since 0.3.0
- */
-
- /**
- * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error.
- * This enables loaders to maintain internal queues, and surface file load errors.
- * @event fileerror
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The even type ("fileerror")
- * @param {LoadItem|object} The item that encountered the error
- * @since 0.6.0
- */
-
- /**
- * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables
- * loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s
- * and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a
- * slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event.
- * @event fileload
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type ("fileload")
- * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
- * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
- * object will contain that value as a `src` property.
- * @param {Object} result The HTML tag or parsed result of the loaded item.
- * @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
- * to a usable object.
- * @since 0.6.0
- */
-
- /**
- * The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load.
- * This allows updates to the loader for specific loading needs, such as binary or XHR image loading.
- * @event initialize
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type ("initialize")
- * @param {AbstractLoader} loader The loader that has been initialized.
- */
-
-
- /**
- * Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was
- * passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
- * {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will
- * be a {{#crossLink "LoadItem"}}{{/crossLink}}.
- * @method getItem
- * @return {Object} The manifest item that this loader is responsible for loading.
- * @since 0.6.0
- */
- p.getItem = function () {
- return this._item;
- };
-
- /**
- * Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}}
- * event is dispatched.
- * @method getResult
- * @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded
- * data (if it exists).
- * @return {Object}
- * @since 0.6.0
- */
- p.getResult = function (raw) {
- return raw ? this._rawResult : this._result;
- };
-
- /**
- * Return the `tag` this object creates or uses for loading.
- * @method getTag
- * @return {Object} The tag instance
- * @since 0.6.0
- */
- p.getTag = function () {
- return this._tag;
- };
-
- /**
- * Set the `tag` this item uses for loading.
- * @method setTag
- * @param {Object} tag The tag instance
- * @since 0.6.0
- */
- p.setTag = function(tag) {
- this._tag = tag;
- };
-
- /**
- * Begin loading the item. This method is required when using a loader by itself.
- *
- *
Example
- *
- * var queue = new createjs.LoadQueue();
- * queue.on("complete", handleComplete);
- * queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet
- * queue.load();
- *
- * @method load
- */
- p.load = function () {
- this._createRequest();
-
- this._request.on("complete", this, this);
- this._request.on("progress", this, this);
- this._request.on("loadStart", this, this);
- this._request.on("abort", this, this);
- this._request.on("timeout", this, this);
- this._request.on("error", this, this);
-
- var evt = new createjs.Event("initialize");
- evt.loader = this._request;
- this.dispatchEvent(evt);
-
- this._request.load();
- };
-
- /**
- * Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in
- * the background), but events will not longer be dispatched.
- * @method cancel
- */
- p.cancel = function () {
- this.canceled = true;
- this.destroy();
- };
-
- /**
- * Clean up the loader.
- * @method destroy
- */
- p.destroy = function() {
- if (this._request) {
- this._request.removeAllEventListeners();
- this._request.destroy();
- }
-
- this._request = null;
-
- this._item = null;
- this._rawResult = null;
- this._result = null;
-
- this._loadItems = null;
-
- this.removeAllEventListeners();
- };
-
- /**
- * Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}}
- * to expose items it loads internally.
- * @method getLoadedItems
- * @return {Array} A list of the items loaded by the loader.
- * @since 0.6.0
- */
- p.getLoadedItems = function () {
- return this._loadedItems;
- };
-
-
- // Private methods
- /**
- * Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or
- * {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}.
- * Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}},
- * which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood.
- * @method _createRequest
- * @protected
- */
- p._createRequest = function() {
- if (!this._preferXHR) {
- this._request = new createjs.TagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
- } else {
- this._request = new createjs.XHRRequest(this._item);
- }
- };
-
- /**
- * Create the HTML tag used for loading. This method does nothing by default, and needs to be implemented
- * by loaders that require tag loading.
- * @method _createTag
- * @param {String} src The tag source
- * @return {HTMLElement} The tag that was created
- * @protected
- */
- p._createTag = function(src) { return null; };
-
- /**
- * Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}}
- * event for details on the event payload.
- * @method _sendLoadStart
- * @protected
- */
- p._sendLoadStart = function () {
- if (this._isCanceled()) { return; }
- this.dispatchEvent("loadstart");
- };
-
- /**
- * Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}.
- * @method _sendProgress
- * @param {Number | Object} value The progress of the loaded item, or an object containing loaded
- * and total properties.
- * @protected
- */
- p._sendProgress = function (value) {
- if (this._isCanceled()) { return; }
- var event = null;
- if (typeof(value) == "number") {
- this.progress = value;
- event = new createjs.ProgressEvent(this.progress);
- } else {
- event = value;
- this.progress = value.loaded / value.total;
- event.progress = this.progress;
- if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
- }
- this.hasEventListener("progress") && this.dispatchEvent(event);
- };
-
- /**
- * Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
- * @method _sendComplete
- * @protected
- */
- p._sendComplete = function () {
- if (this._isCanceled()) { return; }
-
- this.loaded = true;
-
- var event = new createjs.Event("complete");
- event.rawResult = this._rawResult;
-
- if (this._result != null) {
- event.result = this._result;
- }
-
- this.dispatchEvent(event);
- };
-
- /**
- * Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
- * event for details on the event payload.
- * @method _sendError
- * @param {ErrorEvent} event The event object containing specific error properties.
- * @protected
- */
- p._sendError = function (event) {
- if (this._isCanceled() || !this.hasEventListener("error")) { return; }
- if (event == null) {
- event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
- }
- this.dispatchEvent(event);
- };
-
- /**
- * Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
- * do not cause issues after the queue has been cleaned up.
- * @method _isCanceled
- * @return {Boolean} If the loader has been canceled.
- * @protected
- */
- p._isCanceled = function () {
- if (window.createjs == null || this.canceled) {
- return true;
- }
- return false;
- };
-
- /**
- * A custom result formatter function, which is called just before a request dispatches its complete event. Most
- * loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
- * formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
- * @property resultFormatter
- * @type Function
- * @return {Object} The formatted result
- * @since 0.6.0
- */
- p.resultFormatter = null; //TODO: Add support for async formatting.
-
- /**
- * Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but
- * this method can be overridden for custom behaviours.
- * @method handleEvent
- * @param {Event} event The event that the internal request dispatches.
- * @protected
- * @since 0.6.0
- */
- p.handleEvent = function (event) {
- switch (event.type) {
- case "complete":
- this._rawResult = event.target._response;
- var result = this.resultFormatter && this.resultFormatter(this);
- var _this = this;
- if (result instanceof Function) {
- result(function(result) {
- _this._result = result;
- _this._sendComplete();
- });
- } else {
- this._result = result || this._rawResult;
- this._sendComplete();
- }
- break;
- case "progress":
- this._sendProgress(event);
- break;
- case "error":
- this._sendError(event);
- break;
- case "loadstart":
- this._sendLoadStart();
- break;
- case "abort":
- case "timeout":
- if (!this._isCanceled()) {
- this.dispatchEvent(event.type);
- }
- break;
- }
- };
-
- /**
- * @method buildPath
- * @protected
- * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
- * instead.
- */
- p.buildPath = function (src, data) {
- return createjs.RequestUtils.buildPath(src, data);
- };
-
- /**
- * @method toString
- * @return {String} a string representation of the instance.
- */
- p.toString = function () {
- return "[PreloadJS AbstractLoader]";
- };
-
- createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher");
-
-}());
+this.createjs = this.createjs || {};
-//##############################################################################
-// AbstractMediaLoader.js
-//##############################################################################
+(function () {
+ "use strict";
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- // constructor
- /**
- * The AbstractMediaLoader is a base class that handles some of the shared methods and properties of loaders that
- * handle HTML media elements, such as Video and Audio.
- * @class AbstractMediaLoader
- * @param {LoadItem|Object} loadItem
- * @param {Boolean} preferXHR
- * @param {String} type The type of media to load. Usually "video" or "audio".
- * @extends AbstractLoader
- * @constructor
- */
- function AbstractMediaLoader(loadItem, preferXHR, type) {
- this.AbstractLoader_constructor(loadItem, preferXHR, type);
-
- // public properties
- this.resultFormatter = this._formatResult;
-
- // protected properties
- this._tagSrcAttribute = "src";
- };
-
- var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
-
- // static properties
- // public methods
- p.load = function () {
- // TagRequest will handle most of this, but Sound / Video need a few custom properties, so just handle them here.
- if (!this._tag) {
- this._tag = this._createTag(this._item.src);
- }
-
- this._tag.preload = "auto";
- this._tag.load();
-
- this.AbstractLoader_load();
- };
-
- // protected methods
- /**
- * Creates a new tag for loading if it doesn't exist yet.
- * @method _createTag
- * @private
- */
- p._createTag = function () {};
-
-
- p._createRequest = function() {
- if (!this._preferXHR) {
- this._request = new createjs.MediaTagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
- } else {
- this._request = new createjs.XHRRequest(this._item);
- }
- };
-
- /**
- * The result formatter for media files.
- * @method _formatResult
- * @param {AbstractLoader} loader
- * @returns {HTMLVideoElement|HTMLAudioElement}
- * @private
- */
- p._formatResult = function (loader) {
- this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
- this._tag.onstalled = null;
- if (this._preferXHR) {
- loader.getTag().src = loader.getResult(true);
- }
- return loader.getTag();
- };
-
- createjs.AbstractMediaLoader = createjs.promote(AbstractMediaLoader, "AbstractLoader");
-
-}());
+// constructor
+ /**
+ * The base loader, which defines all the generic methods, properties, and events. All loaders extend this class,
+ * including the {{#crossLink "LoadQueue"}}{{/crossLink}}.
+ * @class AbstractLoader
+ * @param {LoadItem|object|string} loadItem The item to be loaded.
+ * @param {Boolean} [preferXHR] Determines if the LoadItem should try and load using XHR, or take a
+ * tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the
+ * other, so this is a suggested directive.
+ * @param {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
+ * such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc.
+ * @extends EventDispatcher
+ */
+ function AbstractLoader(loadItem, preferXHR, type) {
+ this.EventDispatcher_constructor();
-//##############################################################################
-// AbstractRequest.js
-//##############################################################################
+ // public properties
+ /**
+ * If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
+ * used for loading do not pile up resulting in more than one `complete` {{#crossLink "Event"}}{{/crossLink}}.
+ * @property loaded
+ * @type {Boolean}
+ * @default false
+ */
+ this.loaded = false;
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- /**
- * A base class for actual data requests, such as {{#crossLink "XHRRequest"}}{{/crossLink}}, {{#crossLink "TagRequest"}}{{/crossLink}},
- * and {{#crossLink "MediaRequest"}}{{/crossLink}}. PreloadJS loaders will typically use a data loader under the
- * hood to get data.
- * @class AbstractRequest
- * @param {LoadItem} item
- * @constructor
- */
- var AbstractRequest = function (item) {
- this._item = item;
- };
-
- var p = createjs.extend(AbstractRequest, createjs.EventDispatcher);
-
- // public methods
- /**
- * Begin a load.
- * @method load
- */
- p.load = function() {};
-
- /**
- * Clean up a request.
- * @method destroy
- */
- p.destroy = function() {};
-
- /**
- * Cancel an in-progress request.
- * @method cancel
- */
- p.cancel = function() {};
-
- createjs.AbstractRequest = createjs.promote(AbstractRequest, "EventDispatcher");
-
-}());
+ /**
+ * Determine if the loader was canceled. Canceled loads will not fire complete events. Note that this property
+ * is readonly, so {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "LoadQueue/close"}}{{/crossLink}}
+ * instead.
+ * @property canceled
+ * @type {Boolean}
+ * @default false
+ */
+ this.canceled = false;
-//##############################################################################
-// TagRequest.js
-//##############################################################################
+ /**
+ * The current load progress (percentage) for this item. This will be a number between 0 and 1.
+ *
+ *
Example
+ *
+ * var queue = new createjs.LoadQueue();
+ * queue.loadFile("largeImage.png");
+ * queue.on("progress", function() {
+ * console.log("Progress:", queue.progress, event.progress);
+ * });
+ *
+ * @property progress
+ * @type {Number}
+ * @default 0
+ */
+ this.progress = 0;
+
+ /**
+ * The type of item this loader will load. See {{#crossLink "AbstractLoader"}}{{/crossLink}} for a full list of
+ * supported types.
+ * @property type
+ * @type {String}
+ */
+ this.type = type;
+
+ /**
+ * A formatter function that converts the loaded raw result into the final result. For example, the JSONLoader
+ * converts a string of text into a JavaScript object. Not all loaders have a resultFormatter, and this property
+ * can be overridden to provide custom formatting.
+ *
+ * Optionally, a resultFormatter can return a callback function in cases where the formatting needs to be
+ * asynchronous, such as creating a new image.
+ * @property resultFormatter
+ * @type {Function}
+ * @default null
+ */
+ this.resultFormatter = null;
+
+ // protected properties
+ /**
+ * The {{#crossLink "LoadItem"}}{{/crossLink}} this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}},
+ * but will be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}.
+ * @property _item
+ * @type {LoadItem|Object}
+ * @private
+ */
+ if (loadItem) {
+ this._item = createjs.LoadItem.create(loadItem);
+ } else {
+ this._item = null;
+ }
+
+ /**
+ * Whether the loader will try and load content using XHR (true) or HTML tags (false).
+ * @property _preferXHR
+ * @type {Boolean}
+ * @private
+ */
+ this._preferXHR = preferXHR;
+
+ /**
+ * The loaded result after it is formatted by an optional {{#crossLink "resultFormatter"}}{{/crossLink}}. For
+ * items that are not formatted, this will be the same as the {{#crossLink "_rawResult:property"}}{{/crossLink}}.
+ * The result is accessed using the {{#crossLink "getResult"}}{{/crossLink}} method.
+ * @property _result
+ * @type {Object|String}
+ * @private
+ */
+ this._result = null;
+
+ /**
+ * The loaded result before it is formatted. The rawResult is accessed using the {{#crossLink "getResult"}}{{/crossLink}}
+ * method, and passing `true`.
+ * @property _rawResult
+ * @type {Object|String}
+ * @private
+ */
+ this._rawResult = null;
+
+ /**
+ * A list of items that loaders load behind the scenes. This does not include the main item the loader is
+ * responsible for loading. Examples of loaders that have sub-items include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and
+ * {{#crossLink "ManifestLoader"}}{{/crossLink}}.
+ * @property _loadItems
+ * @type {null}
+ * @protected
+ */
+ this._loadedItems = null;
+
+ /**
+ * The attribute the items loaded using tags use for the source.
+ * @type {string}
+ * @default null
+ * @private
+ */
+ this._tagSrcAttribute = null;
+
+ /**
+ * An HTML tag (or similar) that a loader may use to load HTML content, such as images, scripts, etc.
+ * @property _tag
+ * @type {Object}
+ * @private
+ */
+ this._tag = null;
+ };
+
+ var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
+ var s = AbstractLoader;
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+ /**
+ * Defines a POST request, use for a method value when loading data.
+ * @property POST
+ * @type {string}
+ * @default post
+ * @static
+ */
+ s.POST = "POST";
+
+ /**
+ * Defines a GET request, use for a method value when loading data.
+ * @property GET
+ * @type {string}
+ * @default get
+ * @static
+ */
+ s.GET = "GET";
+
+ /**
+ * The preload type for generic binary types. Note that images are loaded as binary files when using XHR.
+ * @property BINARY
+ * @type {String}
+ * @default binary
+ * @static
+ * @since 0.6.0
+ */
+ s.BINARY = "binary";
+
+ /**
+ * The preload type for css files. CSS files are loaded using a <link> when loaded with XHR, or a
+ * <style> tag when loaded with tags.
+ * @property CSS
+ * @type {String}
+ * @default css
+ * @static
+ * @since 0.6.0
+ */
+ s.CSS = "css";
+
+ /**
+ * The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an <image> tag.
+ * @property IMAGE
+ * @type {String}
+ * @default image
+ * @static
+ * @since 0.6.0
+ */
+ s.IMAGE = "image";
+
+ /**
+ * The preload type for javascript files, usually with the "js" file extension. JavaScript files are loaded into a
+ * <script> tag.
+ *
+ * Since version 0.4.1+, due to how tag-loaded scripts work, all JavaScript files are automatically injected into
+ * the body of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier,
+ * only tag-loaded scripts are injected.
+ * @property JAVASCRIPT
+ * @type {String}
+ * @default javascript
+ * @static
+ * @since 0.6.0
+ */
+ s.JAVASCRIPT = "javascript";
+
+ /**
+ * The preload type for json files, usually with the "json" file extension. JSON data is loaded and parsed into a
+ * JavaScript object. Note that if a `callback` is present on the load item, the file will be loaded with JSONP,
+ * no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to, and the JSON
+ * must contain a matching wrapper function.
+ * @property JSON
+ * @type {String}
+ * @default json
+ * @static
+ * @since 0.6.0
+ */
+ s.JSON = "json";
+
+ /**
+ * The preload type for jsonp files, usually with the "json" file extension. JSON data is loaded and parsed into a
+ * JavaScript object. You are required to pass a callback parameter that matches the function wrapper in the JSON.
+ * Note that JSONP will always be used if there is a callback present, no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}}
+ * property is set to.
+ * @property JSONP
+ * @type {String}
+ * @default jsonp
+ * @static
+ * @since 0.6.0
+ */
+ s.JSONP = "jsonp";
+
+ /**
+ * The preload type for json-based manifest files, usually with the "json" file extension. The JSON data is loaded
+ * and parsed into a JavaScript object. PreloadJS will then look for a "manifest" property in the JSON, which is an
+ * Array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
+ * method. If a "callback" is specified on the manifest object, then it will be loaded using JSONP instead,
+ * regardless of what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to.
+ * @property MANIFEST
+ * @type {String}
+ * @default manifest
+ * @static
+ * @since 0.6.0
+ */
+ s.MANIFEST = "manifest";
+
+ /**
+ * The preload type for sound files, usually mp3, ogg, or wav. When loading via tags, audio is loaded into an
+ * <audio> tag.
+ * @property SOUND
+ * @type {String}
+ * @default sound
+ * @static
+ * @since 0.6.0
+ */
+ s.SOUND = "sound";
+
+ /**
+ * The preload type for video files, usually mp4, ts, or ogg. When loading via tags, video is loaded into an
+ * <video> tag.
+ * @property VIDEO
+ * @type {String}
+ * @default video
+ * @static
+ * @since 0.6.0
+ */
+ s.VIDEO = "video";
+
+ /**
+ * The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths.
+ * @property SPRITESHEET
+ * @type {String}
+ * @default spritesheet
+ * @static
+ * @since 0.6.0
+ */
+ s.SPRITESHEET = "spritesheet";
+
+ /**
+ * The preload type for SVG files.
+ * @property SVG
+ * @type {String}
+ * @default svg
+ * @static
+ * @since 0.6.0
+ */
+ s.SVG = "svg";
+
+ /**
+ * The preload type for text files, which is also the default file type if the type can not be determined. Text is
+ * loaded as raw text.
+ * @property TEXT
+ * @type {String}
+ * @default text
+ * @static
+ * @since 0.6.0
+ */
+ s.TEXT = "text";
+
+ /**
+ * The preload type for xml files. XML is loaded into an XML document.
+ * @property XML
+ * @type {String}
+ * @default xml
+ * @static
+ * @since 0.6.0
+ */
+ s.XML = "xml";
+
+// Events
+ /**
+ * The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to
+ * version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}.
+ * @event progress
+ * @since 0.3.0
+ */
+
+ /**
+ * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts.
+ * @event loadstart
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.3.1
+ */
+
+ /**
+ * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded.
+ * @event complete
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.3.0
+ */
+
+ /**
+ * The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was
+ * encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was
+ * just a regular {{#crossLink "Event"}}{{/crossLink}}.
+ * @event error
+ * @since 0.3.0
+ */
+
+ /**
+ * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error.
+ * This enables loaders to maintain internal queues, and surface file load errors.
+ * @event fileerror
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The even type ("fileerror")
+ * @param {LoadItem|object} The item that encountered the error
+ * @since 0.6.0
+ */
+
+ /**
+ * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables
+ * loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s
+ * and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a
+ * slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event.
+ * @event fileload
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type ("fileload")
+ * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
+ * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
+ * object will contain that value as a `src` property.
+ * @param {Object} result The HTML tag or parsed result of the loaded item.
+ * @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
+ * to a usable object.
+ * @since 0.6.0
+ */
+
+ /**
+ * The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load.
+ * This allows updates to the loader for specific loading needs, such as binary or XHR image loading.
+ * @event initialize
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type ("initialize")
+ * @param {AbstractLoader} loader The loader that has been initialized.
+ */
+
+
+ /**
+ * Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was
+ * passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
+ * {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will
+ * be a {{#crossLink "LoadItem"}}{{/crossLink}}.
+ * @method getItem
+ * @return {Object} The manifest item that this loader is responsible for loading.
+ * @since 0.6.0
+ */
+ p.getItem = function () {
+ return this._item;
+ };
+
+ /**
+ * Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}}
+ * event is dispatched.
+ * @method getResult
+ * @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded
+ * data (if it exists).
+ * @return {Object}
+ * @since 0.6.0
+ */
+ p.getResult = function (raw) {
+ return raw ? this._rawResult : this._result;
+ };
+
+ /**
+ * Return the `tag` this object creates or uses for loading.
+ * @method getTag
+ * @return {Object} The tag instance
+ * @since 0.6.0
+ */
+ p.getTag = function () {
+ return this._tag;
+ };
+
+ /**
+ * Set the `tag` this item uses for loading.
+ * @method setTag
+ * @param {Object} tag The tag instance
+ * @since 0.6.0
+ */
+ p.setTag = function(tag) {
+ this._tag = tag;
+ };
+
+ /**
+ * Begin loading the item. This method is required when using a loader by itself.
+ *
+ *
Example
+ *
+ * var queue = new createjs.LoadQueue();
+ * queue.on("complete", handleComplete);
+ * queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet
+ * queue.load();
+ *
+ * @method load
+ */
+ p.load = function () {
+ this._createRequest();
+
+ this._request.on("complete", this, this);
+ this._request.on("progress", this, this);
+ this._request.on("loadStart", this, this);
+ this._request.on("abort", this, this);
+ this._request.on("timeout", this, this);
+ this._request.on("error", this, this);
+
+ var evt = new createjs.Event("initialize");
+ evt.loader = this._request;
+ this.dispatchEvent(evt);
+
+ this._request.load();
+ };
+
+ /**
+ * Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in
+ * the background), but events will not longer be dispatched.
+ * @method cancel
+ */
+ p.cancel = function () {
+ this.canceled = true;
+ this.destroy();
+ };
+
+ /**
+ * Clean up the loader.
+ * @method destroy
+ */
+ p.destroy = function() {
+ if (this._request) {
+ this._request.removeAllEventListeners();
+ this._request.destroy();
+ }
+
+ this._request = null;
+
+ this._item = null;
+ this._rawResult = null;
+ this._result = null;
+
+ this._loadItems = null;
+
+ this.removeAllEventListeners();
+ };
+
+ /**
+ * Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}}
+ * to expose items it loads internally.
+ * @method getLoadedItems
+ * @return {Array} A list of the items loaded by the loader.
+ * @since 0.6.0
+ */
+ p.getLoadedItems = function () {
+ return this._loadedItems;
+ };
+
+
+ // Private methods
+ /**
+ * Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or
+ * {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}.
+ * Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}},
+ * which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood.
+ * @method _createRequest
+ * @protected
+ */
+ p._createRequest = function() {
+ if (!this._preferXHR) {
+ this._request = new createjs.TagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
+ } else {
+ this._request = new createjs.XHRRequest(this._item);
+ }
+ };
+
+ /**
+ * Create the HTML tag used for loading. This method does nothing by default, and needs to be implemented
+ * by loaders that require tag loading.
+ * @method _createTag
+ * @param {String} src The tag source
+ * @return {HTMLElement} The tag that was created
+ * @protected
+ */
+ p._createTag = function(src) { return null; };
+
+ /**
+ * Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}}
+ * event for details on the event payload.
+ * @method _sendLoadStart
+ * @protected
+ */
+ p._sendLoadStart = function () {
+ if (this._isCanceled()) { return; }
+ this.dispatchEvent("loadstart");
+ };
+
+ /**
+ * Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}.
+ * @method _sendProgress
+ * @param {Number | Object} value The progress of the loaded item, or an object containing loaded
+ * and total properties.
+ * @protected
+ */
+ p._sendProgress = function (value) {
+ if (this._isCanceled()) { return; }
+ var event = null;
+ if (typeof(value) == "number") {
+ this.progress = value;
+ event = new createjs.ProgressEvent(this.progress);
+ } else {
+ event = value;
+ this.progress = value.loaded / value.total;
+ event.progress = this.progress;
+ if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
+ }
+ this.hasEventListener("progress") && this.dispatchEvent(event);
+ };
+
+ /**
+ * Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
+ * @method _sendComplete
+ * @protected
+ */
+ p._sendComplete = function () {
+ if (this._isCanceled()) { return; }
+
+ this.loaded = true;
+
+ var event = new createjs.Event("complete");
+ event.rawResult = this._rawResult;
+
+ if (this._result != null) {
+ event.result = this._result;
+ }
+
+ this.dispatchEvent(event);
+ };
+
+ /**
+ * Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
+ * event for details on the event payload.
+ * @method _sendError
+ * @param {ErrorEvent} event The event object containing specific error properties.
+ * @protected
+ */
+ p._sendError = function (event) {
+ if (this._isCanceled() || !this.hasEventListener("error")) { return; }
+ if (event == null) {
+ event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
+ }
+ this.dispatchEvent(event);
+ };
+
+ /**
+ * Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
+ * do not cause issues after the queue has been cleaned up.
+ * @method _isCanceled
+ * @return {Boolean} If the loader has been canceled.
+ * @protected
+ */
+ p._isCanceled = function () {
+ if (window.createjs == null || this.canceled) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * A custom result formatter function, which is called just before a request dispatches its complete event. Most
+ * loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
+ * formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
+ * @property resultFormatter
+ * @type Function
+ * @return {Object} The formatted result
+ * @since 0.6.0
+ */
+ p.resultFormatter = null; //TODO: Add support for async formatting.
+
+ /**
+ * Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but
+ * this method can be overridden for custom behaviours.
+ * @method handleEvent
+ * @param {Event} event The event that the internal request dispatches.
+ * @protected
+ * @since 0.6.0
+ */
+ p.handleEvent = function (event) {
+ switch (event.type) {
+ case "complete":
+ this._rawResult = event.target._response;
+ var result = this.resultFormatter && this.resultFormatter(this);
+ var _this = this;
+ if (result instanceof Function) {
+ result(function(result) {
+ _this._result = result;
+ _this._sendComplete();
+ });
+ } else {
+ this._result = result || this._rawResult;
+ this._sendComplete();
+ }
+ break;
+ case "progress":
+ this._sendProgress(event);
+ break;
+ case "error":
+ this._sendError(event);
+ break;
+ case "loadstart":
+ this._sendLoadStart();
+ break;
+ case "abort":
+ case "timeout":
+ if (!this._isCanceled()) {
+ this.dispatchEvent(event.type);
+ }
+ break;
+ }
+ };
+
+ /**
+ * @method buildPath
+ * @protected
+ * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
+ * instead.
+ */
+ p.buildPath = function (src, data) {
+ return createjs.RequestUtils.buildPath(src, data);
+ };
+
+ /**
+ * @method toString
+ * @return {String} a string representation of the instance.
+ */
+ p.toString = function () {
+ return "[PreloadJS AbstractLoader]";
+ };
+
+ createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher");
+
+}());
+
+//##############################################################################
+// AbstractMediaLoader.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ // constructor
+ /**
+ * The AbstractMediaLoader is a base class that handles some of the shared methods and properties of loaders that
+ * handle HTML media elements, such as Video and Audio.
+ * @class AbstractMediaLoader
+ * @param {LoadItem|Object} loadItem
+ * @param {Boolean} preferXHR
+ * @param {String} type The type of media to load. Usually "video" or "audio".
+ * @extends AbstractLoader
+ * @constructor
+ */
+ function AbstractMediaLoader(loadItem, preferXHR, type) {
+ this.AbstractLoader_constructor(loadItem, preferXHR, type);
+
+ // public properties
+ this.resultFormatter = this._formatResult;
+
+ // protected properties
+ this._tagSrcAttribute = "src";
+ };
+
+ var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
+
+ // static properties
+ // public methods
+ p.load = function () {
+ // TagRequest will handle most of this, but Sound / Video need a few custom properties, so just handle them here.
+ if (!this._tag) {
+ this._tag = this._createTag(this._item.src);
+ }
+
+ this._tag.preload = "auto";
+ this._tag.load();
+
+ this.AbstractLoader_load();
+ };
+
+ // protected methods
+ /**
+ * Creates a new tag for loading if it doesn't exist yet.
+ * @method _createTag
+ * @private
+ */
+ p._createTag = function () {};
+
+
+ p._createRequest = function() {
+ if (!this._preferXHR) {
+ this._request = new createjs.MediaTagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
+ } else {
+ this._request = new createjs.XHRRequest(this._item);
+ }
+ };
+
+ /**
+ * The result formatter for media files.
+ * @method _formatResult
+ * @param {AbstractLoader} loader
+ * @returns {HTMLVideoElement|HTMLAudioElement}
+ * @private
+ */
+ p._formatResult = function (loader) {
+ this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
+ this._tag.onstalled = null;
+ if (this._preferXHR) {
+ loader.getTag().src = loader.getResult(true);
+ }
+ return loader.getTag();
+ };
+
+ createjs.AbstractMediaLoader = createjs.promote(AbstractMediaLoader, "AbstractLoader");
+
+}());
+
+//##############################################################################
+// AbstractRequest.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * A base class for actual data requests, such as {{#crossLink "XHRRequest"}}{{/crossLink}}, {{#crossLink "TagRequest"}}{{/crossLink}},
+ * and {{#crossLink "MediaRequest"}}{{/crossLink}}. PreloadJS loaders will typically use a data loader under the
+ * hood to get data.
+ * @class AbstractRequest
+ * @param {LoadItem} item
+ * @constructor
+ */
+ var AbstractRequest = function (item) {
+ this._item = item;
+ };
+
+ var p = createjs.extend(AbstractRequest, createjs.EventDispatcher);
+
+ // public methods
+ /**
+ * Begin a load.
+ * @method load
+ */
+ p.load = function() {};
+
+ /**
+ * Clean up a request.
+ * @method destroy
+ */
+ p.destroy = function() {};
+
+ /**
+ * Cancel an in-progress request.
+ * @method cancel
+ */
+ p.cancel = function() {};
+
+ createjs.AbstractRequest = createjs.promote(AbstractRequest, "EventDispatcher");
+
+}());
+
+//##############################################################################
+// TagRequest.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ // constructor
+ /**
+ * An {{#crossLink "AbstractRequest"}}{{/crossLink}} that loads HTML tags, such as images and scripts.
+ * @class TagRequest
+ * @param {LoadItem} loadItem
+ * @param {HTMLElement} tag
+ * @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
+ */
+ function TagRequest(loadItem, tag, srcAttribute) {
+ this.AbstractRequest_constructor(loadItem);
+
+ // protected properties
+ /**
+ * The HTML tag instance that is used to load.
+ * @property _tag
+ * @type {HTMLElement}
+ * @protected
+ */
+ this._tag = tag;
+
+ /**
+ * The tag attribute that specifies the source, such as "src", "href", etc.
+ * @property _tagSrcAttribute
+ * @type {String}
+ * @protected
+ */
+ this._tagSrcAttribute = srcAttribute;
+
+ /**
+ * A method closure used for handling the tag load event.
+ * @property _loadedHandler
+ * @type {Function}
+ * @private
+ */
+ this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
+
+ /**
+ * Determines if the element was added to the DOM automatically by PreloadJS, so it can be cleaned up after.
+ * @property _addedToDOM
+ * @type {Boolean}
+ * @private
+ */
+ this._addedToDOM = false;
+
+ /**
+ * Determines what the tags initial style.visibility was, so we can set it correctly after a load.
+ *
+ * @type {null}
+ * @private
+ */
+ this._startTagVisibility = null;
+ };
+
+ var p = createjs.extend(TagRequest, createjs.AbstractRequest);
+
+ // public methods
+ p.load = function () {
+ this._tag.onload = createjs.proxy(this._handleTagComplete, this);
+ this._tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this);
+ this._tag.onerror = createjs.proxy(this._handleError, this);
+
+ var evt = new createjs.Event("initialize");
+ evt.loader = this._tag;
+
+ this.dispatchEvent(evt);
+
+ this._hideTag();
+
+ this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
+
+ this._tag[this._tagSrcAttribute] = this._item.src;
+
+ // wdg:: Append the tag AFTER setting the src, or SVG loading on iOS will fail.
+ if (this._tag.parentNode == null) {
+ window.document.body.appendChild(this._tag);
+ this._addedToDOM = true;
+ }
+ };
+
+ p.destroy = function() {
+ this._clean();
+ this._tag = null;
+
+ this.AbstractRequest_destroy();
+ };
+
+ // private methods
+ /**
+ * Handle the readyStateChange event from a tag. We need this in place of the `onload` callback (mainly SCRIPT
+ * and LINK tags), but other cases may exist.
+ * @method _handleReadyStateChange
+ * @private
+ */
+ p._handleReadyStateChange = function () {
+ clearTimeout(this._loadTimeout);
+ // This is strictly for tags in browsers that do not support onload.
+ var tag = this._tag;
+
+ // Complete is for old IE support.
+ if (tag.readyState == "loaded" || tag.readyState == "complete") {
+ this._handleTagComplete();
+ }
+ };
+
+ /**
+ * Handle any error events from the tag.
+ * @method _handleError
+ * @protected
+ */
+ p._handleError = function() {
+ this._clean();
+ this.dispatchEvent("error");
+ };
+
+ /**
+ * Handle the tag's onload callback.
+ * @method _handleTagComplete
+ * @private
+ */
+ p._handleTagComplete = function () {
+ this._rawResult = this._tag;
+ this._result = this.resultFormatter && this.resultFormatter(this) || this._rawResult;
+
+ this._clean();
+ this._showTag();
+
+ this.dispatchEvent("complete");
+ };
+
+ /**
+ * The tag request has not loaded within the time specified in loadTimeout.
+ * @method _handleError
+ * @param {Object} event The XHR error event.
+ * @private
+ */
+ p._handleTimeout = function () {
+ this._clean();
+ this.dispatchEvent(new createjs.Event("timeout"));
+ };
+
+ /**
+ * Remove event listeners, but don't destroy the request object
+ * @method _clean
+ * @private
+ */
+ p._clean = function() {
+ this._tag.onload = null;
+ this._tag.onreadystatechange = null;
+ this._tag.onerror = null;
+ if (this._addedToDOM && this._tag.parentNode != null) {
+ this._tag.parentNode.removeChild(this._tag);
+ }
+ clearTimeout(this._loadTimeout);
+ };
+
+ p._hideTag = function() {
+ this._startTagVisibility = this._tag.style.visibility;
+ this._tag.style.visibility = "hidden";
+ };
+
+ p._showTag = function() {
+ this._tag.style.visibility = this._startTagVisibility;
+ };
+
+ /**
+ * Handle a stalled audio event. The main place this happens is with HTMLAudio in Chrome when playing back audio
+ * that is already in a load, but not complete.
+ * @method _handleStalled
+ * @private
+ */
+ p._handleStalled = function () {
+ //Ignore, let the timeout take care of it. Sometimes its not really stopped.
+ };
+
+ createjs.TagRequest = createjs.promote(TagRequest, "AbstractRequest");
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- // constructor
- /**
- * An {{#crossLink "AbstractRequest"}}{{/crossLink}} that loads HTML tags, such as images and scripts.
- * @class TagRequest
- * @param {LoadItem} loadItem
- * @param {HTMLElement} tag
- * @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
- */
- function TagRequest(loadItem, tag, srcAttribute) {
- this.AbstractRequest_constructor(loadItem);
-
- // protected properties
- /**
- * The HTML tag instance that is used to load.
- * @property _tag
- * @type {HTMLElement}
- * @protected
- */
- this._tag = tag;
-
- /**
- * The tag attribute that specifies the source, such as "src", "href", etc.
- * @property _tagSrcAttribute
- * @type {String}
- * @protected
- */
- this._tagSrcAttribute = srcAttribute;
-
- /**
- * A method closure used for handling the tag load event.
- * @property _loadedHandler
- * @type {Function}
- * @private
- */
- this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
-
- /**
- * Determines if the element was added to the DOM automatically by PreloadJS, so it can be cleaned up after.
- * @property _addedToDOM
- * @type {Boolean}
- * @private
- */
- this._addedToDOM = false;
-
- /**
- * Determines what the tags initial style.visibility was, so we can set it correctly after a load.
- *
- * @type {null}
- * @private
- */
- this._startTagVisibility = null;
- };
-
- var p = createjs.extend(TagRequest, createjs.AbstractRequest);
-
- // public methods
- p.load = function () {
- this._tag.onload = createjs.proxy(this._handleTagComplete, this);
- this._tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this);
- this._tag.onerror = createjs.proxy(this._handleError, this);
-
- var evt = new createjs.Event("initialize");
- evt.loader = this._tag;
-
- this.dispatchEvent(evt);
-
- this._hideTag();
-
- this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
-
- this._tag[this._tagSrcAttribute] = this._item.src;
-
- // wdg:: Append the tag AFTER setting the src, or SVG loading on iOS will fail.
- if (this._tag.parentNode == null) {
- window.document.body.appendChild(this._tag);
- this._addedToDOM = true;
- }
- };
-
- p.destroy = function() {
- this._clean();
- this._tag = null;
-
- this.AbstractRequest_destroy();
- };
-
- // private methods
- /**
- * Handle the readyStateChange event from a tag. We need this in place of the `onload` callback (mainly SCRIPT
- * and LINK tags), but other cases may exist.
- * @method _handleReadyStateChange
- * @private
- */
- p._handleReadyStateChange = function () {
- clearTimeout(this._loadTimeout);
- // This is strictly for tags in browsers that do not support onload.
- var tag = this._tag;
-
- // Complete is for old IE support.
- if (tag.readyState == "loaded" || tag.readyState == "complete") {
- this._handleTagComplete();
- }
- };
-
- /**
- * Handle any error events from the tag.
- * @method _handleError
- * @protected
- */
- p._handleError = function() {
- this._clean();
- this.dispatchEvent("error");
- };
-
- /**
- * Handle the tag's onload callback.
- * @method _handleTagComplete
- * @private
- */
- p._handleTagComplete = function () {
- this._rawResult = this._tag;
- this._result = this.resultFormatter && this.resultFormatter(this) || this._rawResult;
-
- this._clean();
- this._showTag();
-
- this.dispatchEvent("complete");
- };
-
- /**
- * The tag request has not loaded within the time specified in loadTimeout.
- * @method _handleError
- * @param {Object} event The XHR error event.
- * @private
- */
- p._handleTimeout = function () {
- this._clean();
- this.dispatchEvent(new createjs.Event("timeout"));
- };
-
- /**
- * Remove event listeners, but don't destroy the request object
- * @method _clean
- * @private
- */
- p._clean = function() {
- this._tag.onload = null;
- this._tag.onreadystatechange = null;
- this._tag.onerror = null;
- if (this._addedToDOM && this._tag.parentNode != null) {
- this._tag.parentNode.removeChild(this._tag);
- }
- clearTimeout(this._loadTimeout);
- };
-
- p._hideTag = function() {
- this._startTagVisibility = this._tag.style.visibility;
- this._tag.style.visibility = "hidden";
- };
-
- p._showTag = function() {
- this._tag.style.visibility = this._startTagVisibility;
- };
-
- /**
- * Handle a stalled audio event. The main place this happens is with HTMLAudio in Chrome when playing back audio
- * that is already in a load, but not complete.
- * @method _handleStalled
- * @private
- */
- p._handleStalled = function () {
- //Ignore, let the timeout take care of it. Sometimes its not really stopped.
- };
-
- createjs.TagRequest = createjs.promote(TagRequest, "AbstractRequest");
-
}());
//##############################################################################
// MediaTagRequest.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- // constructor
- /**
- * An {{#crossLink "TagRequest"}}{{/crossLink}} that loads HTML tags for video and audio.
- * @class MediaTagRequest
- * @param {LoadItem} loadItem
- * @param {HTMLAudioElement|HTMLVideoElement} tag
- * @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
- * @constructor
- */
- function MediaTagRequest(loadItem, tag, srcAttribute) {
- this.AbstractRequest_constructor(loadItem);
-
- // protected properties
- this._tag = tag;
- this._tagSrcAttribute = srcAttribute;
- this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
- };
-
- var p = createjs.extend(MediaTagRequest, createjs.TagRequest);
- var s = MediaTagRequest;
-
- // public methods
- p.load = function () {
- var sc = createjs.proxy(this._handleStalled, this);
- this._stalledCallback = sc;
-
- var pc = createjs.proxy(this._handleProgress, this);
- this._handleProgress = pc;
-
- this._tag.addEventListener("stalled", sc);
- this._tag.addEventListener("progress", pc);
-
- // This will tell us when audio is buffered enough to play through, but not when its loaded.
- // The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient.
- this._tag.addEventListener && this._tag.addEventListener("canplaythrough", this._loadedHandler, false); // canplaythrough callback doesn't work in Chrome, so we use an event.
-
- this.TagRequest_load();
- };
-
- // private methods
- p._handleReadyStateChange = function () {
- clearTimeout(this._loadTimeout);
- // This is strictly for tags in browsers that do not support onload.
- var tag = this._tag;
-
- // Complete is for old IE support.
- if (tag.readyState == "loaded" || tag.readyState == "complete") {
- this._handleTagComplete();
- }
- };
-
- p._handleStalled = function () {
- //Ignore, let the timeout take care of it. Sometimes its not really stopped.
- };
-
- /**
- * An XHR request has reported progress.
- * @method _handleProgress
- * @param {Object} event The XHR progress event.
- * @private
- */
- p._handleProgress = function (event) {
- if (!event || event.loaded > 0 && event.total == 0) {
- return; // Sometimes we get no "total", so just ignore the progress event.
- }
-
- var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
- this.dispatchEvent(newEvent);
- };
-
- // protected methods
- p._clean = function () {
- this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
- this._tag.removeEventListener("stalled", this._stalledCallback);
- this._tag.removeEventListener("progress", this._progressCallback);
-
- this.TagRequest__clean();
- };
-
- createjs.MediaTagRequest = createjs.promote(MediaTagRequest, "TagRequest");
-
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ // constructor
+ /**
+ * An {{#crossLink "TagRequest"}}{{/crossLink}} that loads HTML tags for video and audio.
+ * @class MediaTagRequest
+ * @param {LoadItem} loadItem
+ * @param {HTMLAudioElement|HTMLVideoElement} tag
+ * @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
+ * @constructor
+ */
+ function MediaTagRequest(loadItem, tag, srcAttribute) {
+ this.AbstractRequest_constructor(loadItem);
+
+ // protected properties
+ this._tag = tag;
+ this._tagSrcAttribute = srcAttribute;
+ this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
+ };
+
+ var p = createjs.extend(MediaTagRequest, createjs.TagRequest);
+ var s = MediaTagRequest;
+
+ // public methods
+ p.load = function () {
+ var sc = createjs.proxy(this._handleStalled, this);
+ this._stalledCallback = sc;
+
+ var pc = createjs.proxy(this._handleProgress, this);
+ this._handleProgress = pc;
+
+ this._tag.addEventListener("stalled", sc);
+ this._tag.addEventListener("progress", pc);
+
+ // This will tell us when audio is buffered enough to play through, but not when its loaded.
+ // The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient.
+ this._tag.addEventListener && this._tag.addEventListener("canplaythrough", this._loadedHandler, false); // canplaythrough callback doesn't work in Chrome, so we use an event.
+
+ this.TagRequest_load();
+ };
+
+ // private methods
+ p._handleReadyStateChange = function () {
+ clearTimeout(this._loadTimeout);
+ // This is strictly for tags in browsers that do not support onload.
+ var tag = this._tag;
+
+ // Complete is for old IE support.
+ if (tag.readyState == "loaded" || tag.readyState == "complete") {
+ this._handleTagComplete();
+ }
+ };
+
+ p._handleStalled = function () {
+ //Ignore, let the timeout take care of it. Sometimes its not really stopped.
+ };
+
+ /**
+ * An XHR request has reported progress.
+ * @method _handleProgress
+ * @param {Object} event The XHR progress event.
+ * @private
+ */
+ p._handleProgress = function (event) {
+ if (!event || event.loaded > 0 && event.total == 0) {
+ return; // Sometimes we get no "total", so just ignore the progress event.
+ }
+
+ var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
+ this.dispatchEvent(newEvent);
+ };
+
+ // protected methods
+ p._clean = function () {
+ this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
+ this._tag.removeEventListener("stalled", this._stalledCallback);
+ this._tag.removeEventListener("progress", this._progressCallback);
+
+ this.TagRequest__clean();
+ };
+
+ createjs.MediaTagRequest = createjs.promote(MediaTagRequest, "TagRequest");
+
}());
//##############################################################################
// XHRRequest.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
-// constructor
- /**
- * A preloader that loads items using XHR requests, usually XMLHttpRequest. However XDomainRequests will be used
- * for cross-domain requests if possible, and older versions of IE fall back on to ActiveX objects when necessary.
- * XHR requests load the content as text or binary data, provide progress and consistent completion events, and
- * can be canceled during load. Note that XHR is not supported in IE 6 or earlier, and is not recommended for
- * cross-domain loading.
- * @class XHRRequest
- * @constructor
- * @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
- * for an overview of supported file properties.
- * @extends AbstractLoader
- */
- function XHRRequest(item) {
- this.AbstractRequest_constructor(item);
-
- // protected properties
- /**
- * A reference to the XHR request used to load the content.
- * @property _request
- * @type {XMLHttpRequest | XDomainRequest | ActiveX.XMLHTTP}
- * @private
- */
- this._request = null;
-
- /**
- * A manual load timeout that is used for browsers that do not support the onTimeout event on XHR (XHR level 1,
- * typically IE9).
- * @property _loadTimeout
- * @type {Number}
- * @private
- */
- this._loadTimeout = null;
-
- /**
- * The browser's XHR (XMLHTTPRequest) version. Supported versions are 1 and 2. There is no official way to detect
- * the version, so we use capabilities to make a best guess.
- * @property _xhrLevel
- * @type {Number}
- * @default 1
- * @private
- */
- this._xhrLevel = 1;
-
- /**
- * The response of a loaded file. This is set because it is expensive to look up constantly. This property will be
- * null until the file is loaded.
- * @property _response
- * @type {mixed}
- * @private
- */
- this._response = null;
-
- /**
- * The response of the loaded file before it is modified. In most cases, content is converted from raw text to
- * an HTML tag or a formatted object which is set to the result property, but the developer may still
- * want to access the raw content as it was loaded.
- * @property _rawResponse
- * @type {String|Object}
- * @private
- */
- this._rawResponse = null;
-
- this._canceled = false;
-
- // Setup our event handlers now.
- this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this);
- this._handleProgressProxy = createjs.proxy(this._handleProgress, this);
- this._handleAbortProxy = createjs.proxy(this._handleAbort, this);
- this._handleErrorProxy = createjs.proxy(this._handleError, this);
- this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this);
- this._handleLoadProxy = createjs.proxy(this._handleLoad, this);
- this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this);
-
- if (!this._createXHR(item)) {
- //TODO: Throw error?
- }
- };
-
- var p = createjs.extend(XHRRequest, createjs.AbstractRequest);
-
-// static properties
- /**
- * A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE.
- * @property ACTIVEX_VERSIONS
- * @type {Array}
- * @since 0.4.2
- * @private
- */
- XHRRequest.ACTIVEX_VERSIONS = [
- "Msxml2.XMLHTTP.6.0",
- "Msxml2.XMLHTTP.5.0",
- "Msxml2.XMLHTTP.4.0",
- "MSXML2.XMLHTTP.3.0",
- "MSXML2.XMLHTTP",
- "Microsoft.XMLHTTP"
- ];
-
-// Public methods
- /**
- * Look up the loaded result.
- * @method getResult
- * @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content
- * loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
- * returned instead.
- * @return {Object} A result object containing the content that was loaded, such as:
- *
- *
An image tag (<image />) for images
- *
A script tag for JavaScript (<script />). Note that scripts loaded with tags may be added to the
- * HTML head.
- *
A style tag for CSS (<style />)
- *
Raw text for TEXT
- *
A formatted JavaScript object defined by JSON
- *
An XML document
- *
An binary arraybuffer loaded by XHR
- *
- * Note that if a raw result is requested, but not found, the result will be returned instead.
- */
- p.getResult = function (raw) {
- if (raw && this._rawResponse) {
- return this._rawResponse;
- }
- return this._response;
- };
-
- // Overrides abstract method in AbstractRequest
- p.cancel = function () {
- this.canceled = true;
- this._clean();
- this._request.abort();
- };
-
- // Overrides abstract method in AbstractLoader
- p.load = function () {
- if (this._request == null) {
- this._handleError();
- return;
- }
-
- //Events
- this._request.addEventListener("loadstart", this._handleLoadStartProxy, false);
- this._request.addEventListener("progress", this._handleProgressProxy, false);
- this._request.addEventListener("abort", this._handleAbortProxy, false);
- this._request.addEventListener("error",this._handleErrorProxy, false);
- this._request.addEventListener("timeout", this._handleTimeoutProxy, false);
-
- // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
- this._request.addEventListener("load", this._handleLoadProxy, false);
- this._request.addEventListener("readystatechange", this._handleReadyStateChangeProxy, false);
-
- // Set up a timeout if we don't have XHR2
- if (this._xhrLevel == 1) {
- this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
- }
-
- // Sometimes we get back 404s immediately, particularly when there is a cross origin request. // note this does not catch in Chrome
- try {
- if (!this._item.values || this._item.method == createjs.AbstractLoader.GET) {
- this._request.send();
- } else if (this._item.method == createjs.AbstractLoader.POST) {
- this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
- }
- } catch (error) {
- this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
- }
- };
-
- p.setResponseType = function (type) {
- this._request.responseType = type;
- };
-
- /**
- * Get all the response headers from the XmlHttpRequest.
- *
- * From the docs: Return all the HTTP headers, excluding headers that are a case-insensitive match
- * for Set-Cookie or Set-Cookie2, as a single string, with each header line separated by a U+000D CR U+000A LF pair,
- * excluding the status line, and with each header name and header value separated by a U+003A COLON U+0020 SPACE
- * pair.
- * @method getAllResponseHeaders
- * @return {String}
- * @since 0.4.1
- */
- p.getAllResponseHeaders = function () {
- if (this._request.getAllResponseHeaders instanceof Function) {
- return this._request.getAllResponseHeaders();
- } else {
- return null;
- }
- };
-
- /**
- * Get a specific response header from the XmlHttpRequest.
- *
- * From the docs: Returns the header field value from the response of which the field name matches
- * header, unless the field name is Set-Cookie or Set-Cookie2.
- * @method getResponseHeader
- * @param {String} header The header name to retrieve.
- * @return {String}
- * @since 0.4.1
- */
- p.getResponseHeader = function (header) {
- if (this._request.getResponseHeader instanceof Function) {
- return this._request.getResponseHeader(header);
- } else {
- return null;
- }
- };
-
-// protected methods
- /**
- * The XHR request has reported progress.
- * @method _handleProgress
- * @param {Object} event The XHR progress event.
- * @private
- */
- p._handleProgress = function (event) {
- if (!event || event.loaded > 0 && event.total == 0) {
- return; // Sometimes we get no "total", so just ignore the progress event.
- }
-
- var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
- this.dispatchEvent(newEvent);
- };
-
- /**
- * The XHR request has reported a load start.
- * @method _handleLoadStart
- * @param {Object} event The XHR loadStart event.
- * @private
- */
- p._handleLoadStart = function (event) {
- clearTimeout(this._loadTimeout);
- this.dispatchEvent("loadstart");
- };
-
- /**
- * The XHR request has reported an abort event.
- * @method handleAbort
- * @param {Object} event The XHR abort event.
- * @private
- */
- p._handleAbort = function (event) {
- this._clean();
- this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED", null, event));
- };
-
- /**
- * The XHR request has reported an error event.
- * @method _handleError
- * @param {Object} event The XHR error event.
- * @private
- */
- p._handleError = function (event) {
- this._clean();
- this.dispatchEvent(new createjs.ErrorEvent(event.message));
- };
-
- /**
- * The XHR request has reported a readyState change. Note that older browsers (IE 7 & 8) do not provide an onload
- * event, so we must monitor the readyStateChange to determine if the file is loaded.
- * @method _handleReadyStateChange
- * @param {Object} event The XHR readyStateChange event.
- * @private
- */
- p._handleReadyStateChange = function (event) {
- if (this._request.readyState == 4) {
- this._handleLoad();
- }
- };
-
- /**
- * The XHR request has completed. This is called by the XHR request directly, or by a readyStateChange that has
- * request.readyState == 4. Only the first call to this method will be processed.
- * @method _handleLoad
- * @param {Object} event The XHR load event.
- * @private
- */
- p._handleLoad = function (event) {
- if (this.loaded) {
- return;
- }
- this.loaded = true;
-
- var error = this._checkError();
- if (error) {
- this._handleError(error);
- return;
- }
-
- this._response = this._getResponse();
- this._clean();
-
- this.dispatchEvent(new createjs.Event("complete"));
- };
-
- /**
- * The XHR request has timed out. This is called by the XHR request directly, or via a setTimeout
- * callback.
- * @method _handleTimeout
- * @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout.
- * @private
- */
- p._handleTimeout = function (event) {
- this._clean();
-
- this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
- };
-
-// Protected
- /**
- * Determine if there is an error in the current load. This checks the status of the request for problem codes. Note
- * that this does not check for an actual response. Currently, it only checks for 404 or 0 error code.
- * @method _checkError
- * @return {int} If the request status returns an error code.
- * @private
- */
- p._checkError = function () {
- //LM: Probably need additional handlers here, maybe 501
- var status = parseInt(this._request.status);
-
- switch (status) {
- case 404: // Not Found
- case 0: // Not Loaded
- return new Error(status);
- }
- return null;
- };
-
- /**
- * Validate the response. Different browsers have different approaches, some of which throw errors when accessed
- * in other browsers. If there is no response, the _response property will remain null.
- * @method _getResponse
- * @private
- */
- p._getResponse = function () {
- if (this._response != null) {
- return this._response;
- }
-
- if (this._request.response != null) {
- return this._request.response;
- }
-
- // Android 2.2 uses .responseText
- try {
- if (this._request.responseText != null) {
- return this._request.responseText;
- }
- } catch (e) {
- }
-
- // When loading XML, IE9 does not return .response, instead it returns responseXML.xml
- try {
- if (this._request.responseXML != null) {
- return this._request.responseXML;
- }
- } catch (e) {
- }
-
- return null;
- };
-
- /**
- * Create an XHR request. Depending on a number of factors, we get totally different results.
- *
Some browsers get an XDomainRequest when loading cross-domain.
- *
XMLHttpRequest are created when available.
- *
ActiveX.XMLHTTP objects are used in older IE browsers.
- *
Text requests override the mime type if possible
- *
Origin headers are sent for crossdomain requests in some browsers.
- *
Binary loads set the response type to "arraybuffer"
- * @method _createXHR
- * @param {Object} item The requested item that is being loaded.
- * @return {Boolean} If an XHR request or equivalent was successfully created.
- * @private
- */
- p._createXHR = function (item) {
- // Check for cross-domain loads. We can't fully support them, but we can try.
- var crossdomain = createjs.RequestUtils.isCrossDomain(item);
- var headers = {};
-
- // Create the request. Fallback to whatever support we have.
- var req = null;
- if (window.XMLHttpRequest) {
- req = new XMLHttpRequest();
- // This is 8 or 9, so use XDomainRequest instead.
- if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) {
- req = new XDomainRequest();
- }
- } else { // Old IE versions use a different approach
- for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
- var axVersion = s.ACTIVEX_VERSIONS[i];
- try {
- req = new ActiveXObject(axVersions);
- break;
- } catch (e) {}
- }
- if (req == null) { return false; }
- }
-
- // Default to utf-8 for Text requests.
- if (item.mimeType == null && createjs.RequestUtils.isText(item.type)) {
- item.mimeType = "text/plain; charset=utf-8";
- }
-
- // IE9 doesn't support overrideMimeType(), so we need to check for it.
- if (item.mimeType && req.overrideMimeType) {
- req.overrideMimeType(item.mimeType);
- }
-
- // Determine the XHR level
- this._xhrLevel = (typeof req.responseType === "string") ? 2 : 1;
-
- var src = null;
- if (item.method == createjs.AbstractLoader.GET) {
- src = createjs.RequestUtils.buildPath(item.src, item.values);
- } else {
- src = item.src;
- }
-
- // Open the request. Set cross-domain flags if it is supported (XHR level 1 only)
- req.open(item.method || createjs.AbstractLoader.GET, src, true);
-
- if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) {
- headers["Origin"] = location.origin;
- }
-
- // To send data we need to set the Content-type header)
- if (item.values && item.method == createjs.AbstractLoader.POST) {
- headers["Content-Type"] = "application/x-www-form-urlencoded";
- }
-
- if (!crossdomain && !headers["X-Requested-With"]) {
- headers["X-Requested-With"] = "XMLHttpRequest";
- }
-
- if (item.headers) {
- for (var n in item.headers) {
- headers[n] = item.headers[n];
- }
- }
-
- for (n in headers) {
- req.setRequestHeader(n, headers[n])
- }
-
- if (req instanceof XMLHttpRequest && item.withCredentials !== undefined) {
- req.withCredentials = item.withCredentials;
- }
-
- this._request = req;
-
- return true;
- };
-
- /**
- * A request has completed (or failed or canceled), and needs to be disposed.
- * @method _clean
- * @private
- */
- p._clean = function () {
- clearTimeout(this._loadTimeout);
-
- this._request.removeEventListener("loadstart", this._handleLoadStartProxy);
- this._request.removeEventListener("progress", this._handleProgressProxy);
- this._request.removeEventListener("abort", this._handleAbortProxy);
- this._request.removeEventListener("error",this._handleErrorProxy);
- this._request.removeEventListener("timeout", this._handleTimeoutProxy);
- this._request.removeEventListener("load", this._handleLoadProxy);
- this._request.removeEventListener("readystatechange", this._handleReadyStateChangeProxy);
- };
-
- p.toString = function () {
- return "[PreloadJS XHRRequest]";
- };
-
- createjs.XHRRequest = createjs.promote(XHRRequest, "AbstractRequest");
-
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+// constructor
+ /**
+ * A preloader that loads items using XHR requests, usually XMLHttpRequest. However XDomainRequests will be used
+ * for cross-domain requests if possible, and older versions of IE fall back on to ActiveX objects when necessary.
+ * XHR requests load the content as text or binary data, provide progress and consistent completion events, and
+ * can be canceled during load. Note that XHR is not supported in IE 6 or earlier, and is not recommended for
+ * cross-domain loading.
+ * @class XHRRequest
+ * @constructor
+ * @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
+ * for an overview of supported file properties.
+ * @extends AbstractLoader
+ */
+ function XHRRequest(item) {
+ this.AbstractRequest_constructor(item);
+
+ // protected properties
+ /**
+ * A reference to the XHR request used to load the content.
+ * @property _request
+ * @type {XMLHttpRequest | XDomainRequest | ActiveX.XMLHTTP}
+ * @private
+ */
+ this._request = null;
+
+ /**
+ * A manual load timeout that is used for browsers that do not support the onTimeout event on XHR (XHR level 1,
+ * typically IE9).
+ * @property _loadTimeout
+ * @type {Number}
+ * @private
+ */
+ this._loadTimeout = null;
+
+ /**
+ * The browser's XHR (XMLHTTPRequest) version. Supported versions are 1 and 2. There is no official way to detect
+ * the version, so we use capabilities to make a best guess.
+ * @property _xhrLevel
+ * @type {Number}
+ * @default 1
+ * @private
+ */
+ this._xhrLevel = 1;
+
+ /**
+ * The response of a loaded file. This is set because it is expensive to look up constantly. This property will be
+ * null until the file is loaded.
+ * @property _response
+ * @type {mixed}
+ * @private
+ */
+ this._response = null;
+
+ /**
+ * The response of the loaded file before it is modified. In most cases, content is converted from raw text to
+ * an HTML tag or a formatted object which is set to the result property, but the developer may still
+ * want to access the raw content as it was loaded.
+ * @property _rawResponse
+ * @type {String|Object}
+ * @private
+ */
+ this._rawResponse = null;
+
+ this._canceled = false;
+
+ // Setup our event handlers now.
+ this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this);
+ this._handleProgressProxy = createjs.proxy(this._handleProgress, this);
+ this._handleAbortProxy = createjs.proxy(this._handleAbort, this);
+ this._handleErrorProxy = createjs.proxy(this._handleError, this);
+ this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this);
+ this._handleLoadProxy = createjs.proxy(this._handleLoad, this);
+ this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this);
+
+ if (!this._createXHR(item)) {
+ //TODO: Throw error?
+ }
+ };
+
+ var p = createjs.extend(XHRRequest, createjs.AbstractRequest);
+
+// static properties
+ /**
+ * A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE.
+ * @property ACTIVEX_VERSIONS
+ * @type {Array}
+ * @since 0.4.2
+ * @private
+ */
+ XHRRequest.ACTIVEX_VERSIONS = [
+ "Msxml2.XMLHTTP.6.0",
+ "Msxml2.XMLHTTP.5.0",
+ "Msxml2.XMLHTTP.4.0",
+ "MSXML2.XMLHTTP.3.0",
+ "MSXML2.XMLHTTP",
+ "Microsoft.XMLHTTP"
+ ];
+
+// Public methods
+ /**
+ * Look up the loaded result.
+ * @method getResult
+ * @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content
+ * loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
+ * returned instead.
+ * @return {Object} A result object containing the content that was loaded, such as:
+ *
+ *
An image tag (<image />) for images
+ *
A script tag for JavaScript (<script />). Note that scripts loaded with tags may be added to the
+ * HTML head.
+ *
A style tag for CSS (<style />)
+ *
Raw text for TEXT
+ *
A formatted JavaScript object defined by JSON
+ *
An XML document
+ *
An binary arraybuffer loaded by XHR
+ *
+ * Note that if a raw result is requested, but not found, the result will be returned instead.
+ */
+ p.getResult = function (raw) {
+ if (raw && this._rawResponse) {
+ return this._rawResponse;
+ }
+ return this._response;
+ };
+
+ // Overrides abstract method in AbstractRequest
+ p.cancel = function () {
+ this.canceled = true;
+ this._clean();
+ this._request.abort();
+ };
+
+ // Overrides abstract method in AbstractLoader
+ p.load = function () {
+ if (this._request == null) {
+ this._handleError();
+ return;
+ }
+
+ //Events
+ this._request.addEventListener("loadstart", this._handleLoadStartProxy, false);
+ this._request.addEventListener("progress", this._handleProgressProxy, false);
+ this._request.addEventListener("abort", this._handleAbortProxy, false);
+ this._request.addEventListener("error",this._handleErrorProxy, false);
+ this._request.addEventListener("timeout", this._handleTimeoutProxy, false);
+
+ // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
+ this._request.addEventListener("load", this._handleLoadProxy, false);
+ this._request.addEventListener("readystatechange", this._handleReadyStateChangeProxy, false);
+
+ // Set up a timeout if we don't have XHR2
+ if (this._xhrLevel == 1) {
+ this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
+ }
+
+ // Sometimes we get back 404s immediately, particularly when there is a cross origin request. // note this does not catch in Chrome
+ try {
+ if (!this._item.values || this._item.method == createjs.AbstractLoader.GET) {
+ this._request.send();
+ } else if (this._item.method == createjs.AbstractLoader.POST) {
+ this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
+ }
+ } catch (error) {
+ this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
+ }
+ };
+
+ p.setResponseType = function (type) {
+ this._request.responseType = type;
+ };
+
+ /**
+ * Get all the response headers from the XmlHttpRequest.
+ *
+ * From the docs: Return all the HTTP headers, excluding headers that are a case-insensitive match
+ * for Set-Cookie or Set-Cookie2, as a single string, with each header line separated by a U+000D CR U+000A LF pair,
+ * excluding the status line, and with each header name and header value separated by a U+003A COLON U+0020 SPACE
+ * pair.
+ * @method getAllResponseHeaders
+ * @return {String}
+ * @since 0.4.1
+ */
+ p.getAllResponseHeaders = function () {
+ if (this._request.getAllResponseHeaders instanceof Function) {
+ return this._request.getAllResponseHeaders();
+ } else {
+ return null;
+ }
+ };
+
+ /**
+ * Get a specific response header from the XmlHttpRequest.
+ *
+ * From the docs: Returns the header field value from the response of which the field name matches
+ * header, unless the field name is Set-Cookie or Set-Cookie2.
+ * @method getResponseHeader
+ * @param {String} header The header name to retrieve.
+ * @return {String}
+ * @since 0.4.1
+ */
+ p.getResponseHeader = function (header) {
+ if (this._request.getResponseHeader instanceof Function) {
+ return this._request.getResponseHeader(header);
+ } else {
+ return null;
+ }
+ };
+
+// protected methods
+ /**
+ * The XHR request has reported progress.
+ * @method _handleProgress
+ * @param {Object} event The XHR progress event.
+ * @private
+ */
+ p._handleProgress = function (event) {
+ if (!event || event.loaded > 0 && event.total == 0) {
+ return; // Sometimes we get no "total", so just ignore the progress event.
+ }
+
+ var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
+ this.dispatchEvent(newEvent);
+ };
+
+ /**
+ * The XHR request has reported a load start.
+ * @method _handleLoadStart
+ * @param {Object} event The XHR loadStart event.
+ * @private
+ */
+ p._handleLoadStart = function (event) {
+ clearTimeout(this._loadTimeout);
+ this.dispatchEvent("loadstart");
+ };
+
+ /**
+ * The XHR request has reported an abort event.
+ * @method handleAbort
+ * @param {Object} event The XHR abort event.
+ * @private
+ */
+ p._handleAbort = function (event) {
+ this._clean();
+ this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED", null, event));
+ };
+
+ /**
+ * The XHR request has reported an error event.
+ * @method _handleError
+ * @param {Object} event The XHR error event.
+ * @private
+ */
+ p._handleError = function (event) {
+ this._clean();
+ this.dispatchEvent(new createjs.ErrorEvent(event.message));
+ };
+
+ /**
+ * The XHR request has reported a readyState change. Note that older browsers (IE 7 & 8) do not provide an onload
+ * event, so we must monitor the readyStateChange to determine if the file is loaded.
+ * @method _handleReadyStateChange
+ * @param {Object} event The XHR readyStateChange event.
+ * @private
+ */
+ p._handleReadyStateChange = function (event) {
+ if (this._request.readyState == 4) {
+ this._handleLoad();
+ }
+ };
+
+ /**
+ * The XHR request has completed. This is called by the XHR request directly, or by a readyStateChange that has
+ * request.readyState == 4. Only the first call to this method will be processed.
+ * @method _handleLoad
+ * @param {Object} event The XHR load event.
+ * @private
+ */
+ p._handleLoad = function (event) {
+ if (this.loaded) {
+ return;
+ }
+ this.loaded = true;
+
+ var error = this._checkError();
+ if (error) {
+ this._handleError(error);
+ return;
+ }
+
+ this._response = this._getResponse();
+ this._clean();
+
+ this.dispatchEvent(new createjs.Event("complete"));
+ };
+
+ /**
+ * The XHR request has timed out. This is called by the XHR request directly, or via a setTimeout
+ * callback.
+ * @method _handleTimeout
+ * @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout.
+ * @private
+ */
+ p._handleTimeout = function (event) {
+ this._clean();
+
+ this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
+ };
+
+// Protected
+ /**
+ * Determine if there is an error in the current load. This checks the status of the request for problem codes. Note
+ * that this does not check for an actual response. Currently, it only checks for 404 or 0 error code.
+ * @method _checkError
+ * @return {int} If the request status returns an error code.
+ * @private
+ */
+ p._checkError = function () {
+ //LM: Probably need additional handlers here, maybe 501
+ var status = parseInt(this._request.status);
+
+ switch (status) {
+ case 404: // Not Found
+ case 0: // Not Loaded
+ return new Error(status);
+ }
+ return null;
+ };
+
+ /**
+ * Validate the response. Different browsers have different approaches, some of which throw errors when accessed
+ * in other browsers. If there is no response, the _response property will remain null.
+ * @method _getResponse
+ * @private
+ */
+ p._getResponse = function () {
+ if (this._response != null) {
+ return this._response;
+ }
+
+ if (this._request.response != null) {
+ return this._request.response;
+ }
+
+ // Android 2.2 uses .responseText
+ try {
+ if (this._request.responseText != null) {
+ return this._request.responseText;
+ }
+ } catch (e) {
+ }
+
+ // When loading XML, IE9 does not return .response, instead it returns responseXML.xml
+ try {
+ if (this._request.responseXML != null) {
+ return this._request.responseXML;
+ }
+ } catch (e) {
+ }
+
+ return null;
+ };
+
+ /**
+ * Create an XHR request. Depending on a number of factors, we get totally different results.
+ *
Some browsers get an XDomainRequest when loading cross-domain.
+ *
XMLHttpRequest are created when available.
+ *
ActiveX.XMLHTTP objects are used in older IE browsers.
+ *
Text requests override the mime type if possible
+ *
Origin headers are sent for crossdomain requests in some browsers.
+ *
Binary loads set the response type to "arraybuffer"
+ * @method _createXHR
+ * @param {Object} item The requested item that is being loaded.
+ * @return {Boolean} If an XHR request or equivalent was successfully created.
+ * @private
+ */
+ p._createXHR = function (item) {
+ // Check for cross-domain loads. We can't fully support them, but we can try.
+ var crossdomain = createjs.RequestUtils.isCrossDomain(item);
+ var headers = {};
+
+ // Create the request. Fallback to whatever support we have.
+ var req = null;
+ if (window.XMLHttpRequest) {
+ req = new XMLHttpRequest();
+ // This is 8 or 9, so use XDomainRequest instead.
+ if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) {
+ req = new XDomainRequest();
+ }
+ } else { // Old IE versions use a different approach
+ for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
+ var axVersion = s.ACTIVEX_VERSIONS[i];
+ try {
+ req = new ActiveXObject(axVersions);
+ break;
+ } catch (e) {}
+ }
+ if (req == null) { return false; }
+ }
+
+ // Default to utf-8 for Text requests.
+ if (item.mimeType == null && createjs.RequestUtils.isText(item.type)) {
+ item.mimeType = "text/plain; charset=utf-8";
+ }
+
+ // IE9 doesn't support overrideMimeType(), so we need to check for it.
+ if (item.mimeType && req.overrideMimeType) {
+ req.overrideMimeType(item.mimeType);
+ }
+
+ // Determine the XHR level
+ this._xhrLevel = (typeof req.responseType === "string") ? 2 : 1;
+
+ var src = null;
+ if (item.method == createjs.AbstractLoader.GET) {
+ src = createjs.RequestUtils.buildPath(item.src, item.values);
+ } else {
+ src = item.src;
+ }
+
+ // Open the request. Set cross-domain flags if it is supported (XHR level 1 only)
+ req.open(item.method || createjs.AbstractLoader.GET, src, true);
+
+ if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) {
+ headers["Origin"] = location.origin;
+ }
+
+ // To send data we need to set the Content-type header)
+ if (item.values && item.method == createjs.AbstractLoader.POST) {
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
+ }
+
+ if (!crossdomain && !headers["X-Requested-With"]) {
+ headers["X-Requested-With"] = "XMLHttpRequest";
+ }
+
+ if (item.headers) {
+ for (var n in item.headers) {
+ headers[n] = item.headers[n];
+ }
+ }
+
+ for (n in headers) {
+ req.setRequestHeader(n, headers[n])
+ }
+
+ if (req instanceof XMLHttpRequest && item.withCredentials !== undefined) {
+ req.withCredentials = item.withCredentials;
+ }
+
+ this._request = req;
+
+ return true;
+ };
+
+ /**
+ * A request has completed (or failed or canceled), and needs to be disposed.
+ * @method _clean
+ * @private
+ */
+ p._clean = function () {
+ clearTimeout(this._loadTimeout);
+
+ this._request.removeEventListener("loadstart", this._handleLoadStartProxy);
+ this._request.removeEventListener("progress", this._handleProgressProxy);
+ this._request.removeEventListener("abort", this._handleAbortProxy);
+ this._request.removeEventListener("error",this._handleErrorProxy);
+ this._request.removeEventListener("timeout", this._handleTimeoutProxy);
+ this._request.removeEventListener("load", this._handleLoadProxy);
+ this._request.removeEventListener("readystatechange", this._handleReadyStateChangeProxy);
+ };
+
+ p.toString = function () {
+ return "[PreloadJS XHRRequest]";
+ };
+
+ createjs.XHRRequest = createjs.promote(XHRRequest, "AbstractRequest");
+
}());
//##############################################################################
// SoundLoader.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- // constructor
- /**
- * A loader for HTML audio files. PreloadJS can not load WebAudio files, as a WebAudio context is required, which
- * should be created by either a library playing the sound (such as SoundJS, or an
- * external framework that handles audio playback. To load content that can be played by WebAudio, use the
- * {{#crossLink "BinaryLoader"}}{{/crossLink}}, and handle the audio context decoding manually.
- * @class SoundLoader
- * @param {LoadItem|Object} loadItem
- * @param {Boolean} preferXHR
- * @extends AbstractMediaLoader
- * @constructor
- */
- function SoundLoader(loadItem, preferXHR) {
- this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
-
- // protected properties
- if (createjs.RequestUtils.isAudioTag(loadItem)) {
- this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
- this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
- this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
- }
-
- if (this._tag != null) {
- this._preferXHR = false;
- }
- };
-
- var p = createjs.extend(SoundLoader, createjs.AbstractMediaLoader);
- var s = SoundLoader;
-
- // static methods
- /**
- * Determines if the loader can load a specific item. This loader can only load items that are of type
- * {{#crossLink "AbstractLoader/SOUND:property"}}{{/crossLink}}.
- * @method canLoadItem
- * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
- * @returns {Boolean} Whether the loader can load the item.
- * @static
- */
- s.canLoadItem = function (item) {
- return item.type == createjs.AbstractLoader.SOUND;
- };
-
- // protected methods
- p._createTag = function (src) {
- var tag = document.createElement("audio");
- tag.autoplay = false;
- tag.preload = "none";
-
- //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
- tag.src = src;
- return tag;
- };
-
- createjs.SoundLoader = createjs.promote(SoundLoader, "AbstractMediaLoader");
-
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ // constructor
+ /**
+ * A loader for HTML audio files. PreloadJS can not load WebAudio files, as a WebAudio context is required, which
+ * should be created by either a library playing the sound (such as SoundJS, or an
+ * external framework that handles audio playback. To load content that can be played by WebAudio, use the
+ * {{#crossLink "BinaryLoader"}}{{/crossLink}}, and handle the audio context decoding manually.
+ * @class SoundLoader
+ * @param {LoadItem|Object} loadItem
+ * @param {Boolean} preferXHR
+ * @extends AbstractMediaLoader
+ * @constructor
+ */
+ function SoundLoader(loadItem, preferXHR) {
+ this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
+
+ // protected properties
+ if (createjs.RequestUtils.isAudioTag(loadItem)) {
+ this._tag = loadItem;
+ } else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
+ this._tag = loadItem;
+ } else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
+ this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
+ }
+
+ if (this._tag != null) {
+ this._preferXHR = false;
+ }
+ };
+
+ var p = createjs.extend(SoundLoader, createjs.AbstractMediaLoader);
+ var s = SoundLoader;
+
+ // static methods
+ /**
+ * Determines if the loader can load a specific item. This loader can only load items that are of type
+ * {{#crossLink "AbstractLoader/SOUND:property"}}{{/crossLink}}.
+ * @method canLoadItem
+ * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
+ * @returns {Boolean} Whether the loader can load the item.
+ * @static
+ */
+ s.canLoadItem = function (item) {
+ return item.type == createjs.AbstractLoader.SOUND;
+ };
+
+ // protected methods
+ p._createTag = function (src) {
+ var tag = document.createElement("audio");
+ tag.autoplay = false;
+ tag.preload = "none";
+
+ //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
+ tag.src = src;
+ return tag;
+ };
+
+ createjs.SoundLoader = createjs.promote(SoundLoader, "AbstractMediaLoader");
+
}());
//##############################################################################
// AudioSprite.js
//##############################################################################
-// NOTE this is "Class" is purely to document audioSprite Setup and usage.
-
-
-/**
- * Note: AudioSprite is not a class, but its usage is easily lost in the documentation, so it has been called
- * out here for quick reference.
- *
- * Audio sprites are much like CSS sprites or image sprite sheets: multiple audio assets grouped into a single file.
- * Audio sprites work around limitations in certain browsers, where only a single sound can be loaded and played at a
- * time. We recommend at least 300ms of silence between audio clips to deal with HTML audio tag inaccuracy, and to prevent
- * accidentally playing bits of the neighbouring clips.
- *
- * Benefits of Audio Sprites:
- *
- *
More robust support for older browsers and devices that only allow a single audio instance, such as iOS 5.
- *
They provide a work around for the Internet Explorer 9 audio tag limit, which restricts how many different
- * sounds that could be loaded at once.
- *
Faster loading by only requiring a single network request for several sounds, especially on mobile devices
- * where the network round trip for each file can add significant latency.
- *
- *
- * Drawbacks of Audio Sprites
- *
- *
No guarantee of smooth looping when using HTML or Flash audio. If you have a track that needs to loop
- * smoothly and you are supporting non-web audio browsers, do not use audio sprites for that sound if you can avoid
- * it.
- *
No guarantee that HTML audio will play back immediately, especially the first time. In some browsers
- * (Chrome!), HTML audio will only load enough to play through at the current download speed – so we rely on the
- * `canplaythrough` event to determine if the audio is loaded. Since audio sprites must jump ahead to play specific
- * sounds, the audio may not yet have downloaded fully.
- *
Audio sprites share the same core source, so if you have a sprite with 5 sounds and are limited to 2
- * concurrently playing instances, you can only play 2 of the sounds at the same time.
- *
- *
- *
Example
- *
- * createjs.Sound.initializeDefaultPlugins();
- * var assetsPath = "./assets/";
- * var sounds = [{
- * src:"MyAudioSprite.ogg", data: {
- * audioSprite: [
- * {id:"sound1", startTime:0, duration:500},
- * {id:"sound2", startTime:1000, duration:400},
- * {id:"sound3", startTime:1700, duration: 1000}
- * ]}
- * }
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", loadSound);
- * createjs.Sound.registerSounds(sounds, assetsPath);
- * // after load is complete
- * createjs.Sound.play("sound2");
- *
- * You can also create audio sprites on the fly by setting the startTime and duration when creating an new AbstractSoundInstance.
- *
- * createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400});
- *
- * The excellent CreateJS community has created a tool to create audio sprites, available at
- * https://github.com/tonistiigi/audiosprite,
- * as well as a jsfiddle to convert the output
- * to SoundJS format.
- *
- * @class AudioSprite
- * @since 0.6.0
+// NOTE this is "Class" is purely to document audioSprite Setup and usage.
+
+
+/**
+ * Note: AudioSprite is not a class, but its usage is easily lost in the documentation, so it has been called
+ * out here for quick reference.
+ *
+ * Audio sprites are much like CSS sprites or image sprite sheets: multiple audio assets grouped into a single file.
+ * Audio sprites work around limitations in certain browsers, where only a single sound can be loaded and played at a
+ * time. We recommend at least 300ms of silence between audio clips to deal with HTML audio tag inaccuracy, and to prevent
+ * accidentally playing bits of the neighbouring clips.
+ *
+ * Benefits of Audio Sprites:
+ *
+ *
More robust support for older browsers and devices that only allow a single audio instance, such as iOS 5.
+ *
They provide a work around for the Internet Explorer 9 audio tag limit, which restricts how many different
+ * sounds that could be loaded at once.
+ *
Faster loading by only requiring a single network request for several sounds, especially on mobile devices
+ * where the network round trip for each file can add significant latency.
+ *
+ *
+ * Drawbacks of Audio Sprites
+ *
+ *
No guarantee of smooth looping when using HTML or Flash audio. If you have a track that needs to loop
+ * smoothly and you are supporting non-web audio browsers, do not use audio sprites for that sound if you can avoid
+ * it.
+ *
No guarantee that HTML audio will play back immediately, especially the first time. In some browsers
+ * (Chrome!), HTML audio will only load enough to play through at the current download speed – so we rely on the
+ * `canplaythrough` event to determine if the audio is loaded. Since audio sprites must jump ahead to play specific
+ * sounds, the audio may not yet have downloaded fully.
+ *
Audio sprites share the same core source, so if you have a sprite with 5 sounds and are limited to 2
+ * concurrently playing instances, you can only play 2 of the sounds at the same time.
+ *
+ *
+ *
Example
+ *
+ * createjs.Sound.initializeDefaultPlugins();
+ * var assetsPath = "./assets/";
+ * var sounds = [{
+ * src:"MyAudioSprite.ogg", data: {
+ * audioSprite: [
+ * {id:"sound1", startTime:0, duration:500},
+ * {id:"sound2", startTime:1000, duration:400},
+ * {id:"sound3", startTime:1700, duration: 1000}
+ * ]}
+ * }
+ * ];
+ * createjs.Sound.alternateExtensions = ["mp3"];
+ * createjs.Sound.on("fileload", loadSound);
+ * createjs.Sound.registerSounds(sounds, assetsPath);
+ * // after load is complete
+ * createjs.Sound.play("sound2");
+ *
+ * You can also create audio sprites on the fly by setting the startTime and duration when creating an new AbstractSoundInstance.
+ *
+ * createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400});
+ *
+ * The excellent CreateJS community has created a tool to create audio sprites, available at
+ * https://github.com/tonistiigi/audiosprite,
+ * as well as a jsfiddle to convert the output
+ * to SoundJS format.
+ *
+ * @class AudioSprite
+ * @since 0.6.0
*/
//##############################################################################
// PlayPropsConfig.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
- /**
- * A class to store the optional play properties passed in {{#crossLink "Sound/play"}}{{/crossLink}} and
- * {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} calls.
- *
- * Optional Play Properties Include:
- *
- *
interrupt - How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
- * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- *
delay - The amount of time to delay the start of audio playback, in milliseconds.
- *
offset - The offset from the start of the audio to begin playback, in milliseconds.
- *
loop - How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- *
volume - The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- *
pan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- *
startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- *
duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- *
- *
- *
Example
- *
- * var ppc = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
- * createjs.Sound.play("mySound", ppc);
- * mySoundInstance.play(ppc);
- *
- * @class PlayPropsConfig
- * @constructor
- * @since 0.6.1
- */
- // TODO think of a better name for this class
- var PlayPropsConfig = function () {
-// Public Properties
- /**
- * How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as
- * INTERRUPT_TYPE constants on the Sound class, with the default defined by
- * {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- * @property interrupt
- * @type {string}
- * @default null
- */
- this.interrupt = null;
-
- /**
- * The amount of time to delay the start of audio playback, in milliseconds.
- * @property delay
- * @type {Number}
- * @default null
- */
- this.delay = null;
-
- /**
- * The offset from the start of the audio to begin playback, in milliseconds.
- * @property offset
- * @type {number}
- * @default null
- */
- this.offset = null;
-
- /**
- * How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- * @property loop
- * @type {number}
- * @default null
- */
- this.loop = null;
-
- /**
- * The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- * @property volume
- * @type {number}
- * @default null
- */
- this.volume = null;
-
- /**
- * The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- * @property pan
- * @type {number}
- * @default null
- */
- this.pan = null;
-
- /**
- * Used to create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- * @property startTime
- * @type {number}
- * @default null
- */
- this.startTime = null;
-
- /**
- * Used to create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- * @property duration
- * @type {number}
- * @default null
- */
- this.duration = null;
- };
- var p = PlayPropsConfig.prototype = {};
- var s = PlayPropsConfig;
-
-
-// Static Methods
- /**
- * Creates a PlayPropsConfig from another PlayPropsConfig or an Object.
- *
- * @method create
- * @param {PlayPropsConfig|Object} value The play properties
- * @returns {PlayPropsConfig}
- * @static
- */
- s.create = function (value) {
- if (value instanceof s || value instanceof Object) {
- var ppc = new createjs.PlayPropsConfig();
- ppc.set(value);
- return ppc;
- } else {
- throw new Error("Type not recognized.");
- }
- };
-
-// Public Methods
- /**
- * Provides a chainable shortcut method for setting a number of properties on the instance.
- *
- *
Example
- *
- * var PlayPropsConfig = new createjs.PlayPropsConfig().set({loop:-1, volume:0.7});
- *
- * @method set
- * @param {Object} props A generic object containing properties to copy to the PlayPropsConfig instance.
- * @return {PlayPropsConfig} 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;
- };
-
- p.toString = function() {
- return "[PlayPropsConfig]";
- };
-
- createjs.PlayPropsConfig = s;
-
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+ /**
+ * A class to store the optional play properties passed in {{#crossLink "Sound/play"}}{{/crossLink}} and
+ * {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} calls.
+ *
+ * Optional Play Properties Include:
+ *
+ *
interrupt - How to interrupt any currently playing instances of audio with the same source,
+ * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
+ * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
+ *
delay - The amount of time to delay the start of audio playback, in milliseconds.
+ *
offset - The offset from the start of the audio to begin playback, in milliseconds.
+ *
loop - How many times the audio loops when it reaches the end of playback. The default is 0 (no
+ * loops), and -1 can be used for infinite playback.
+ *
volume - The volume of the sound, between 0 and 1. Note that the master volume is applied
+ * against the individual volume.
+ *
pan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
+ *
startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
+ *
duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
+ *
+ *
+ *
Example
+ *
+ * var ppc = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
+ * createjs.Sound.play("mySound", ppc);
+ * mySoundInstance.play(ppc);
+ *
+ * @class PlayPropsConfig
+ * @constructor
+ * @since 0.6.1
+ */
+ // TODO think of a better name for this class
+ var PlayPropsConfig = function () {
+// Public Properties
+ /**
+ * How to interrupt any currently playing instances of audio with the same source,
+ * if the maximum number of instances of the sound are already playing. Values are defined as
+ * INTERRUPT_TYPE constants on the Sound class, with the default defined by
+ * {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
+ * @property interrupt
+ * @type {string}
+ * @default null
+ */
+ this.interrupt = null;
+
+ /**
+ * The amount of time to delay the start of audio playback, in milliseconds.
+ * @property delay
+ * @type {Number}
+ * @default null
+ */
+ this.delay = null;
+
+ /**
+ * The offset from the start of the audio to begin playback, in milliseconds.
+ * @property offset
+ * @type {number}
+ * @default null
+ */
+ this.offset = null;
+
+ /**
+ * How many times the audio loops when it reaches the end of playback. The default is 0 (no
+ * loops), and -1 can be used for infinite playback.
+ * @property loop
+ * @type {number}
+ * @default null
+ */
+ this.loop = null;
+
+ /**
+ * The volume of the sound, between 0 and 1. Note that the master volume is applied
+ * against the individual volume.
+ * @property volume
+ * @type {number}
+ * @default null
+ */
+ this.volume = null;
+
+ /**
+ * The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
+ * @property pan
+ * @type {number}
+ * @default null
+ */
+ this.pan = null;
+
+ /**
+ * Used to create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
+ * @property startTime
+ * @type {number}
+ * @default null
+ */
+ this.startTime = null;
+
+ /**
+ * Used to create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
+ * @property duration
+ * @type {number}
+ * @default null
+ */
+ this.duration = null;
+ };
+ var p = PlayPropsConfig.prototype = {};
+ var s = PlayPropsConfig;
+
+
+// Static Methods
+ /**
+ * Creates a PlayPropsConfig from another PlayPropsConfig or an Object.
+ *
+ * @method create
+ * @param {PlayPropsConfig|Object} value The play properties
+ * @returns {PlayPropsConfig}
+ * @static
+ */
+ s.create = function (value) {
+ if (value instanceof s || value instanceof Object) {
+ var ppc = new createjs.PlayPropsConfig();
+ ppc.set(value);
+ return ppc;
+ } else {
+ throw new Error("Type not recognized.");
+ }
+ };
+
+// Public Methods
+ /**
+ * Provides a chainable shortcut method for setting a number of properties on the instance.
+ *
+ *
Example
+ *
+ * var PlayPropsConfig = new createjs.PlayPropsConfig().set({loop:-1, volume:0.7});
+ *
+ * @method set
+ * @param {Object} props A generic object containing properties to copy to the PlayPropsConfig instance.
+ * @return {PlayPropsConfig} 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;
+ };
+
+ p.toString = function() {
+ return "[PlayPropsConfig]";
+ };
+
+ createjs.PlayPropsConfig = s;
+
}());
//##############################################################################
// Sound.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-
-
-(function () {
- "use strict";
-
- /**
- * 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.
- *
- * Registering and Preloading
- * Before you can play a sound, it must be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}},
- * or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/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
- * PreloadJS, 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.
- *
- * Playback
- * To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method.
- * This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc.
- * Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs.
- *
- * Plugins
- * By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
- * are used (when available), although developers can change plugin priority or add new plugins (such as the
- * provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API
- * methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see
- * {{#crossLink "Sound/installPlugins"}}{{/crossLink}}.
- *
- *
Example
- *
- * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
- * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]);
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", createjs.proxy(this.loadHandler, (this)));
- * createjs.Sound.registerSound("path/to/mySound.ogg", "sound");
- * function loadHandler(event) {
- * // This is fired for each sound that is registered.
- * var instance = createjs.Sound.play("sound"); // play using id. Could also use full source path or event.src.
- * instance.on("complete", createjs.proxy(this.handleComplete, this));
- * instance.volume = 0.5;
- * }
- *
- * The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument
- * of {{#crossLink "Sound/registerSound"}}{{/crossLink}}. Note that if not specified, the active plugin will apply
- * a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a
- * default limit of 100.
- *
- * createjs.Sound.registerSound("sound.mp3", "soundId", 4);
- *
- * 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 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.
- *
- * var queue = new createjs.LoadQueue();
- * queue.installPlugin(createjs.Sound);
- *
- * Audio Sprites
- * SoundJS has added support for {{#crossLink "AudioSprite"}}{{/crossLink}}, available as of version 0.6.0.
- * For those unfamiliar with audio sprites, they are much like CSS sprites or sprite sheets: multiple audio assets
- * grouped into a single file.
- *
- *
Example
- *
- * var assetsPath = "./assets/";
- * var sounds = [{
- * src:"MyAudioSprite.ogg", data: {
- * audioSprite: [
- * {id:"sound1", startTime:0, duration:500},
- * {id:"sound2", startTime:1000, duration:400},
- * {id:"sound3", startTime:1700, duration: 1000}
- * ]}
- * }
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", loadSound);
- * createjs.Sound.registerSounds(sounds, assetsPath);
- * // after load is complete
- * createjs.Sound.play("sound2");
- *
- * Mobile Safe Approach
- * Mobile devices require sounds to be played inside of a user initiated event (touch/click) in varying degrees.
- * As of SoundJS 0.4.1, you can launch a site inside of a user initiated event and have audio playback work. To
- * enable as broadly as possible, the site needs to setup the Sound plugin in its initialization (for example via
- * createjs.Sound.initializeDefaultPlugins();), and all sounds need to be played in the scope of the
- * application. See the MobileSafe demo for a working example.
- *
- *
Example
- *
- * document.getElementById("status").addEventListener("click", handleTouch, false); // works on Android and iPad
- * function handleTouch(event) {
- * document.getElementById("status").removeEventListener("click", handleTouch, false); // remove the listener
- * var thisApp = new myNameSpace.MyApp(); // launch the app
- * }
- *
- * Loading Alternate Paths and Extensionless Files
- * SoundJS supports loading alternate paths and extensionless files by passing an object for src that has various paths
- * with property labels matching the extension. These labels are how SoundJS determines if the browser will support the sound.
- * Priority is determined by the property order (first property is tried first). This is supported by both internal loading
- * and loading with PreloadJS.
- *
- * Note an id is required for playback.
- *
- *
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.
- *
MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
- * encoding with 64kbps works.
- *
Occasionally very short samples will get cut off.
- *
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.
- *
- * Firefox 25 Web Audio limitations
- *
mp3 audio files do not load properly on all windows machines, reported
- * here.
- * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.
-
- * Safari limitations
- *
Safari requires Quicktime to be installed for audio playback.
- *
- * iOS 6 Web Audio limitations
- *
Sound is initially muted and will only unmute through play being called inside a user initiated event
- * (touch/click).
- *
A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio at a different sampleRate.
- *
- *
- * Android HTML Audio limitations
- *
We have no control over audio volume. Only the user can set volume on their device.
- *
We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
- * a delay.
- *
- * Web Audio and PreloadJS
- *
Web Audio must be loaded through XHR, therefore when used with PreloadJS tag loading is not possible. This means that tag loading cannot
- * be used to avoid cross domain issues if WebAudioPlugin is used
- *
- * @class Sound
- * @static
- * @uses EventDispatcher
- */
- function Sound() {
- throw "Sound cannot be instantiated";
- }
-
- var s = Sound;
-
-
-// Static Properties
- /**
- * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
- * instances of the sound are already playing.
- * @property INTERRUPT_ANY
- * @type {String}
- * @default any
- * @static
- */
- s.INTERRUPT_ANY = "any";
-
- /**
- * The interrupt value to interrupt the earliest currently playing instance with the same source that progressed the
- * least distance in the audio track, if the maximum number of instances of the sound are already playing.
- * @property INTERRUPT_EARLY
- * @type {String}
- * @default early
- * @static
- */
- s.INTERRUPT_EARLY = "early";
-
- /**
- * The interrupt value to interrupt the currently playing instance with the same source that progressed the most
- * distance in the audio track, if the maximum number of instances of the sound are already playing.
- * @property INTERRUPT_LATE
- * @type {String}
- * @default late
- * @static
- */
- s.INTERRUPT_LATE = "late";
-
- /**
- * The interrupt value to not interrupt any currently playing instances with the same source, if the maximum number of
- * instances of the sound are already playing.
- * @property INTERRUPT_NONE
- * @type {String}
- * @default none
- * @static
- */
- s.INTERRUPT_NONE = "none";
-
- /**
- * Defines the playState of an instance that is still initializing.
- * @property PLAY_INITED
- * @type {String}
- * @default playInited
- * @static
- */
- s.PLAY_INITED = "playInited";
-
- /**
- * Defines the playState of an instance that is currently playing or paused.
- * @property PLAY_SUCCEEDED
- * @type {String}
- * @default playSucceeded
- * @static
- */
- s.PLAY_SUCCEEDED = "playSucceeded";
-
- /**
- * Defines the playState of an instance that was interrupted by another instance.
- * @property PLAY_INTERRUPTED
- * @type {String}
- * @default playInterrupted
- * @static
- */
- s.PLAY_INTERRUPTED = "playInterrupted";
-
- /**
- * Defines the playState of an instance that completed playback.
- * @property PLAY_FINISHED
- * @type {String}
- * @default playFinished
- * @static
- */
- s.PLAY_FINISHED = "playFinished";
-
- /**
- * Defines the playState of an instance that failed to play. This is usually caused by a lack of available channels
- * when the interrupt mode was "INTERRUPT_NONE", the playback stalled, or the sound could not be found.
- * @property PLAY_FAILED
- * @type {String}
- * @default playFailed
- * @static
- */
- s.PLAY_FAILED = "playFailed";
-
- /**
- * A list of the default supported extensions that Sound will try to play. Plugins will check if the browser
- * can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to
- * support additional media types.
- *
- * NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
- *
- * More details on file formats can be found at http://en.wikipedia.org/wiki/Audio_file_format.
- * A very detailed list of file formats can be found at http://www.fileinfo.com/filetypes/audio.
- * @property SUPPORTED_EXTENSIONS
- * @type {Array[String]}
- * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
- * @since 0.4.0
- * @static
- */
- s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "opus", "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
- * that support so plugins can accurately determine if an extension is supported. Adding to this list can help
- * plugins determine more accurately if an extension is supported.
- *
- * A useful list of extensions for each format can be found at http://html5doctor.com/html5-audio-the-state-of-play/.
- * @property EXTENSION_MAP
- * @type {Object}
- * @since 0.4.0
- * @default {m4a:"mp4"}
- * @static
- */
- s.EXTENSION_MAP = {
- m4a:"mp4"
- };
-
- /**
- * The RegExp pattern used to parse file URIs. This supports simple file names, as well as full domain URIs with
- * query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6.
- * @property FILE_PATTERN
- * @type {RegExp}
- * @static
- * @protected
- */
- s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
-
-
-// Class Public properties
- /**
- * Determines the default behavior for interrupting other currently playing instances with the same source, if the
- * maximum number of instances of the sound are already playing. Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}
- * but this can be set and will change playback behavior accordingly. This is only used when {{#crossLink "Sound/play"}}{{/crossLink}}
- * is called without passing a value for interrupt.
- * @property defaultInterruptBehavior
- * @type {String}
- * @default Sound.INTERRUPT_NONE, or "none"
- * @static
- * @since 0.4.0
- */
- s.defaultInterruptBehavior = s.INTERRUPT_NONE; // OJR does s.INTERRUPT_ANY make more sense as default? Needs game dev testing to see which case makes more sense.
-
- /**
- * An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin.
- * These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your
- * extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need
- * to exist in the same location, as only the extension is altered.
- *
- * Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}}
- * and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading.
- *
- *
Example
- *
- * var sounds = [
- * {src:"myPath/mySound.ogg", id:"example"},
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3
- * createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
- * createjs.Sound.registerSounds(sounds, assetPath);
- * // ...
- * createjs.Sound.play("myPath/mySound.ogg"); // works regardless of what extension is supported. Note calling with ID is a better approach
- *
- * @property alternateExtensions
- * @type {Array}
- * @since 0.5.2
- * @static
- */
- s.alternateExtensions = [];
-
- /**
- * The currently active plugin. If this is null, then no plugin could be initialized. If no plugin was specified,
- * Sound attempts to apply the default plugins: {{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by
- * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
- * @property activePlugin
- * @type {Object}
- * @static
- */
- s.activePlugin = null;
-
-
-// class getter / setter properties
- /**
- * Set the master volume of Sound. The master volume is multiplied against each sound's individual volume. For
- * example, if master volume is 0.5 and a sound's volume is 0.5, the resulting volume is 0.25. To set individual
- * sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.volume = 0.5;
- *
- *
- * @property volume
- * @type {Number}
- * @default 1
- * @since 0.6.1
- */
- s._masterVolume = 1;
- Object.defineProperty(s, "volume", {
- get: function () {return this._masterVolume;},
- set: function (value) {
- 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;
- for (var i = 0, l = instances.length; i < l; i++) {
- instances[i].setMasterVolume(value);
- }
- }
- }
- });
-
- /**
- * 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
- * instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.muted = true;
- *
- *
- * @property muted
- * @type {Boolean}
- * @default false
- * @since 0.6.1
- */
- s._masterMute = false;
- // OJR references to the methods were not working, so the code had to be duplicated here
- Object.defineProperty(s, "muted", {
- get: function () {return this._masterMute;},
- set: function (value) {
- if (value == null) {return false;}
-
- this._masterMute = value;
- if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
- var instances = this._instances;
- for (var i = 0, l = instances.length; i < l; i++) {
- instances[i].setMasterMute(value);
- }
- }
- return true;
- }
- });
-
- /**
- * Get the active plugins capabilities, which help determine if a plugin can be used in the current environment,
- * or if the plugin supports a specific feature. Capabilities include:
- *
- *
panning: If the plugin can pan audio from left to right
- *
volume; If the plugin can control audio volume.
- *
tracks: The maximum number of audio tracks that can be played back at a time. This will be -1
- * if there is no known limit.
- * An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
- *
mp3: If MP3 audio is supported.
- *
ogg: If OGG audio is supported.
- *
wav: If WAV audio is supported.
- *
mpeg: If MPEG audio is supported.
- *
m4a: If M4A audio is supported.
- *
mp4: If MP4 audio is supported.
- *
aiff: If aiff audio is supported.
- *
wma: If wma audio is supported.
- *
mid: If mid audio is supported.
- *
- *
- * You can get a specific capability of the active plugin using standard object notation
- *
- *
Example
- *
- * var mp3 = createjs.Sound.capabilities.mp3;
- *
- * Note this property is read only.
- *
- * @property capabilities
- * @type {Object}
- * @static
- * @readOnly
- * @since 0.6.1
- */
- Object.defineProperty(s, "capabilities", {
- get: function () {
- if (s.activePlugin == null) {return null;}
- return s.activePlugin._capabilities;
- },
- set: function (value) { return false;}
- });
-
-
-// Class Private properties
- /**
- * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
- * plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}).
- * If plugins have been registered, but none are applicable, then sound playback will fail.
- * @property _pluginsRegistered
- * @type {Boolean}
- * @default false
- * @static
- * @protected
- */
- s._pluginsRegistered = false;
-
- /**
- * Used internally to assign unique IDs to each AbstractSoundInstance.
- * @property _lastID
- * @type {Number}
- * @static
- * @protected
- */
- s._lastID = 0;
-
- /**
- * An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
- * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
- * When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}}
- * method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}}
- * method.
- * @property _instances
- * @type {Array}
- * @protected
- * @static
- */
- s._instances = [];
-
- /**
- * An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
- * @property _idHash
- * @type {Object}
- * @protected
- * @static
- */
- s._idHash = {};
-
- /**
- * An object hash that stores preloading sound sources via the parsed source that is passed to the plugin. Contains the
- * source, id, and data that was passed in by the user. Parsed sources can contain multiple instances of source, id,
- * and data.
- * @property _preloadHash
- * @type {Object}
- * @protected
- * @static
- */
- s._preloadHash = {};
-
- /**
- * An object hash storing {{#crossLink "PlayPropsConfig"}}{{/crossLink}} via the parsed source that is passed as defaultPlayProps in
- * {{#crossLink "Sound/registerSound"}}{{/crossLink}} and {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * @property _defaultPlayPropsHash
- * @type {Object}
- * @protected
- * @static
- * @since 0.6.1
- */
- s._defaultPlayPropsHash = {};
-
-
-// EventDispatcher methods:
- s.addEventListener = null;
- s.removeEventListener = null;
- s.removeAllEventListeners = null;
- s.dispatchEvent = null;
- s.hasEventListener = null;
- s._listeners = null;
-
- createjs.EventDispatcher.initialize(s); // inject EventDispatcher methods.
-
-
-// Events
- /**
- * This event is fired when a file finishes loading internally. This event is fired for each loaded sound,
- * so any handler methods should look up the event.src to handle a particular sound.
- * @event fileload
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {String} src The source of the sound that was loaded.
- * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
- * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
- * @since 0.4.1
- */
-
- /**
- * This event is fired when a file fails loading internally. This event is fired for each loaded sound,
- * so any handler methods should look up the event.src to handle a particular sound.
- * @event fileerror
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @param {String} src The source of the sound that was loaded.
- * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
- * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
- * @since 0.6.0
- */
-
-
-// Class Public Methods
- /**
- * Get the preload rules to allow Sound to be used as a plugin by PreloadJS.
- * Any load calls that have the matching type or extension will fire the callback method, and use the resulting
- * object, which is potentially modified by Sound. This helps when determining the correct path, as well as
- * registering the audio instance(s) with Sound. This method should not be called, except by PreloadJS.
- * @method getPreloadHandlers
- * @return {Object} An object containing:
- *
callback: A preload callback that is fired when a file is added to PreloadJS, which provides
- * Sound a mechanism to modify the load parameters, select the correct file format, register the sound, etc.
- *
types: A list of file types that are supported by Sound (currently supports "sound").
- *
extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
- * @static
- * @protected
- */
- s.getPreloadHandlers = function () {
- return {
- callback:createjs.proxy(s.initLoad, s),
- types:["sound"],
- extensions:s.SUPPORTED_EXTENSIONS
- };
- };
-
- /**
- * Used to dispatch fileload events from internal loading.
- * @method _handleLoadComplete
- * @param event A loader event.
- * @protected
- * @static
- * @since 0.6.0
- */
- s._handleLoadComplete = function(event) {
- var src = event.target.getItem().src;
- if (!s._preloadHash[src]) {return;}
-
- for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
- var item = s._preloadHash[src][i];
- s._preloadHash[src][i] = true;
-
- if (!s.hasEventListener("fileload")) { continue; }
-
- var event = new createjs.Event("fileload");
- event.src = item.src;
- event.id = item.id;
- event.data = item.data;
- event.sprite = item.sprite;
-
- s.dispatchEvent(event);
- }
- };
-
- /**
- * Used to dispatch error events from internal preloading.
- * @param event
- * @protected
- * @since 0.6.0
- * @static
- */
- s._handleLoadError = function(event) {
- var src = event.target.getItem().src;
- if (!s._preloadHash[src]) {return;}
-
- for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
- var item = s._preloadHash[src][i];
- s._preloadHash[src][i] = false;
-
- if (!s.hasEventListener("fileerror")) { continue; }
-
- var event = new createjs.Event("fileerror");
- event.src = item.src;
- event.id = item.id;
- event.data = item.data;
- event.sprite = item.sprite;
-
- s.dispatchEvent(event);
- }
- };
-
- /**
- * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
- *
- * @method _registerPlugin
- * @param {Object} plugin The plugin class to install.
- * @return {Boolean} Whether the plugin was successfully initialized.
- * @static
- * @private
- */
- s._registerPlugin = function (plugin) {
- // 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();
- return true;
- }
- return false;
- };
-
- /**
- * Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
- *
- *
Example
- *
- * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
- * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
- *
- * @method registerPlugins
- * @param {Array} plugins An array of plugins classes to install.
- * @return {Boolean} Whether a plugin was successfully initialized.
- * @static
- */
- s.registerPlugins = function (plugins) {
- s._pluginsRegistered = true;
- for (var i = 0, l = plugins.length; i < l; i++) {
- if (s._registerPlugin(plugins[i])) {
- return true;
- }
- }
- return false;
- };
-
- /**
- * Initialize the default plugins. This method is automatically called when any audio is played or registered before
- * the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the
- * default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
- *
- *
Example
- *
- * if (!createjs.initializeDefaultPlugins()) { return; }
- *
- * @method initializeDefaultPlugins
- * @returns {Boolean} True if a plugin was initialized, false otherwise.
- * @since 0.4.0
- * @static
- */
- s.initializeDefaultPlugins = function () {
- if (s.activePlugin != null) {return true;}
- if (s._pluginsRegistered) {return false;}
- if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
- return false;
- };
-
- /**
- * Determines if Sound has been initialized, and a plugin has been activated.
- *
- *
Example
- * This example sets up a Flash fallback, but only if there is no plugin specified yet.
- *
- * if (!createjs.Sound.isReady()) {
- * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
- * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
- * }
- *
- * @method isReady
- * @return {Boolean} If Sound has initialized a plugin.
- * @static
- */
- s.isReady = function () {
- return (s.activePlugin != null);
- };
-
- /**
- * Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
- *
- * @method getCapabilities
- * @return {Object} An object containing the capabilities of the active plugin.
- * @static
- * @deprecated
- */
- s.getCapabilities = function () {
- if (s.activePlugin == null) {return null;}
- return s.activePlugin._capabilities;
- };
-
- /**
- * Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
- *
- * @method getCapability
- * @param {String} key The capability to retrieve
- * @return {Number|Boolean} The value of the capability.
- * @static
- * @see getCapabilities
- * @deprecated
- */
- s.getCapability = function (key) {
- if (s.activePlugin == null) {return null;}
- return s.activePlugin._capabilities[key];
- };
-
- /**
- * Process manifest items from PreloadJS. This method is intended
- * for usage by a plugin, and not for direct interaction.
- * @method initLoad
- * @param {Object} src The object to load.
- * @return {Object|AbstractLoader} An instance of AbstractLoader.
- * @protected
- * @static
- */
- s.initLoad = function (loadItem) {
- return s._registerSound(loadItem);
- };
-
- /**
- * Internal method for loading sounds. This should not be called directly.
- *
- * @method _registerSound
- * @param {Object} src The object to load, containing src property and optionally containing id and data.
- * @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.6.0
- */
-
- s._registerSound = function (loadItem) {
- if (!s.initializeDefaultPlugins()) {return false;}
-
- var details;
- if (loadItem.src instanceof Object) {
- details = s._parseSrc(loadItem.src);
- details.src = loadItem.path + details.src;
- } else {
- details = s._parsePath(loadItem.src);
- }
- if (details == null) {return false;}
- loadItem.src = details.src;
- loadItem.type = "sound";
-
- var data = loadItem.data;
- var numChannels = 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: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
-
- if (sp.defaultPlayProps) {
- s._defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
- }
- }
- }
- }
- if (loadItem.id != null) {s._idHash[loadItem.id] = {src: loadItem.src}};
- var loader = s.activePlugin.register(loadItem);
-
- SoundChannel.create(loadItem.src, numChannels);
-
- // return the number of instances to the user. This will also be returned in the load event.
- if (data == null || !isNaN(data)) {
- loadItem.data = numChannels || SoundChannel.maxPerChannel();
- } else {
- loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
- }
-
- if (loader.type) {loadItem.type = loader.type;}
-
- if (loadItem.defaultPlayProps) {
- s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
- }
- return loader;
- };
-
- /**
- * Register an audio file for loading and future playback in Sound. This is automatically called when using
- * PreloadJS. It is recommended to register all sounds that
- * need to be played back in order to properly prepare and preload them. Sound does internal preloading when required.
- *
- *
Example
- *
- * createjs.Sound.alternateExtensions = ["mp3"];
- * createjs.Sound.on("fileload", handleLoad); // add an event listener for when load is completed
- * createjs.Sound.registerSound("myAudioPath/mySound.ogg", "myID", 3);
- * createjs.Sound.registerSound({ogg:"path1/mySound.ogg", mp3:"path2/mySoundNoExtension"}, "myID", 3);
- *
- *
- * @method registerSound
- * @param {String | Object} src The source or an Object with a "src" property or an Object with multiple extension labeled src properties.
- * @param {String} [id] An id specified by the user to play the sound later. Note id is required for when src is multiple extension labeled src properties.
- * @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 {{#crossLink "AudioSprite"}}{{/crossLink}} array of objects in the following format {id, startTime, duration}.
- * id used to play the sound later, in the same manner as a sound src with an id.
- * startTime is the initial offset to start playback and loop from, in milliseconds.
- * duration is the amount of time to play the clip for, in milliseconds.
- * 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.
- * @param {Object | PlayPropsConfig} defaultPlayProps Optional Playback properties that will be set as the defaults on any new AbstractSoundInstance.
- * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for options.
- * @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
- * @since 0.4.0
- */
- s.registerSound = function (src, id, data, basePath, defaultPlayProps) {
- var loadItem = {src: src, id: id, data:data, defaultPlayProps:defaultPlayProps};
- if (src instanceof Object && src.src) {
- basePath = id;
- loadItem = src;
- }
- loadItem = createjs.LoadItem.create(loadItem);
- loadItem.path = basePath;
-
- if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + src;}
-
- var loader = s._registerSound(loadItem);
- if(!loader) {return false;}
-
- if (!s._preloadHash[loadItem.src]) { s._preloadHash[loadItem.src] = [];}
- s._preloadHash[loadItem.src].push(loadItem);
- if (s._preloadHash[loadItem.src].length == 1) {
- // OJR note this will disallow reloading a sound if loading fails or the source changes
- loader.on("complete", createjs.proxy(this._handleLoadComplete, this));
- loader.on("error", createjs.proxy(this._handleLoadError, this));
- s.activePlugin.preload(loader);
- } else {
- if (s._preloadHash[loadItem.src][0] == true) {return true;}
- }
-
- return loadItem;
- };
-
- /**
- * Register an array of audio files for loading and future playback in Sound. It is recommended to register all
- * sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading
- * when required.
- *
- *
Example
- *
- * var assetPath = "./myAudioPath/";
- * var sounds = [
- * {src:"asset0.ogg", id:"example"},
- * {src:"asset1.ogg", id:"1", data:6},
- * {src:"asset2.mp3", id:"works"}
- * {src:{mp3:"path1/asset3.mp3", ogg:"path2/asset3NoExtension}, id:"better"}
- * ];
- * createjs.Sound.alternateExtensions = ["mp3"]; // if the passed extension is not supported, try this extension
- * createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
- * createjs.Sound.registerSounds(sounds, assetPath);
- *
- * @method registerSounds
- * @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: {src:srcURI, id:ID, data:Data}
- * with "id" and "data" being optional.
- * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to load.
- * Note id is required if src is an object with extension labeled src properties.
- * @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing
- * audio that was loaded with a basePath by src, the basePath must be included.
- * @return {Object} An array of objects with the modified values that were passed in, which defines each sound.
- * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized.
- * Also, it will return true for any values when the source is already loaded.
- * @static
- * @since 0.6.0
- */
- s.registerSounds = function (sounds, basePath) {
- var returnValues = [];
- if (sounds.path) {
- if (!basePath) {
- basePath = sounds.path;
- } else {
- basePath = basePath + sounds.path;
- }
- sounds = sounds.manifest;
- // TODO document this feature
- }
- for (var i = 0, l = sounds.length; i < l; i++) {
- returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
- }
- return returnValues;
- };
-
- /**
- * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
- * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * Note this will stop playback on active instances playing this sound before deleting them.
- * Note if you passed in a basePath, you need to pass it or prepend it to the src here.
- *
- *
Example
- *
- * createjs.Sound.removeSound("myID");
- * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
- * createjs.Sound.removeSound("myPath/myOtherSound.mp3", "myBasePath/");
- * createjs.Sound.removeSound({mp3:"musicNoExtension", ogg:"music.ogg"}, "myBasePath/");
- *
- * @method removeSound
- * @param {String | Object} src The src or ID of the audio, or an Object with a "src" property, or an Object with multiple extension labeled src properties.
- * @param {string} basePath Set a path that will be prepended to each src when removing.
- * @return {Boolean} True if sound is successfully removed.
- * @static
- * @since 0.4.1
- */
- s.removeSound = function(src, basePath) {
- if (s.activePlugin == null) {return false;}
-
- if (src instanceof Object && src.src) {src = src.src;}
-
- var details;
- if (src instanceof Object) {
- details = s._parseSrc(src);
- } else {
- src = s._getSrcById(src).src;
- details = s._parsePath(src);
- }
- if (details == null) {return false;}
- src = details.src;
- if (basePath != null) {src = basePath + src;}
-
- for(var prop in s._idHash){
- if(s._idHash[prop].src == src) {
- delete(s._idHash[prop]);
- }
- }
-
- // clear from SoundChannel, which also stops and deletes all instances
- SoundChannel.removeSrc(src);
-
- delete(s._preloadHash[src]);
-
- s.activePlugin.removeSound(src);
-
- return true;
- };
-
- /**
- * Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
- * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * Note this will stop playback on active instances playing this audio before deleting them.
- * Note if you passed in a basePath, you need to pass it or prepend it to the src here.
- *
- *
Example
- *
- * assetPath = "./myPath/";
- * var sounds = [
- * {src:"asset0.ogg", id:"example"},
- * {src:"asset1.ogg", id:"1", data:6},
- * {src:"asset2.mp3", id:"works"}
- * ];
- * createjs.Sound.removeSounds(sounds, assetPath);
- *
- * @method removeSounds
- * @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: {srcOrID:srcURIorID}.
- * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to remove.
- * @param {string} basePath Set a path that will be prepended to each src when removing.
- * @return {Object} An array of Boolean values representing if the sounds with the same array index were
- * successfully removed.
- * @static
- * @since 0.4.1
- */
- s.removeSounds = function (sounds, basePath) {
- var returnValues = [];
- if (sounds.path) {
- if (!basePath) {
- basePath = sounds.path;
- } else {
- basePath = basePath + sounds.path;
- }
- sounds = sounds.manifest;
- }
- for (var i = 0, l = sounds.length; i < l; i++) {
- returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath);
- }
- return returnValues;
- };
-
- /**
- * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
- * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
- * Note this will stop playback on all active sound instances before deleting them.
- *
- *
Example
- *
- * createjs.Sound.removeAllSounds();
- *
- * @method removeAllSounds
- * @static
- * @since 0.4.1
- */
- s.removeAllSounds = function() {
- s._idHash = {};
- s._preloadHash = {};
- SoundChannel.removeAll();
- if (s.activePlugin) {s.activePlugin.removeAllSounds();}
- };
-
- /**
- * Check if a source has been loaded by internal preloaders. This is necessary to ensure that sounds that are
- * not completed preloading will not kick off a new internal preload if they are played.
- *
- *
Example
- *
- * var mySound = "assetPath/asset0.ogg";
- * if(createjs.Sound.loadComplete(mySound) {
- * createjs.Sound.play(mySound);
- * }
- *
- * @method loadComplete
- * @param {String} src The src or id that is being loaded.
- * @return {Boolean} If the src is already loaded.
- * @since 0.4.0
- * @static
- */
- s.loadComplete = function (src) {
- if (!s.isReady()) { return false; }
- var details = s._parsePath(src);
- if (details) {
- src = s._getSrcById(details.src).src;
- } else {
- src = s._getSrcById(src).src;
- }
- if(s._preloadHash[src] == undefined) {return false;}
- 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. 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.
- * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
- * and returned to a preloader like PreloadJS.
- * @protected
- * @static
- */
- s._parsePath = function (value) {
- if (typeof(value) != "string") {value = value.toString();}
-
- var match = value.match(s.FILE_PATTERN);
- if (match == null) {return false;}
-
- var name = match[4];
- var ext = match[5];
- var c = s.capabilities;
- 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 = {name:name, src:value, extension:ext};
- return ret;
- };
-
- /**
- * Parse the path of a sound based on properties of src matching with supported extensions.
- * Returns false if none of the properties are supported
- * @method _parseSrc
- * @param {Object} value The paths to an audio source, indexed by extension type.
- * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
- * and returned to a preloader like PreloadJS.
- * @protected
- * @static
- */
- s._parseSrc = function (value) {
- var ret = {name:undefined, src:undefined, extension:undefined};
- var c = s.capabilities;
-
- for (var prop in value) {
- if(value.hasOwnProperty(prop) && c[prop]) {
- ret.src = value[prop];
- ret.extension = prop;
- break;
- }
- }
- if (!ret.src) {return false;} // no matches
-
- var i = ret.src.lastIndexOf("/");
- if (i != -1) {
- ret.name = ret.src.slice(i+1);
- } else {
- ret.name = ret.src;
- }
-
- return ret;
- };
-
- /* ---------------
- Static API.
- --------------- */
- /**
- * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a
- * AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}.
- * Note that even on sounds with failed playback, you may still be able to call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
- * since the failure could be due to lack of available channels. If the src does not have a supported extension or
- * if there is no available plugin, a default AbstractSoundInstance will be returned which will not play any audio, but will not generate errors.
- *
- *
Example
- *
- * createjs.Sound.on("fileload", handleLoad);
- * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
- * function handleLoad(event) {
- * createjs.Sound.play("myID");
- * // store off AbstractSoundInstance for controlling
- * var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
- * }
- *
- * 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.
- *
- * Parameters Deprecated
- * The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
- *
- * @method play
- * @param {String} src The src or ID of the audio.
- * @param {String | Object} [interrupt="none"|options] This parameter will be renamed playProps in the next release.
- * This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
- * including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
- * OR
- * Deprecated How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
- * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- * @param {Number} [delay=0] Deprecated The amount of time to delay the start of audio playback, in milliseconds.
- * @param {Number} [offset=0] Deprecated The offset from the start of the audio to begin playback, in milliseconds.
- * @param {Number} [loop=0] Deprecated How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- * @param {Number} [volume=1] Deprecated The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- * @param {Number} [pan=0] Deprecated The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- * @param {Number} [startTime=null] Deprecated To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- * @param {Number} [duration=null] Deprecated To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
- * @static
- */
- s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) {
- var playProps;
- if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
- playProps = createjs.PlayPropsConfig.create(interrupt);
- } else {
- playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan, startTime:startTime, duration:duration});
- }
- var instance = s.createInstance(src, playProps.startTime, playProps.duration);
- var ok = s._playInstance(instance, playProps);
- if (!ok) {instance._playFailed();}
- return instance;
- };
-
- /**
- * Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a
- * supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be
- * called safely but does nothing.
- *
- *
Example
- *
- * var myInstance = null;
- * createjs.Sound.on("fileload", handleLoad);
- * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
- * function handleLoad(event) {
- * myInstance = createjs.Sound.createInstance("myID");
- * // alternately we could call the following
- * 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 {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
- * Unsupported extensions will return the default AbstractSoundInstance.
- * @since 0.4.0
- * @static
- */
- s.createInstance = function (src, startTime, duration) {
- if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}
-
- var defaultPlayProps = s._defaultPlayPropsHash[src]; // for audio sprites, which create and store defaults by id
- src = s._getSrcById(src);
-
- var details = s._parsePath(src.src);
-
- var instance = null;
- if (details != null && details.src != null) {
- SoundChannel.create(details.src);
- if (startTime == null) {startTime = src.startTime;}
- instance = s.activePlugin.create(details.src, startTime, duration || src.duration);
-
- defaultPlayProps = defaultPlayProps || s._defaultPlayPropsHash[details.src];
- if(defaultPlayProps) {
- instance.applyPlayProps(defaultPlayProps);
- }
- } else {
- instance = new createjs.DefaultSoundInstance(src, startTime, duration);
- }
-
- instance.uniqueId = s._lastID++;
-
- return instance;
- };
-
- /**
- * Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped,
- * call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
- *
- *
Example
- *
- * createjs.Sound.stop();
- *
- * @method stop
- * @static
- */
- s.stop = function () {
- var instances = this._instances;
- for (var i = instances.length; i--; ) {
- instances[i].stop(); // NOTE stop removes instance from this._instances
- }
- };
-
- /**
- * Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
- *
- * @method setVolume
- * @param {Number} value The master volume value. The acceptable range is 0-1.
- * @static
- * @deprecated
- */
- s.setVolume = function (value) {
- 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;
- for (var i = 0, l = instances.length; i < l; i++) {
- instances[i].setMasterVolume(value);
- }
- }
- };
-
- /**
- * Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
- *
- * @method getVolume
- * @return {Number} The master volume, in a range of 0-1.
- * @static
- * @deprecated
- */
- s.getVolume = function () {
- return this._masterVolume;
- };
-
- /**
- * Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
- *
- * @method setMute
- * @param {Boolean} value Whether the audio should be muted or not.
- * @return {Boolean} If the mute was set.
- * @static
- * @since 0.4.0
- * @deprecated
- */
- s.setMute = function (value) {
- if (value == null) {return false;}
-
- this._masterMute = value;
- if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
- var instances = this._instances;
- for (var i = 0, l = instances.length; i < l; i++) {
- instances[i].setMasterMute(value);
- }
- }
- return true;
- };
-
- /**
- * Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
- *
- * @method getMute
- * @return {Boolean} The mute value of Sound.
- * @static
- * @since 0.4.0
- * @deprecated
- */
- s.getMute = function () {
- return this._masterMute;
- };
-
- /**
- * Set the default playback properties for all new SoundInstances of the passed in src or ID.
- * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
- *
- * @method setDefaultPlayProps
- * @param {String} src The src or ID used to register the audio.
- * @param {Object | PlayPropsConfig} playProps The playback properties you would like to set.
- * @since 0.6.1
- */
- s.setDefaultPlayProps = function(src, playProps) {
- src = s._getSrcById(src);
- s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
- };
-
- /**
- * Get the default playback properties for the passed in src or ID. These properties are applied to all
- * new SoundInstances. Returns null if default does not exist.
- *
- * @method getDefaultPlayProps
- * @param {String} src The src or ID used to register the audio.
- * @returns {PlayPropsConfig} returns an existing PlayPropsConfig or null if one does not exist
- * @since 0.6.1
- */
- s.getDefaultPlayProps = function(src) {
- src = s._getSrcById(src);
- return s._defaultPlayPropsHash[s._parsePath(src.src).src];
- };
-
-
- /* ---------------
- Internal methods
- --------------- */
- /**
- * Play an instance. This is called by the static API, as well as from plugins. This allows the core class to
- * control delays.
- * @method _playInstance
- * @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
- * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
- * @return {Boolean} If the sound can start playing. Sounds that fail immediately will return false. Sounds that
- * have a delay will return true, but may still fail to play.
- * @protected
- * @static
- */
- s._playInstance = function (instance, playProps) {
- var defaultPlayProps = s._defaultPlayPropsHash[instance.src] || {};
- if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || s.defaultInterruptBehavior};
- if (playProps.delay == null) {playProps.delay = defaultPlayProps.delay || 0;}
- if (playProps.offset == null) {playProps.offset = instance.getPosition();}
- if (playProps.loop == null) {playProps.loop = instance.loop;}
- if (playProps.volume == null) {playProps.volume = instance.volume;}
- if (playProps.pan == null) {playProps.pan = instance.pan;}
-
- if (playProps.delay == 0) {
- var ok = s._beginPlaying(instance, playProps);
- 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
- var delayTimeoutId = setTimeout(function () {
- s._beginPlaying(instance, playProps);
- }, playProps.delay);
- instance.delayTimeoutId = delayTimeoutId;
- }
-
- this._instances.push(instance);
-
- return true;
- };
-
- /**
- * Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}.
- * @method _beginPlaying
- * @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback.
- * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
- * @return {Boolean} If the sound can start playing. If there are no available channels, or the instance fails to
- * start, this will return false.
- * @protected
- * @static
- */
- s._beginPlaying = function (instance, playProps) {
- if (!SoundChannel.add(instance, playProps.interrupt)) {
- return false;
- }
- var result = instance._beginPlaying(playProps);
- if (!result) {
- var index = createjs.indexOf(this._instances, instance);
- if (index > -1) {this._instances.splice(index, 1);}
- return false;
- }
- return true;
- };
-
- /**
- * Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned
- * instead.
- * @method _getSrcById
- * @param {String} value The ID the sound was registered with.
- * @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) {
- return s._idHash[value] || {src: value};
- };
-
- /**
- * A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from
- * Sound management. It will be added again, if the sound re-plays. Note that this method is called from the
- * instances themselves.
- * @method _playFinished
- * @param {AbstractSoundInstance} instance The instance that finished playback.
- * @protected
- * @static
- */
- s._playFinished = function (instance) {
- SoundChannel.remove(instance);
- var index = createjs.indexOf(this._instances, instance);
- 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;
-
- /**
- * An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for
- * each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class.
- *
- * The number of sounds is artificially limited by Sound in order to prevent over-saturation of a
- * single sound, as well as to stay within hardware limitations, although the latter may disappear with better
- * browser support.
- *
- * When a sound is played, this class ensures that there is an available instance, or interrupts an appropriate
- * sound that is already playing.
- * #class SoundChannel
- * @param {String} src The source of the instances
- * @param {Number} [max=1] The number of instances allowed
- * @constructor
- * @protected
- */
- function SoundChannel(src, max) {
- this.init(src, max);
- }
-
- /* ------------
- Static API
- ------------ */
- /**
- * A hash of channel instances indexed by source.
- * #property channels
- * @type {Object}
- * @static
- */
- SoundChannel.channels = {};
-
- /**
- * Create a sound channel. Note that if the sound channel already exists, this will fail.
- * #method create
- * @param {String} src The source for the channel
- * @param {Number} max The maximum amount this channel holds. The default is {{#crossLink "SoundChannel.maxDefault"}}{{/crossLink}}.
- * @return {Boolean} If the channels were created.
- * @static
- */
- SoundChannel.create = function (src, max) {
- var channel = SoundChannel.get(src);
- if (channel == null) {
- SoundChannel.channels[src] = new SoundChannel(src, max);
- return true;
- }
- return false;
- };
- /**
- * Delete a sound channel, stop and delete all related instances. Note that if the sound channel does not exist, this will fail.
- * #method remove
- * @param {String} src The source for the channel
- * @return {Boolean} If the channels were deleted.
- * @static
- */
- SoundChannel.removeSrc = function (src) {
- var channel = SoundChannel.get(src);
- if (channel == null) {return false;}
- channel._removeAll(); // this stops and removes all active instances
- delete(SoundChannel.channels[src]);
- return true;
- };
- /**
- * Delete all sound channels, stop and delete all related instances.
- * #method removeAll
- * @static
- */
- SoundChannel.removeAll = function () {
- for(var channel in SoundChannel.channels) {
- SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances
- }
- SoundChannel.channels = {};
- };
- /**
- * Add an instance to a sound channel.
- * #method add
- * @param {AbstractSoundInstance} instance The instance to add to the channel
- * @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}}
- * for details on interrupt modes.
- * @return {Boolean} The success of the method call. If the channel is full, it will return false.
- * @static
- */
- SoundChannel.add = function (instance, interrupt) {
- var channel = SoundChannel.get(instance.src);
- if (channel == null) {return false;}
- return channel._add(instance, interrupt);
- };
- /**
- * Remove an instance from the channel.
- * #method remove
- * @param {AbstractSoundInstance} instance The instance to remove from the channel
- * @return The success of the method call. If there is no channel, it will return false.
- * @static
- */
- SoundChannel.remove = function (instance) {
- var channel = SoundChannel.get(instance.src);
- if (channel == null) {return false;}
- channel._remove(instance);
- return true;
- };
- /**
- * Get the maximum number of sounds you can have in a channel.
- * #method maxPerChannel
- * @return {Number} The maximum number of sounds you can have in a channel.
- */
- SoundChannel.maxPerChannel = function () {
- return p.maxDefault;
- };
- /**
- * Get a channel instance by its src.
- * #method get
- * @param {String} src The src to use to look up the channel
- * @static
- */
- SoundChannel.get = function (src) {
- return SoundChannel.channels[src];
- };
-
- var p = SoundChannel.prototype;
- p.constructor = SoundChannel;
-
- /**
- * REMOVED. Removed in favor of using `MySuperClass_constructor`.
- * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
- * for details.
- *
- * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
- *
- * @method initialize
- * @protected
- * @deprecated
- */
- // p.initialize = function() {}; // searchable for devs wondering where it is.
-
-
- /**
- * The source of the channel.
- * #property src
- * @type {String}
- */
- p.src = null;
-
- /**
- * The maximum number of instances in this channel. -1 indicates no limit
- * #property max
- * @type {Number}
- */
- p.max = null;
-
- /**
- * The default value to set for max, if it isn't passed in. Also used if -1 is passed.
- * #property maxDefault
- * @type {Number}
- * @default 100
- * @since 0.4.0
- */
- p.maxDefault = 100;
-
- /**
- * The current number of active instances.
- * #property length
- * @type {Number}
- */
- p.length = 0;
-
- /**
- * Initialize the channel.
- * #method init
- * @param {String} src The source of the channel
- * @param {Number} max The maximum number of instances in the channel
- * @protected
- */
- p.init = function (src, max) {
- this.src = src;
- this.max = max || this.maxDefault;
- if (this.max == -1) {this.max = this.maxDefault;}
- this._instances = [];
- };
-
- /**
- * Get an instance by index.
- * #method get
- * @param {Number} index The index to return.
- * @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
- */
- p._get = function (index) {
- return this._instances[index];
- };
-
- /**
- * Add a new instance to the channel.
- * #method add
- * @param {AbstractSoundInstance} 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;}
- this._instances.push(instance);
- this.length++;
- return true;
- };
-
- /**
- * Remove an instance from the channel, either when it has finished playing, or it has been interrupted.
- * #method remove
- * @param {AbstractSoundInstance} instance The instance to remove
- * @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) {
- var index = createjs.indexOf(this._instances, instance);
- if (index == -1) {return false;}
- this._instances.splice(index, 1);
- this.length--;
- return true;
- };
-
- /**
- * 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
- for (var i=this.length-1; i>=0; i--) {
- this._instances[i].stop();
- }
- };
-
- /**
- * Get an available slot depending on interrupt value and if slots are available.
- * #method getSlot
- * @param {String} interrupt The interrupt value to use.
- * @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful.
- * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
- * an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false.
- */
- p._getSlot = function (interrupt, instance) {
- var target, replacement;
-
- if (interrupt != Sound.INTERRUPT_NONE) {
- // First replacement candidate
- replacement = this._get(0);
- if (replacement == null) {
- return true;
- }
- }
-
- for (var i = 0, l = this.max; i < l; i++) {
- target = this._get(i);
-
- // Available Space
- if (target == null) {
- return true;
- }
-
- // Audio is complete or not playing
- if (target.playState == Sound.PLAY_FINISHED ||
- target.playState == Sound.PLAY_INTERRUPTED ||
- target.playState == Sound.PLAY_FAILED) {
- replacement = target;
- break;
- }
-
- if (interrupt == Sound.INTERRUPT_NONE) {
- continue;
- }
-
- // Audio is a better candidate than the current target, according to playhead
- 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);
- return true;
- }
- return false;
- };
-
- p.toString = function () {
- return "[Sound SoundChannel]";
- };
- // do not add SoundChannel to namespace
-
-}());
-
-//##############################################################################
-// AbstractSoundInstance.js
-//##############################################################################
-
-this.createjs = this.createjs || {};
-
-/**
- * A AbstractSoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or
- * {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The AbstractSoundInstance is returned by the active plugin
- * for control by the user.
- *
- *
Example
- *
- * 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
- * API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments.
- *
- * Once a AbstractSoundInstance is created, a reference can be stored that can be used to control the audio directly through
- * the AbstractSoundInstance. If the reference is not stored, the AbstractSoundInstance will play out its audio (and any loops), and
- * is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio
- * playback has completed, a simple call to the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} instance method
- * will rebuild the references the Sound class need to control it.
- *
- * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2});
- * myInstance.on("loop", handleLoop);
- * function handleLoop(event) {
- * myInstance.volume = myInstance.volume * 0.5;
- * }
- *
- * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails
- *
- * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
- * myInstance.on("complete", handleComplete);
- * myInstance.on("loop", handleLoop);
- * myInstance.on("failed", handleFailed);
- *
- *
- * @class AbstractSoundInstance
- * @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} playbackResource Any resource needed by plugin to support audio playback.
- * @extends EventDispatcher
- * @constructor
- */
-
-(function () {
- "use strict";
-
-
-// Constructor:
- var AbstractSoundInstance = function (src, startTime, duration, playbackResource) {
- this.EventDispatcher_constructor();
-
-
- // public properties:
- /**
- * The source of the sound.
- * @property src
- * @type {String}
- * @default null
- */
- this.src = src;
-
- /**
- * The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}.
- * @property uniqueId
- * @type {String} | Number
- * @default -1
- */
- this.uniqueId = -1;
-
- /**
- * The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}.
- * @property playState
- * @type {String}
- * @default null
- */
- this.playState = null;
-
- /**
- * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this AbstractSoundInstance is played with a delay.
- * This allows AbstractSoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins.
- * @property delayTimeoutId
- * @type {timeoutVariable}
- * @default null
- * @protected
- * @since 0.4.0
- */
- this.delayTimeoutId = null;
- // TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins
-
-
- // private properties
- // Getter / Setter Properties
- // OJR TODO find original reason that we didn't use defined functions. I think it was performance related
- /**
- * The volume of the sound, between 0 and 1.
- *
- * The actual output volume of a sound can be calculated using:
- * myInstance.volume * createjs.Sound.getVolume();
- *
- * @property volume
- * @type {Number}
- * @default 1
- */
- this._volume = 1;
- Object.defineProperty(this, "volume", {
- get: this.getVolume,
- set: this.setVolume
- });
-
- /**
- * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
- *
- * Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio.
- *
- * @property pan
- * @type {Number}
- * @default 0
- */
- this._pan = 0;
- Object.defineProperty(this, "pan", {
- get: this.getPan,
- set: this.setPan
- });
-
- /**
- * Audio sprite property used to determine the starting offset.
- * @property startTime
- * @type {Number}
- * @default 0
- * @since 0.6.1
- */
- this._startTime = Math.max(0, startTime || 0);
- Object.defineProperty(this, "startTime", {
- get: this.getStartTime,
- set: this.setStartTime
- });
-
- /**
- * The length of the audio clip, in milliseconds.
- *
- * @property duration
- * @type {Number}
- * @default 0
- * @since 0.6.0
- */
- this._duration = Math.max(0, duration || 0);
- Object.defineProperty(this, "duration", {
- get: this.getDuration,
- set: this.setDuration
- });
-
- /**
- * Object that holds plugin specific resource need for audio playback.
- * This is set internally by the plugin. For example, WebAudioPlugin will set an array buffer,
- * HTMLAudioPlugin will set a tag, FlashAudioPlugin will set a flash reference.
- *
- * @property playbackResource
- * @type {Object}
- * @default null
- */
- this._playbackResource = null;
- Object.defineProperty(this, "playbackResource", {
- get: this.getPlaybackResource,
- set: this.setPlaybackResource
- });
- if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
-
- /**
- * The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
- *
- * @property position
- * @type {Number}
- * @default 0
- * @since 0.6.0
- */
- this._position = 0;
- Object.defineProperty(this, "position", {
- get: this.getPosition,
- set: this.setPosition
- });
-
- /**
- * The number of play loops remaining. Negative values will loop infinitely.
- *
- * @property loop
- * @type {Number}
- * @default 0
- * @public
- * @since 0.6.0
- */
- this._loop = 0;
- Object.defineProperty(this, "loop", {
- get: this.getLoop,
- set: this.setLoop
- });
-
- /**
- * Determines if the audio is currently muted.
- *
- * @property muted
- * @type {Boolean}
- * @default false
- * @since 0.6.0
- */
- this._muted = false;
- Object.defineProperty(this, "muted", {
- get: this.getMuted,
- set: this.setMuted
- });
-
- /**
- * Tells you if the audio is currently paused.
- *
- * @property paused
- * @type {Boolean}
- */
- this._paused = false;
- Object.defineProperty(this, "paused", {
- get: this.getPaused,
- set: this.setPaused
- });
-
-
- // Events
- /**
- * The event that is fired when playback has started successfully.
- * @event succeeded
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.4.0
- */
-
- /**
- * The event that is fired when playback is interrupted. This happens when another sound with the same
- * src property is played using an interrupt value that causes this instance to stop playing.
- * @event interrupted
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.4.0
- */
-
- /**
- * The event that is fired when playback has failed. This happens when there are too many channels with the same
- * src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or
- * the sound could not be played, perhaps due to a 404 error.
- * @event failed
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.4.0
- */
-
- /**
- * The event that is fired when a sound has completed playing but has loops remaining.
- * @event loop
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.4.0
- */
-
- /**
- * The event that is fired when playback completes. This means that the sound has finished playing in its
- * entirety, including its loop iterations.
- * @event complete
- * @param {Object} target The object that dispatched the event.
- * @param {String} type The event type.
- * @since 0.4.0
- */
- };
-
- var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// Public Methods:
- /**
- * Play an instance. This method is intended to be called on SoundInstances that already exist (created
- * with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}).
- *
- *
Example
- *
- * var myInstance = createjs.Sound.createInstance(mySrc);
- * myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
- *
- * Note that if this sound is already playing, this call will still set the passed in parameters.
-
- * Parameters Deprecated
- * The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
- *
- * @method play
- * @param {String | Object} [interrupt="none"|options] This parameter will be renamed playProps in the next release.
- * This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
- * including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
- * OR
- * Deprecated How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
- * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- * @param {Number} [delay=0] Deprecated The amount of time to delay the start of audio playback, in milliseconds.
- * @param {Number} [offset=0] Deprecated The offset from the start of the audio to begin playback, in milliseconds.
- * @param {Number} [loop=0] Deprecated How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- * @param {Number} [volume=1] Deprecated The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- * @param {Number} [pan=0] Deprecated The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- * Note that pan is not supported for HTML Audio.
- * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
- */
- p.play = function (interrupt, delay, offset, loop, volume, pan) {
- var playProps;
- if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
- playProps = createjs.PlayPropsConfig.create(interrupt);
- } else {
- playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan});
- }
-
- if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this.applyPlayProps(playProps);
- if (this._paused) { this.setPaused(false); }
- return;
- }
- this._cleanUp();
- createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
- return this;
- };
-
- /**
- * Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "AbstractSoundInstance/resume"}}{{/crossLink}}
- * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
- *
- *
Example
- *
- * myInstance.stop();
- *
- * @method stop
- * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
- */
- p.stop = function () {
- this._position = 0;
- this._paused = false;
- this._handleStop();
- this._cleanUp();
- this.playState = createjs.Sound.PLAY_FINISHED;
- return this;
- };
-
- /**
- * Remove all external references and resources from AbstractSoundInstance. Note this is irreversible and AbstractSoundInstance will no longer work
- * @method destroy
- * @since 0.6.0
- */
- p.destroy = function() {
- this._cleanUp();
- this.src = null;
- this.playbackResource = null;
-
- this.removeAllEventListeners();
- };
-
- /**
- * Takes an PlayPropsConfig or Object with the same properties and sets them on this instance.
- * @method applyPlayProps
- * @param {PlayPropsConfig | Object} playProps A PlayPropsConfig or object containing the same properties.
- * @since 0.6.1
- * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
- */
- p.applyPlayProps = function(playProps) {
- if (playProps.offset != null) { this.setPosition(playProps.offset) }
- if (playProps.loop != null) { this.setLoop(playProps.loop); }
- if (playProps.volume != null) { this.setVolume(playProps.volume); }
- if (playProps.pan != null) { this.setPan(playProps.pan); }
- if (playProps.startTime != null) {
- this.setStartTime(playProps.startTime);
- this.setDuration(playProps.duration);
- }
- return this;
- };
-
- p.toString = function () {
- return "[AbstractSoundInstance]";
- };
-
-// get/set methods that allow support for IE8
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property,
- *
- * @deprecated
- * @method getPaused
- * @returns {boolean} If the instance is currently paused
- * @since 0.6.0
- */
- p.getPaused = function() {
- return this._paused;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setPaused
- * @param {boolean} value
- * @since 0.6.0
- * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
- */
- p.setPaused = function (value) {
- if ((value !== true && value !== false) || this._paused == value) {return;}
- if (value == true && this.playState != createjs.Sound.PLAY_SUCCEEDED) {return;}
- this._paused = value;
- if(value) {
- this._pause();
- } else {
- this._resume();
- }
- clearTimeout(this.delayTimeoutId);
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setVolume
- * @param {Number} value The volume to set, between 0 and 1.
- * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
- */
- p.setVolume = function (value) {
- if (value == this._volume) { return this; }
- this._volume = Math.max(0, Math.min(1, value));
- if (!this._muted) {
- this._updateVolume();
- }
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getVolume
- * @return {Number} The current volume of the sound instance.
- */
- p.getVolume = function () {
- return this._volume;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setMuted
- * @param {Boolean} value If the sound should be muted.
- * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
- * @since 0.6.0
- */
- p.setMuted = function (value) {
- if (value !== true && value !== false) {return;}
- this._muted = value;
- this._updateVolume();
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getMuted
- * @return {Boolean} If the sound is muted.
- * @since 0.6.0
- */
- p.getMuted = function () {
- return this._muted;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setPan
- * @param {Number} value The pan value, between -1 (left) and 1 (right).
- * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
- */
- p.setPan = function (value) {
- if(value == this._pan) { return this; }
- this._pan = Math.max(-1, Math.min(1, value));
- this._updatePan();
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getPan
- * @return {Number} The value of the pan, between -1 (left) and 1 (right).
- */
- p.getPan = function () {
- return this._pan;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getPosition
- * @return {Number} The position of the playhead in the sound, in milliseconds.
- */
- p.getPosition = function () {
- if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this._position = this._calculateCurrentPosition();
- }
- return this._position;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setPosition
- * @param {Number} value The position to place the playhead, in milliseconds.
- * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
- */
- p.setPosition = function (value) {
- this._position = Math.max(0, value);
- if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this._updatePosition();
- }
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getStartTime
- * @return {Number} The startTime of the sound instance in milliseconds.
- */
- p.getStartTime = function () {
- return this._startTime;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setStartTime
- * @param {number} value The new startTime time in milli seconds.
- * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
- */
- p.setStartTime = function (value) {
- if (value == this._startTime) { return this; }
- this._startTime = Math.max(0, value || 0);
- this._updateStartTime();
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getDuration
- * @return {Number} The duration of the sound instance in milliseconds.
- */
- p.getDuration = function () {
- return this._duration;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setDuration
- * @param {number} value The new duration time in milli seconds.
- * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
- * @since 0.6.0
- */
- p.setDuration = function (value) {
- if (value == this._duration) { return this; }
- this._duration = Math.max(0, value || 0);
- this._updateDuration();
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setPlayback
- * @param {Object} value The new playback resource.
- * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
- * @since 0.6.0
- **/
- p.setPlaybackResource = function (value) {
- this._playbackResource = value;
- if (this._duration == 0) { this._setDurationFromSource(); }
- return this;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method setPlayback
- * @param {Object} value The new playback resource.
- * @return {Object} playback resource used for playing audio
- * @since 0.6.0
- **/
- p.getPlaybackResource = function () {
- return this._playbackResource;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
- *
- * @deprecated
- * @method getLoop
- * @return {number}
- * @since 0.6.0
- **/
- p.getLoop = function () {
- return this._loop;
- };
-
- /**
- * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property,
- *
- * @deprecated
- * @method setLoop
- * @param {number} value The number of times to loop after play.
- * @since 0.6.0
- */
- p.setLoop = function (value) {
- if(this._playbackResource != null) {
- // remove looping
- if (this._loop != 0 && value == 0) {
- this._removeLooping(value);
- }
- // add looping
- else if (this._loop == 0 && value != 0) {
- this._addLooping(value);
- }
- }
- this._loop = value;
- };
-
-
-// Private Methods:
- /**
- * A helper method that dispatches all events for AbstractSoundInstance.
- * @method _sendEvent
- * @param {String} type The event type
- * @protected
- */
- p._sendEvent = function (type) {
- var event = new createjs.Event(type);
- this.dispatchEvent(event);
- };
-
- /**
- * Clean up the instance. Remove references and clean up any additional properties such as timers.
- * @method _cleanUp
- * @protected
- */
- p._cleanUp = function () {
- clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
- this._handleCleanUp();
- this._paused = false;
-
- createjs.Sound._playFinished(this); // TODO change to an event
- };
-
- /**
- * The sound has been interrupted.
- * @method _interrupt
- * @protected
- */
- p._interrupt = function () {
- this._cleanUp();
- this.playState = createjs.Sound.PLAY_INTERRUPTED;
- this._sendEvent("interrupted");
- };
-
- /**
- * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the
- * src is loaded, otherwise playback will fail.
- * @method _beginPlaying
- * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
- * @return {Boolean} If playback succeeded.
- * @protected
- */
- // OJR FlashAudioSoundInstance overwrites
- p._beginPlaying = function (playProps) {
- this.setPosition(playProps.offset);
- this.setLoop(playProps.loop);
- this.setVolume(playProps.volume);
- this.setPan(playProps.pan);
- if (playProps.startTime != null) {
- this.setStartTime(playProps.startTime);
- this.setDuration(playProps.duration);
- }
-
- if (this._playbackResource != null && this._position < this._duration) {
- this._paused = false;
- this._handleSoundReady();
- this.playState = createjs.Sound.PLAY_SUCCEEDED;
- this._sendEvent("succeeded");
- return true;
- } else {
- this._playFailed();
- return false;
- }
- };
-
- /**
- * Play has failed, which can happen for a variety of reasons.
- * Cleans up instance and dispatches failed event
- * @method _playFailed
- * @private
- */
- p._playFailed = function () {
- this._cleanUp();
- this.playState = createjs.Sound.PLAY_FAILED;
- this._sendEvent("failed");
- };
-
- /**
- * Audio has finished playing. Manually loop it if required.
- * @method _handleSoundComplete
- * @param event
- * @protected
- */
- p._handleSoundComplete = function (event) {
- this._position = 0; // have to set this as it can be set by pause during playback
-
- if (this._loop != 0) {
- this._loop--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1
- this._handleLoop();
- this._sendEvent("loop");
- return;
- }
-
- this._cleanUp();
- this.playState = createjs.Sound.PLAY_FINISHED;
- this._sendEvent("complete");
- };
-
-// Plugin specific code
- /**
- * Handles starting playback when the sound is ready for playing.
- * @method _handleSoundReady
- * @protected
- */
- p._handleSoundReady = function () {
- // plugin specific code
- };
-
- /**
- * Internal function used to update the volume based on the instance volume, master volume, instance mute value,
- * and master mute value.
- * @method _updateVolume
- * @protected
- */
- p._updateVolume = function () {
- // plugin specific code
- };
-
- /**
- * Internal function used to update the pan
- * @method _updatePan
- * @protected
- * @since 0.6.0
- */
- p._updatePan = function () {
- // plugin specific code
- };
-
- /**
- * Internal function used to update the startTime of the audio.
- * @method _updateStartTime
- * @protected
- * @since 0.6.1
- */
- p._updateStartTime = function () {
- // plugin specific code
- };
-
- /**
- * Internal function used to update the duration of the audio.
- * @method _updateDuration
- * @protected
- * @since 0.6.0
- */
- p._updateDuration = function () {
- // plugin specific code
- };
-
- /**
- * Internal function used to get the duration of the audio from the source we'll be playing.
- * @method _updateDuration
- * @protected
- * @since 0.6.0
- */
- p._setDurationFromSource = function () {
- // plugin specific code
- };
-
- /**
- * Internal function that calculates the current position of the playhead and sets this._position to that value
- * @method _calculateCurrentPosition
- * @protected
- * @since 0.6.0
- */
- p._calculateCurrentPosition = function () {
- // plugin specific code that sets this.position
- };
-
- /**
- * Internal function used to update the position of the playhead.
- * @method _updatePosition
- * @protected
- * @since 0.6.0
- */
- p._updatePosition = function () {
- // plugin specific code
- };
-
- /**
- * Internal function called when looping is removed during playback.
- * @method _removeLooping
- * @param {number} value The number of times to loop after play.
- * @protected
- * @since 0.6.0
- */
- p._removeLooping = function (value) {
- // plugin specific code
- };
-
- /**
- * Internal function called when looping is added during playback.
- * @method _addLooping
- * @param {number} value The number of times to loop after play.
- * @protected
- * @since 0.6.0
- */
- p._addLooping = function (value) {
- // plugin specific code
- };
-
- /**
- * Internal function called when pausing playback
- * @method _pause
- * @protected
- * @since 0.6.0
- */
- p._pause = function () {
- // plugin specific code
- };
-
- /**
- * Internal function called when resuming playback
- * @method _resume
- * @protected
- * @since 0.6.0
- */
- p._resume = function () {
- // plugin specific code
- };
-
- /**
- * Internal function called when stopping playback
- * @method _handleStop
- * @protected
- * @since 0.6.0
- */
- p._handleStop = function() {
- // plugin specific code
- };
-
- /**
- * Internal function called when AbstractSoundInstance is being cleaned up
- * @method _handleCleanUp
- * @protected
- * @since 0.6.0
- */
- p._handleCleanUp = function() {
- // plugin specific code
- };
-
- /**
- * Internal function called when AbstractSoundInstance has played to end and is looping
- * @method _handleLoop
- * @protected
- * @since 0.6.0
- */
- p._handleLoop = function () {
- // plugin specific code
- };
-
- createjs.AbstractSoundInstance = createjs.promote(AbstractSoundInstance, "EventDispatcher");
- createjs.DefaultSoundInstance = createjs.AbstractSoundInstance; // used when no plugin is supported
+this.createjs = this.createjs || {};
+
+
+
+(function () {
+ "use strict";
+
+ /**
+ * 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.
+ *
+ * Registering and Preloading
+ * Before you can play a sound, it must be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}},
+ * or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/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
+ * PreloadJS, 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.
+ *
+ * Playback
+ * To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method.
+ * This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc.
+ * Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs.
+ *
+ * Plugins
+ * By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
+ * are used (when available), although developers can change plugin priority or add new plugins (such as the
+ * provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API
+ * methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see
+ * {{#crossLink "Sound/installPlugins"}}{{/crossLink}}.
+ *
+ *
Example
+ *
+ * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
+ * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]);
+ * createjs.Sound.alternateExtensions = ["mp3"];
+ * createjs.Sound.on("fileload", createjs.proxy(this.loadHandler, (this)));
+ * createjs.Sound.registerSound("path/to/mySound.ogg", "sound");
+ * function loadHandler(event) {
+ * // This is fired for each sound that is registered.
+ * var instance = createjs.Sound.play("sound"); // play using id. Could also use full source path or event.src.
+ * instance.on("complete", createjs.proxy(this.handleComplete, this));
+ * instance.volume = 0.5;
+ * }
+ *
+ * The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument
+ * of {{#crossLink "Sound/registerSound"}}{{/crossLink}}. Note that if not specified, the active plugin will apply
+ * a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a
+ * default limit of 100.
+ *
+ * createjs.Sound.registerSound("sound.mp3", "soundId", 4);
+ *
+ * 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 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.
+ *
+ * var queue = new createjs.LoadQueue();
+ * queue.installPlugin(createjs.Sound);
+ *
+ * Audio Sprites
+ * SoundJS has added support for {{#crossLink "AudioSprite"}}{{/crossLink}}, available as of version 0.6.0.
+ * For those unfamiliar with audio sprites, they are much like CSS sprites or sprite sheets: multiple audio assets
+ * grouped into a single file.
+ *
+ *
Example
+ *
+ * var assetsPath = "./assets/";
+ * var sounds = [{
+ * src:"MyAudioSprite.ogg", data: {
+ * audioSprite: [
+ * {id:"sound1", startTime:0, duration:500},
+ * {id:"sound2", startTime:1000, duration:400},
+ * {id:"sound3", startTime:1700, duration: 1000}
+ * ]}
+ * }
+ * ];
+ * createjs.Sound.alternateExtensions = ["mp3"];
+ * createjs.Sound.on("fileload", loadSound);
+ * createjs.Sound.registerSounds(sounds, assetsPath);
+ * // after load is complete
+ * createjs.Sound.play("sound2");
+ *
+ * Mobile Safe Approach
+ * Mobile devices require sounds to be played inside of a user initiated event (touch/click) in varying degrees.
+ * As of SoundJS 0.4.1, you can launch a site inside of a user initiated event and have audio playback work. To
+ * enable as broadly as possible, the site needs to setup the Sound plugin in its initialization (for example via
+ * createjs.Sound.initializeDefaultPlugins();), and all sounds need to be played in the scope of the
+ * application. See the MobileSafe demo for a working example.
+ *
+ *
Example
+ *
+ * document.getElementById("status").addEventListener("click", handleTouch, false); // works on Android and iPad
+ * function handleTouch(event) {
+ * document.getElementById("status").removeEventListener("click", handleTouch, false); // remove the listener
+ * var thisApp = new myNameSpace.MyApp(); // launch the app
+ * }
+ *
+ * Loading Alternate Paths and Extensionless Files
+ * SoundJS supports loading alternate paths and extensionless files by passing an object for src that has various paths
+ * with property labels matching the extension. These labels are how SoundJS determines if the browser will support the sound.
+ * Priority is determined by the property order (first property is tried first). This is supported by both internal loading
+ * and loading with PreloadJS.
+ *
+ * Note an id is required for playback.
+ *
+ *
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.
+ *
MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
+ * encoding with 64kbps works.
+ *
Occasionally very short samples will get cut off.
+ *
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.
+ *
+ * Firefox 25 Web Audio limitations
+ *
mp3 audio files do not load properly on all windows machines, reported
+ * here.
+ * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.
+
+ * Safari limitations
+ *
Safari requires Quicktime to be installed for audio playback.
+ *
+ * iOS 6 Web Audio limitations
+ *
Sound is initially muted and will only unmute through play being called inside a user initiated event
+ * (touch/click).
+ *
A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio at a different sampleRate.
+ *
+ *
+ * Android HTML Audio limitations
+ *
We have no control over audio volume. Only the user can set volume on their device.
+ *
We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
+ * a delay.
+ *
+ * Web Audio and PreloadJS
+ *
Web Audio must be loaded through XHR, therefore when used with PreloadJS tag loading is not possible. This means that tag loading cannot
+ * be used to avoid cross domain issues if WebAudioPlugin is used
+ *
+ * @class Sound
+ * @static
+ * @uses EventDispatcher
+ */
+ function Sound() {
+ throw "Sound cannot be instantiated";
+ }
+
+ var s = Sound;
+
+
+// Static Properties
+ /**
+ * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
+ * instances of the sound are already playing.
+ * @property INTERRUPT_ANY
+ * @type {String}
+ * @default any
+ * @static
+ */
+ s.INTERRUPT_ANY = "any";
+
+ /**
+ * The interrupt value to interrupt the earliest currently playing instance with the same source that progressed the
+ * least distance in the audio track, if the maximum number of instances of the sound are already playing.
+ * @property INTERRUPT_EARLY
+ * @type {String}
+ * @default early
+ * @static
+ */
+ s.INTERRUPT_EARLY = "early";
+
+ /**
+ * The interrupt value to interrupt the currently playing instance with the same source that progressed the most
+ * distance in the audio track, if the maximum number of instances of the sound are already playing.
+ * @property INTERRUPT_LATE
+ * @type {String}
+ * @default late
+ * @static
+ */
+ s.INTERRUPT_LATE = "late";
+
+ /**
+ * The interrupt value to not interrupt any currently playing instances with the same source, if the maximum number of
+ * instances of the sound are already playing.
+ * @property INTERRUPT_NONE
+ * @type {String}
+ * @default none
+ * @static
+ */
+ s.INTERRUPT_NONE = "none";
+
+ /**
+ * Defines the playState of an instance that is still initializing.
+ * @property PLAY_INITED
+ * @type {String}
+ * @default playInited
+ * @static
+ */
+ s.PLAY_INITED = "playInited";
+
+ /**
+ * Defines the playState of an instance that is currently playing or paused.
+ * @property PLAY_SUCCEEDED
+ * @type {String}
+ * @default playSucceeded
+ * @static
+ */
+ s.PLAY_SUCCEEDED = "playSucceeded";
+
+ /**
+ * Defines the playState of an instance that was interrupted by another instance.
+ * @property PLAY_INTERRUPTED
+ * @type {String}
+ * @default playInterrupted
+ * @static
+ */
+ s.PLAY_INTERRUPTED = "playInterrupted";
+
+ /**
+ * Defines the playState of an instance that completed playback.
+ * @property PLAY_FINISHED
+ * @type {String}
+ * @default playFinished
+ * @static
+ */
+ s.PLAY_FINISHED = "playFinished";
+
+ /**
+ * Defines the playState of an instance that failed to play. This is usually caused by a lack of available channels
+ * when the interrupt mode was "INTERRUPT_NONE", the playback stalled, or the sound could not be found.
+ * @property PLAY_FAILED
+ * @type {String}
+ * @default playFailed
+ * @static
+ */
+ s.PLAY_FAILED = "playFailed";
+
+ /**
+ * A list of the default supported extensions that Sound will try to play. Plugins will check if the browser
+ * can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to
+ * support additional media types.
+ *
+ * NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
+ *
+ * More details on file formats can be found at http://en.wikipedia.org/wiki/Audio_file_format.
+ * A very detailed list of file formats can be found at http://www.fileinfo.com/filetypes/audio.
+ * @property SUPPORTED_EXTENSIONS
+ * @type {Array[String]}
+ * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
+ * @since 0.4.0
+ * @static
+ */
+ s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "opus", "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
+ * that support so plugins can accurately determine if an extension is supported. Adding to this list can help
+ * plugins determine more accurately if an extension is supported.
+ *
+ * A useful list of extensions for each format can be found at http://html5doctor.com/html5-audio-the-state-of-play/.
+ * @property EXTENSION_MAP
+ * @type {Object}
+ * @since 0.4.0
+ * @default {m4a:"mp4"}
+ * @static
+ */
+ s.EXTENSION_MAP = {
+ m4a:"mp4"
+ };
+
+ /**
+ * The RegExp pattern used to parse file URIs. This supports simple file names, as well as full domain URIs with
+ * query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6.
+ * @property FILE_PATTERN
+ * @type {RegExp}
+ * @static
+ * @protected
+ */
+ s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
+
+
+// Class Public properties
+ /**
+ * Determines the default behavior for interrupting other currently playing instances with the same source, if the
+ * maximum number of instances of the sound are already playing. Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}
+ * but this can be set and will change playback behavior accordingly. This is only used when {{#crossLink "Sound/play"}}{{/crossLink}}
+ * is called without passing a value for interrupt.
+ * @property defaultInterruptBehavior
+ * @type {String}
+ * @default Sound.INTERRUPT_NONE, or "none"
+ * @static
+ * @since 0.4.0
+ */
+ s.defaultInterruptBehavior = s.INTERRUPT_NONE; // OJR does s.INTERRUPT_ANY make more sense as default? Needs game dev testing to see which case makes more sense.
+
+ /**
+ * An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin.
+ * These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your
+ * extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need
+ * to exist in the same location, as only the extension is altered.
+ *
+ * Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}}
+ * and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading.
+ *
+ *
Example
+ *
+ * var sounds = [
+ * {src:"myPath/mySound.ogg", id:"example"},
+ * ];
+ * createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3
+ * createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
+ * createjs.Sound.registerSounds(sounds, assetPath);
+ * // ...
+ * createjs.Sound.play("myPath/mySound.ogg"); // works regardless of what extension is supported. Note calling with ID is a better approach
+ *
+ * @property alternateExtensions
+ * @type {Array}
+ * @since 0.5.2
+ * @static
+ */
+ s.alternateExtensions = [];
+
+ /**
+ * The currently active plugin. If this is null, then no plugin could be initialized. If no plugin was specified,
+ * Sound attempts to apply the default plugins: {{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by
+ * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
+ * @property activePlugin
+ * @type {Object}
+ * @static
+ */
+ s.activePlugin = null;
+
+
+// class getter / setter properties
+ /**
+ * Set the master volume of Sound. The master volume is multiplied against each sound's individual volume. For
+ * example, if master volume is 0.5 and a sound's volume is 0.5, the resulting volume is 0.25. To set individual
+ * sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead.
+ *
+ *
Example
+ *
+ * createjs.Sound.volume = 0.5;
+ *
+ *
+ * @property volume
+ * @type {Number}
+ * @default 1
+ * @since 0.6.1
+ */
+ s._masterVolume = 1;
+ Object.defineProperty(s, "volume", {
+ get: function () {return this._masterVolume;},
+ set: function (value) {
+ 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;
+ for (var i = 0, l = instances.length; i < l; i++) {
+ instances[i].setMasterVolume(value);
+ }
+ }
+ }
+ });
+
+ /**
+ * 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
+ * instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
+ *
+ *
Example
+ *
+ * createjs.Sound.muted = true;
+ *
+ *
+ * @property muted
+ * @type {Boolean}
+ * @default false
+ * @since 0.6.1
+ */
+ s._masterMute = false;
+ // OJR references to the methods were not working, so the code had to be duplicated here
+ Object.defineProperty(s, "muted", {
+ get: function () {return this._masterMute;},
+ set: function (value) {
+ if (value == null) {return false;}
+
+ this._masterMute = value;
+ if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
+ var instances = this._instances;
+ for (var i = 0, l = instances.length; i < l; i++) {
+ instances[i].setMasterMute(value);
+ }
+ }
+ return true;
+ }
+ });
+
+ /**
+ * Get the active plugins capabilities, which help determine if a plugin can be used in the current environment,
+ * or if the plugin supports a specific feature. Capabilities include:
+ *
+ *
panning: If the plugin can pan audio from left to right
+ *
volume; If the plugin can control audio volume.
+ *
tracks: The maximum number of audio tracks that can be played back at a time. This will be -1
+ * if there is no known limit.
+ * An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
+ *
mp3: If MP3 audio is supported.
+ *
ogg: If OGG audio is supported.
+ *
wav: If WAV audio is supported.
+ *
mpeg: If MPEG audio is supported.
+ *
m4a: If M4A audio is supported.
+ *
mp4: If MP4 audio is supported.
+ *
aiff: If aiff audio is supported.
+ *
wma: If wma audio is supported.
+ *
mid: If mid audio is supported.
+ *
+ *
+ * You can get a specific capability of the active plugin using standard object notation
+ *
+ *
Example
+ *
+ * var mp3 = createjs.Sound.capabilities.mp3;
+ *
+ * Note this property is read only.
+ *
+ * @property capabilities
+ * @type {Object}
+ * @static
+ * @readOnly
+ * @since 0.6.1
+ */
+ Object.defineProperty(s, "capabilities", {
+ get: function () {
+ if (s.activePlugin == null) {return null;}
+ return s.activePlugin._capabilities;
+ },
+ set: function (value) { return false;}
+ });
+
+
+// Class Private properties
+ /**
+ * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
+ * plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}).
+ * If plugins have been registered, but none are applicable, then sound playback will fail.
+ * @property _pluginsRegistered
+ * @type {Boolean}
+ * @default false
+ * @static
+ * @protected
+ */
+ s._pluginsRegistered = false;
+
+ /**
+ * Used internally to assign unique IDs to each AbstractSoundInstance.
+ * @property _lastID
+ * @type {Number}
+ * @static
+ * @protected
+ */
+ s._lastID = 0;
+
+ /**
+ * An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
+ * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
+ * When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}}
+ * method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}}
+ * method.
+ * @property _instances
+ * @type {Array}
+ * @protected
+ * @static
+ */
+ s._instances = [];
+
+ /**
+ * An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
+ * @property _idHash
+ * @type {Object}
+ * @protected
+ * @static
+ */
+ s._idHash = {};
+
+ /**
+ * An object hash that stores preloading sound sources via the parsed source that is passed to the plugin. Contains the
+ * source, id, and data that was passed in by the user. Parsed sources can contain multiple instances of source, id,
+ * and data.
+ * @property _preloadHash
+ * @type {Object}
+ * @protected
+ * @static
+ */
+ s._preloadHash = {};
+
+ /**
+ * An object hash storing {{#crossLink "PlayPropsConfig"}}{{/crossLink}} via the parsed source that is passed as defaultPlayProps in
+ * {{#crossLink "Sound/registerSound"}}{{/crossLink}} and {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ * @property _defaultPlayPropsHash
+ * @type {Object}
+ * @protected
+ * @static
+ * @since 0.6.1
+ */
+ s._defaultPlayPropsHash = {};
+
+
+// EventDispatcher methods:
+ s.addEventListener = null;
+ s.removeEventListener = null;
+ s.removeAllEventListeners = null;
+ s.dispatchEvent = null;
+ s.hasEventListener = null;
+ s._listeners = null;
+
+ createjs.EventDispatcher.initialize(s); // inject EventDispatcher methods.
+
+
+// Events
+ /**
+ * This event is fired when a file finishes loading internally. This event is fired for each loaded sound,
+ * so any handler methods should look up the event.src to handle a particular sound.
+ * @event fileload
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @param {String} src The source of the sound that was loaded.
+ * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
+ * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
+ * @since 0.4.1
+ */
+
+ /**
+ * This event is fired when a file fails loading internally. This event is fired for each loaded sound,
+ * so any handler methods should look up the event.src to handle a particular sound.
+ * @event fileerror
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @param {String} src The source of the sound that was loaded.
+ * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
+ * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
+ * @since 0.6.0
+ */
+
+
+// Class Public Methods
+ /**
+ * Get the preload rules to allow Sound to be used as a plugin by PreloadJS.
+ * Any load calls that have the matching type or extension will fire the callback method, and use the resulting
+ * object, which is potentially modified by Sound. This helps when determining the correct path, as well as
+ * registering the audio instance(s) with Sound. This method should not be called, except by PreloadJS.
+ * @method getPreloadHandlers
+ * @return {Object} An object containing:
+ *
callback: A preload callback that is fired when a file is added to PreloadJS, which provides
+ * Sound a mechanism to modify the load parameters, select the correct file format, register the sound, etc.
+ *
types: A list of file types that are supported by Sound (currently supports "sound").
+ *
extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
+ * @static
+ * @protected
+ */
+ s.getPreloadHandlers = function () {
+ return {
+ callback:createjs.proxy(s.initLoad, s),
+ types:["sound"],
+ extensions:s.SUPPORTED_EXTENSIONS
+ };
+ };
+
+ /**
+ * Used to dispatch fileload events from internal loading.
+ * @method _handleLoadComplete
+ * @param event A loader event.
+ * @protected
+ * @static
+ * @since 0.6.0
+ */
+ s._handleLoadComplete = function(event) {
+ var src = event.target.getItem().src;
+ if (!s._preloadHash[src]) {return;}
+
+ for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
+ var item = s._preloadHash[src][i];
+ s._preloadHash[src][i] = true;
+
+ if (!s.hasEventListener("fileload")) { continue; }
+
+ var event = new createjs.Event("fileload");
+ event.src = item.src;
+ event.id = item.id;
+ event.data = item.data;
+ event.sprite = item.sprite;
+
+ s.dispatchEvent(event);
+ }
+ };
+
+ /**
+ * Used to dispatch error events from internal preloading.
+ * @param event
+ * @protected
+ * @since 0.6.0
+ * @static
+ */
+ s._handleLoadError = function(event) {
+ var src = event.target.getItem().src;
+ if (!s._preloadHash[src]) {return;}
+
+ for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
+ var item = s._preloadHash[src][i];
+ s._preloadHash[src][i] = false;
+
+ if (!s.hasEventListener("fileerror")) { continue; }
+
+ var event = new createjs.Event("fileerror");
+ event.src = item.src;
+ event.id = item.id;
+ event.data = item.data;
+ event.sprite = item.sprite;
+
+ s.dispatchEvent(event);
+ }
+ };
+
+ /**
+ * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
+ *
+ * @method _registerPlugin
+ * @param {Object} plugin The plugin class to install.
+ * @return {Boolean} Whether the plugin was successfully initialized.
+ * @static
+ * @private
+ */
+ s._registerPlugin = function (plugin) {
+ // 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();
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
+ *
+ *
Example
+ *
+ * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
+ * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
+ *
+ * @method registerPlugins
+ * @param {Array} plugins An array of plugins classes to install.
+ * @return {Boolean} Whether a plugin was successfully initialized.
+ * @static
+ */
+ s.registerPlugins = function (plugins) {
+ s._pluginsRegistered = true;
+ for (var i = 0, l = plugins.length; i < l; i++) {
+ if (s._registerPlugin(plugins[i])) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * Initialize the default plugins. This method is automatically called when any audio is played or registered before
+ * the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the
+ * default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
+ *
+ *
Example
+ *
+ * if (!createjs.initializeDefaultPlugins()) { return; }
+ *
+ * @method initializeDefaultPlugins
+ * @returns {Boolean} True if a plugin was initialized, false otherwise.
+ * @since 0.4.0
+ * @static
+ */
+ s.initializeDefaultPlugins = function () {
+ if (s.activePlugin != null) {return true;}
+ if (s._pluginsRegistered) {return false;}
+ if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
+ return false;
+ };
+
+ /**
+ * Determines if Sound has been initialized, and a plugin has been activated.
+ *
+ *
Example
+ * This example sets up a Flash fallback, but only if there is no plugin specified yet.
+ *
+ * if (!createjs.Sound.isReady()) {
+ * createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
+ * createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
+ * }
+ *
+ * @method isReady
+ * @return {Boolean} If Sound has initialized a plugin.
+ * @static
+ */
+ s.isReady = function () {
+ return (s.activePlugin != null);
+ };
+
+ /**
+ * Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
+ *
+ * @method getCapabilities
+ * @return {Object} An object containing the capabilities of the active plugin.
+ * @static
+ * @deprecated
+ */
+ s.getCapabilities = function () {
+ if (s.activePlugin == null) {return null;}
+ return s.activePlugin._capabilities;
+ };
+
+ /**
+ * Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
+ *
+ * @method getCapability
+ * @param {String} key The capability to retrieve
+ * @return {Number|Boolean} The value of the capability.
+ * @static
+ * @see getCapabilities
+ * @deprecated
+ */
+ s.getCapability = function (key) {
+ if (s.activePlugin == null) {return null;}
+ return s.activePlugin._capabilities[key];
+ };
+
+ /**
+ * Process manifest items from PreloadJS. This method is intended
+ * for usage by a plugin, and not for direct interaction.
+ * @method initLoad
+ * @param {Object} src The object to load.
+ * @return {Object|AbstractLoader} An instance of AbstractLoader.
+ * @protected
+ * @static
+ */
+ s.initLoad = function (loadItem) {
+ return s._registerSound(loadItem);
+ };
+
+ /**
+ * Internal method for loading sounds. This should not be called directly.
+ *
+ * @method _registerSound
+ * @param {Object} src The object to load, containing src property and optionally containing id and data.
+ * @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.6.0
+ */
+
+ s._registerSound = function (loadItem) {
+ if (!s.initializeDefaultPlugins()) {return false;}
+
+ var details;
+ if (loadItem.src instanceof Object) {
+ details = s._parseSrc(loadItem.src);
+ details.src = loadItem.path + details.src;
+ } else {
+ details = s._parsePath(loadItem.src);
+ }
+ if (details == null) {return false;}
+ loadItem.src = details.src;
+ loadItem.type = "sound";
+
+ var data = loadItem.data;
+ var numChannels = 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: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
+
+ if (sp.defaultPlayProps) {
+ s._defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
+ }
+ }
+ }
+ }
+ if (loadItem.id != null) {s._idHash[loadItem.id] = {src: loadItem.src}};
+ var loader = s.activePlugin.register(loadItem);
+
+ SoundChannel.create(loadItem.src, numChannels);
+
+ // return the number of instances to the user. This will also be returned in the load event.
+ if (data == null || !isNaN(data)) {
+ loadItem.data = numChannels || SoundChannel.maxPerChannel();
+ } else {
+ loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
+ }
+
+ if (loader.type) {loadItem.type = loader.type;}
+
+ if (loadItem.defaultPlayProps) {
+ s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
+ }
+ return loader;
+ };
+
+ /**
+ * Register an audio file for loading and future playback in Sound. This is automatically called when using
+ * PreloadJS. It is recommended to register all sounds that
+ * need to be played back in order to properly prepare and preload them. Sound does internal preloading when required.
+ *
+ *
Example
+ *
+ * createjs.Sound.alternateExtensions = ["mp3"];
+ * createjs.Sound.on("fileload", handleLoad); // add an event listener for when load is completed
+ * createjs.Sound.registerSound("myAudioPath/mySound.ogg", "myID", 3);
+ * createjs.Sound.registerSound({ogg:"path1/mySound.ogg", mp3:"path2/mySoundNoExtension"}, "myID", 3);
+ *
+ *
+ * @method registerSound
+ * @param {String | Object} src The source or an Object with a "src" property or an Object with multiple extension labeled src properties.
+ * @param {String} [id] An id specified by the user to play the sound later. Note id is required for when src is multiple extension labeled src properties.
+ * @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 {{#crossLink "AudioSprite"}}{{/crossLink}} array of objects in the following format {id, startTime, duration}.
+ * id used to play the sound later, in the same manner as a sound src with an id.
+ * startTime is the initial offset to start playback and loop from, in milliseconds.
+ * duration is the amount of time to play the clip for, in milliseconds.
+ * 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.
+ * @param {Object | PlayPropsConfig} defaultPlayProps Optional Playback properties that will be set as the defaults on any new AbstractSoundInstance.
+ * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for options.
+ * @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
+ * @since 0.4.0
+ */
+ s.registerSound = function (src, id, data, basePath, defaultPlayProps) {
+ var loadItem = {src: src, id: id, data:data, defaultPlayProps:defaultPlayProps};
+ if (src instanceof Object && src.src) {
+ basePath = id;
+ loadItem = src;
+ }
+ loadItem = createjs.LoadItem.create(loadItem);
+ loadItem.path = basePath;
+
+ if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + src;}
+
+ var loader = s._registerSound(loadItem);
+ if(!loader) {return false;}
+
+ if (!s._preloadHash[loadItem.src]) { s._preloadHash[loadItem.src] = [];}
+ s._preloadHash[loadItem.src].push(loadItem);
+ if (s._preloadHash[loadItem.src].length == 1) {
+ // OJR note this will disallow reloading a sound if loading fails or the source changes
+ loader.on("complete", createjs.proxy(this._handleLoadComplete, this));
+ loader.on("error", createjs.proxy(this._handleLoadError, this));
+ s.activePlugin.preload(loader);
+ } else {
+ if (s._preloadHash[loadItem.src][0] == true) {return true;}
+ }
+
+ return loadItem;
+ };
+
+ /**
+ * Register an array of audio files for loading and future playback in Sound. It is recommended to register all
+ * sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading
+ * when required.
+ *
+ *
Example
+ *
+ * var assetPath = "./myAudioPath/";
+ * var sounds = [
+ * {src:"asset0.ogg", id:"example"},
+ * {src:"asset1.ogg", id:"1", data:6},
+ * {src:"asset2.mp3", id:"works"}
+ * {src:{mp3:"path1/asset3.mp3", ogg:"path2/asset3NoExtension}, id:"better"}
+ * ];
+ * createjs.Sound.alternateExtensions = ["mp3"]; // if the passed extension is not supported, try this extension
+ * createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
+ * createjs.Sound.registerSounds(sounds, assetPath);
+ *
+ * @method registerSounds
+ * @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for
+ * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: {src:srcURI, id:ID, data:Data}
+ * with "id" and "data" being optional.
+ * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to load.
+ * Note id is required if src is an object with extension labeled src properties.
+ * @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing
+ * audio that was loaded with a basePath by src, the basePath must be included.
+ * @return {Object} An array of objects with the modified values that were passed in, which defines each sound.
+ * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized.
+ * Also, it will return true for any values when the source is already loaded.
+ * @static
+ * @since 0.6.0
+ */
+ s.registerSounds = function (sounds, basePath) {
+ var returnValues = [];
+ if (sounds.path) {
+ if (!basePath) {
+ basePath = sounds.path;
+ } else {
+ basePath = basePath + sounds.path;
+ }
+ sounds = sounds.manifest;
+ // TODO document this feature
+ }
+ for (var i = 0, l = sounds.length; i < l; i++) {
+ returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
+ }
+ return returnValues;
+ };
+
+ /**
+ * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
+ * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ * Note this will stop playback on active instances playing this sound before deleting them.
+ * Note if you passed in a basePath, you need to pass it or prepend it to the src here.
+ *
+ *
Example
+ *
+ * createjs.Sound.removeSound("myID");
+ * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
+ * createjs.Sound.removeSound("myPath/myOtherSound.mp3", "myBasePath/");
+ * createjs.Sound.removeSound({mp3:"musicNoExtension", ogg:"music.ogg"}, "myBasePath/");
+ *
+ * @method removeSound
+ * @param {String | Object} src The src or ID of the audio, or an Object with a "src" property, or an Object with multiple extension labeled src properties.
+ * @param {string} basePath Set a path that will be prepended to each src when removing.
+ * @return {Boolean} True if sound is successfully removed.
+ * @static
+ * @since 0.4.1
+ */
+ s.removeSound = function(src, basePath) {
+ if (s.activePlugin == null) {return false;}
+
+ if (src instanceof Object && src.src) {src = src.src;}
+
+ var details;
+ if (src instanceof Object) {
+ details = s._parseSrc(src);
+ } else {
+ src = s._getSrcById(src).src;
+ details = s._parsePath(src);
+ }
+ if (details == null) {return false;}
+ src = details.src;
+ if (basePath != null) {src = basePath + src;}
+
+ for(var prop in s._idHash){
+ if(s._idHash[prop].src == src) {
+ delete(s._idHash[prop]);
+ }
+ }
+
+ // clear from SoundChannel, which also stops and deletes all instances
+ SoundChannel.removeSrc(src);
+
+ delete(s._preloadHash[src]);
+
+ s.activePlugin.removeSound(src);
+
+ return true;
+ };
+
+ /**
+ * Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
+ * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ * Note this will stop playback on active instances playing this audio before deleting them.
+ * Note if you passed in a basePath, you need to pass it or prepend it to the src here.
+ *
+ *
Example
+ *
+ * assetPath = "./myPath/";
+ * var sounds = [
+ * {src:"asset0.ogg", id:"example"},
+ * {src:"asset1.ogg", id:"1", data:6},
+ * {src:"asset2.mp3", id:"works"}
+ * ];
+ * createjs.Sound.removeSounds(sounds, assetPath);
+ *
+ * @method removeSounds
+ * @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for
+ * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: {srcOrID:srcURIorID}.
+ * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to remove.
+ * @param {string} basePath Set a path that will be prepended to each src when removing.
+ * @return {Object} An array of Boolean values representing if the sounds with the same array index were
+ * successfully removed.
+ * @static
+ * @since 0.4.1
+ */
+ s.removeSounds = function (sounds, basePath) {
+ var returnValues = [];
+ if (sounds.path) {
+ if (!basePath) {
+ basePath = sounds.path;
+ } else {
+ basePath = basePath + sounds.path;
+ }
+ sounds = sounds.manifest;
+ }
+ for (var i = 0, l = sounds.length; i < l; i++) {
+ returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath);
+ }
+ return returnValues;
+ };
+
+ /**
+ * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
+ * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
+ * Note this will stop playback on all active sound instances before deleting them.
+ *
+ *
Example
+ *
+ * createjs.Sound.removeAllSounds();
+ *
+ * @method removeAllSounds
+ * @static
+ * @since 0.4.1
+ */
+ s.removeAllSounds = function() {
+ s._idHash = {};
+ s._preloadHash = {};
+ SoundChannel.removeAll();
+ if (s.activePlugin) {s.activePlugin.removeAllSounds();}
+ };
+
+ /**
+ * Check if a source has been loaded by internal preloaders. This is necessary to ensure that sounds that are
+ * not completed preloading will not kick off a new internal preload if they are played.
+ *
+ *
Example
+ *
+ * var mySound = "assetPath/asset0.ogg";
+ * if(createjs.Sound.loadComplete(mySound) {
+ * createjs.Sound.play(mySound);
+ * }
+ *
+ * @method loadComplete
+ * @param {String} src The src or id that is being loaded.
+ * @return {Boolean} If the src is already loaded.
+ * @since 0.4.0
+ * @static
+ */
+ s.loadComplete = function (src) {
+ if (!s.isReady()) { return false; }
+ var details = s._parsePath(src);
+ if (details) {
+ src = s._getSrcById(details.src).src;
+ } else {
+ src = s._getSrcById(src).src;
+ }
+ if(s._preloadHash[src] == undefined) {return false;}
+ 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. 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.
+ * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
+ * and returned to a preloader like PreloadJS.
+ * @protected
+ * @static
+ */
+ s._parsePath = function (value) {
+ if (typeof(value) != "string") {value = value.toString();}
+
+ var match = value.match(s.FILE_PATTERN);
+ if (match == null) {return false;}
+
+ var name = match[4];
+ var ext = match[5];
+ var c = s.capabilities;
+ 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 = {name:name, src:value, extension:ext};
+ return ret;
+ };
+
+ /**
+ * Parse the path of a sound based on properties of src matching with supported extensions.
+ * Returns false if none of the properties are supported
+ * @method _parseSrc
+ * @param {Object} value The paths to an audio source, indexed by extension type.
+ * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
+ * and returned to a preloader like PreloadJS.
+ * @protected
+ * @static
+ */
+ s._parseSrc = function (value) {
+ var ret = {name:undefined, src:undefined, extension:undefined};
+ var c = s.capabilities;
+
+ for (var prop in value) {
+ if(value.hasOwnProperty(prop) && c[prop]) {
+ ret.src = value[prop];
+ ret.extension = prop;
+ break;
+ }
+ }
+ if (!ret.src) {return false;} // no matches
+
+ var i = ret.src.lastIndexOf("/");
+ if (i != -1) {
+ ret.name = ret.src.slice(i+1);
+ } else {
+ ret.name = ret.src;
+ }
+
+ return ret;
+ };
+
+ /* ---------------
+ Static API.
+ --------------- */
+ /**
+ * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a
+ * AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}.
+ * Note that even on sounds with failed playback, you may still be able to call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
+ * since the failure could be due to lack of available channels. If the src does not have a supported extension or
+ * if there is no available plugin, a default AbstractSoundInstance will be returned which will not play any audio, but will not generate errors.
+ *
+ *
Example
+ *
+ * createjs.Sound.on("fileload", handleLoad);
+ * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
+ * function handleLoad(event) {
+ * createjs.Sound.play("myID");
+ * // store off AbstractSoundInstance for controlling
+ * var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
+ * }
+ *
+ * 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.
+ *
+ * Parameters Deprecated
+ * The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
+ *
+ * @method play
+ * @param {String} src The src or ID of the audio.
+ * @param {String | Object} [interrupt="none"|options] This parameter will be renamed playProps in the next release.
+ * This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
+ * including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
+ * OR
+ * Deprecated How to interrupt any currently playing instances of audio with the same source,
+ * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
+ * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
+ * @param {Number} [delay=0] Deprecated The amount of time to delay the start of audio playback, in milliseconds.
+ * @param {Number} [offset=0] Deprecated The offset from the start of the audio to begin playback, in milliseconds.
+ * @param {Number} [loop=0] Deprecated How many times the audio loops when it reaches the end of playback. The default is 0 (no
+ * loops), and -1 can be used for infinite playback.
+ * @param {Number} [volume=1] Deprecated The volume of the sound, between 0 and 1. Note that the master volume is applied
+ * against the individual volume.
+ * @param {Number} [pan=0] Deprecated The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
+ * @param {Number} [startTime=null] Deprecated To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
+ * @param {Number} [duration=null] Deprecated To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
+ * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
+ * @static
+ */
+ s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) {
+ var playProps;
+ if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
+ playProps = createjs.PlayPropsConfig.create(interrupt);
+ } else {
+ playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan, startTime:startTime, duration:duration});
+ }
+ var instance = s.createInstance(src, playProps.startTime, playProps.duration);
+ var ok = s._playInstance(instance, playProps);
+ if (!ok) {instance._playFailed();}
+ return instance;
+ };
+
+ /**
+ * Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a
+ * supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be
+ * called safely but does nothing.
+ *
+ *
Example
+ *
+ * var myInstance = null;
+ * createjs.Sound.on("fileload", handleLoad);
+ * createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
+ * function handleLoad(event) {
+ * myInstance = createjs.Sound.createInstance("myID");
+ * // alternately we could call the following
+ * 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 {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
+ * Unsupported extensions will return the default AbstractSoundInstance.
+ * @since 0.4.0
+ * @static
+ */
+ s.createInstance = function (src, startTime, duration) {
+ if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}
+
+ var defaultPlayProps = s._defaultPlayPropsHash[src]; // for audio sprites, which create and store defaults by id
+ src = s._getSrcById(src);
+
+ var details = s._parsePath(src.src);
+
+ var instance = null;
+ if (details != null && details.src != null) {
+ SoundChannel.create(details.src);
+ if (startTime == null) {startTime = src.startTime;}
+ instance = s.activePlugin.create(details.src, startTime, duration || src.duration);
+
+ defaultPlayProps = defaultPlayProps || s._defaultPlayPropsHash[details.src];
+ if(defaultPlayProps) {
+ instance.applyPlayProps(defaultPlayProps);
+ }
+ } else {
+ instance = new createjs.DefaultSoundInstance(src, startTime, duration);
+ }
+
+ instance.uniqueId = s._lastID++;
+
+ return instance;
+ };
+
+ /**
+ * Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped,
+ * call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
+ *
+ *
Example
+ *
+ * createjs.Sound.stop();
+ *
+ * @method stop
+ * @static
+ */
+ s.stop = function () {
+ var instances = this._instances;
+ for (var i = instances.length; i--; ) {
+ instances[i].stop(); // NOTE stop removes instance from this._instances
+ }
+ };
+
+ /**
+ * Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
+ *
+ * @method setVolume
+ * @param {Number} value The master volume value. The acceptable range is 0-1.
+ * @static
+ * @deprecated
+ */
+ s.setVolume = function (value) {
+ 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;
+ for (var i = 0, l = instances.length; i < l; i++) {
+ instances[i].setMasterVolume(value);
+ }
+ }
+ };
+
+ /**
+ * Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
+ *
+ * @method getVolume
+ * @return {Number} The master volume, in a range of 0-1.
+ * @static
+ * @deprecated
+ */
+ s.getVolume = function () {
+ return this._masterVolume;
+ };
+
+ /**
+ * Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
+ *
+ * @method setMute
+ * @param {Boolean} value Whether the audio should be muted or not.
+ * @return {Boolean} If the mute was set.
+ * @static
+ * @since 0.4.0
+ * @deprecated
+ */
+ s.setMute = function (value) {
+ if (value == null) {return false;}
+
+ this._masterMute = value;
+ if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
+ var instances = this._instances;
+ for (var i = 0, l = instances.length; i < l; i++) {
+ instances[i].setMasterMute(value);
+ }
+ }
+ return true;
+ };
+
+ /**
+ * Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
+ *
+ * @method getMute
+ * @return {Boolean} The mute value of Sound.
+ * @static
+ * @since 0.4.0
+ * @deprecated
+ */
+ s.getMute = function () {
+ return this._masterMute;
+ };
+
+ /**
+ * Set the default playback properties for all new SoundInstances of the passed in src or ID.
+ * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
+ *
+ * @method setDefaultPlayProps
+ * @param {String} src The src or ID used to register the audio.
+ * @param {Object | PlayPropsConfig} playProps The playback properties you would like to set.
+ * @since 0.6.1
+ */
+ s.setDefaultPlayProps = function(src, playProps) {
+ src = s._getSrcById(src);
+ s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
+ };
+
+ /**
+ * Get the default playback properties for the passed in src or ID. These properties are applied to all
+ * new SoundInstances. Returns null if default does not exist.
+ *
+ * @method getDefaultPlayProps
+ * @param {String} src The src or ID used to register the audio.
+ * @returns {PlayPropsConfig} returns an existing PlayPropsConfig or null if one does not exist
+ * @since 0.6.1
+ */
+ s.getDefaultPlayProps = function(src) {
+ src = s._getSrcById(src);
+ return s._defaultPlayPropsHash[s._parsePath(src.src).src];
+ };
+
+
+ /* ---------------
+ Internal methods
+ --------------- */
+ /**
+ * Play an instance. This is called by the static API, as well as from plugins. This allows the core class to
+ * control delays.
+ * @method _playInstance
+ * @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
+ * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
+ * @return {Boolean} If the sound can start playing. Sounds that fail immediately will return false. Sounds that
+ * have a delay will return true, but may still fail to play.
+ * @protected
+ * @static
+ */
+ s._playInstance = function (instance, playProps) {
+ var defaultPlayProps = s._defaultPlayPropsHash[instance.src] || {};
+ if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || s.defaultInterruptBehavior};
+ if (playProps.delay == null) {playProps.delay = defaultPlayProps.delay || 0;}
+ if (playProps.offset == null) {playProps.offset = instance.getPosition();}
+ if (playProps.loop == null) {playProps.loop = instance.loop;}
+ if (playProps.volume == null) {playProps.volume = instance.volume;}
+ if (playProps.pan == null) {playProps.pan = instance.pan;}
+
+ if (playProps.delay == 0) {
+ var ok = s._beginPlaying(instance, playProps);
+ 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
+ var delayTimeoutId = setTimeout(function () {
+ s._beginPlaying(instance, playProps);
+ }, playProps.delay);
+ instance.delayTimeoutId = delayTimeoutId;
+ }
+
+ this._instances.push(instance);
+
+ return true;
+ };
+
+ /**
+ * Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}.
+ * @method _beginPlaying
+ * @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback.
+ * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
+ * @return {Boolean} If the sound can start playing. If there are no available channels, or the instance fails to
+ * start, this will return false.
+ * @protected
+ * @static
+ */
+ s._beginPlaying = function (instance, playProps) {
+ if (!SoundChannel.add(instance, playProps.interrupt)) {
+ return false;
+ }
+ var result = instance._beginPlaying(playProps);
+ if (!result) {
+ var index = createjs.indexOf(this._instances, instance);
+ if (index > -1) {this._instances.splice(index, 1);}
+ return false;
+ }
+ return true;
+ };
+
+ /**
+ * Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned
+ * instead.
+ * @method _getSrcById
+ * @param {String} value The ID the sound was registered with.
+ * @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) {
+ return s._idHash[value] || {src: value};
+ };
+
+ /**
+ * A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from
+ * Sound management. It will be added again, if the sound re-plays. Note that this method is called from the
+ * instances themselves.
+ * @method _playFinished
+ * @param {AbstractSoundInstance} instance The instance that finished playback.
+ * @protected
+ * @static
+ */
+ s._playFinished = function (instance) {
+ SoundChannel.remove(instance);
+ var index = createjs.indexOf(this._instances, instance);
+ 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;
+
+ /**
+ * An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for
+ * each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class.
+ *
+ * The number of sounds is artificially limited by Sound in order to prevent over-saturation of a
+ * single sound, as well as to stay within hardware limitations, although the latter may disappear with better
+ * browser support.
+ *
+ * When a sound is played, this class ensures that there is an available instance, or interrupts an appropriate
+ * sound that is already playing.
+ * #class SoundChannel
+ * @param {String} src The source of the instances
+ * @param {Number} [max=1] The number of instances allowed
+ * @constructor
+ * @protected
+ */
+ function SoundChannel(src, max) {
+ this.init(src, max);
+ }
+
+ /* ------------
+ Static API
+ ------------ */
+ /**
+ * A hash of channel instances indexed by source.
+ * #property channels
+ * @type {Object}
+ * @static
+ */
+ SoundChannel.channels = {};
+
+ /**
+ * Create a sound channel. Note that if the sound channel already exists, this will fail.
+ * #method create
+ * @param {String} src The source for the channel
+ * @param {Number} max The maximum amount this channel holds. The default is {{#crossLink "SoundChannel.maxDefault"}}{{/crossLink}}.
+ * @return {Boolean} If the channels were created.
+ * @static
+ */
+ SoundChannel.create = function (src, max) {
+ var channel = SoundChannel.get(src);
+ if (channel == null) {
+ SoundChannel.channels[src] = new SoundChannel(src, max);
+ return true;
+ }
+ return false;
+ };
+ /**
+ * Delete a sound channel, stop and delete all related instances. Note that if the sound channel does not exist, this will fail.
+ * #method remove
+ * @param {String} src The source for the channel
+ * @return {Boolean} If the channels were deleted.
+ * @static
+ */
+ SoundChannel.removeSrc = function (src) {
+ var channel = SoundChannel.get(src);
+ if (channel == null) {return false;}
+ channel._removeAll(); // this stops and removes all active instances
+ delete(SoundChannel.channels[src]);
+ return true;
+ };
+ /**
+ * Delete all sound channels, stop and delete all related instances.
+ * #method removeAll
+ * @static
+ */
+ SoundChannel.removeAll = function () {
+ for(var channel in SoundChannel.channels) {
+ SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances
+ }
+ SoundChannel.channels = {};
+ };
+ /**
+ * Add an instance to a sound channel.
+ * #method add
+ * @param {AbstractSoundInstance} instance The instance to add to the channel
+ * @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}}
+ * for details on interrupt modes.
+ * @return {Boolean} The success of the method call. If the channel is full, it will return false.
+ * @static
+ */
+ SoundChannel.add = function (instance, interrupt) {
+ var channel = SoundChannel.get(instance.src);
+ if (channel == null) {return false;}
+ return channel._add(instance, interrupt);
+ };
+ /**
+ * Remove an instance from the channel.
+ * #method remove
+ * @param {AbstractSoundInstance} instance The instance to remove from the channel
+ * @return The success of the method call. If there is no channel, it will return false.
+ * @static
+ */
+ SoundChannel.remove = function (instance) {
+ var channel = SoundChannel.get(instance.src);
+ if (channel == null) {return false;}
+ channel._remove(instance);
+ return true;
+ };
+ /**
+ * Get the maximum number of sounds you can have in a channel.
+ * #method maxPerChannel
+ * @return {Number} The maximum number of sounds you can have in a channel.
+ */
+ SoundChannel.maxPerChannel = function () {
+ return p.maxDefault;
+ };
+ /**
+ * Get a channel instance by its src.
+ * #method get
+ * @param {String} src The src to use to look up the channel
+ * @static
+ */
+ SoundChannel.get = function (src) {
+ return SoundChannel.channels[src];
+ };
+
+ var p = SoundChannel.prototype;
+ p.constructor = SoundChannel;
+
+ /**
+ * REMOVED. Removed in favor of using `MySuperClass_constructor`.
+ * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
+ * for details.
+ *
+ * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
+ *
+ * @method initialize
+ * @protected
+ * @deprecated
+ */
+ // p.initialize = function() {}; // searchable for devs wondering where it is.
+
+
+ /**
+ * The source of the channel.
+ * #property src
+ * @type {String}
+ */
+ p.src = null;
+
+ /**
+ * The maximum number of instances in this channel. -1 indicates no limit
+ * #property max
+ * @type {Number}
+ */
+ p.max = null;
+
+ /**
+ * The default value to set for max, if it isn't passed in. Also used if -1 is passed.
+ * #property maxDefault
+ * @type {Number}
+ * @default 100
+ * @since 0.4.0
+ */
+ p.maxDefault = 100;
+
+ /**
+ * The current number of active instances.
+ * #property length
+ * @type {Number}
+ */
+ p.length = 0;
+
+ /**
+ * Initialize the channel.
+ * #method init
+ * @param {String} src The source of the channel
+ * @param {Number} max The maximum number of instances in the channel
+ * @protected
+ */
+ p.init = function (src, max) {
+ this.src = src;
+ this.max = max || this.maxDefault;
+ if (this.max == -1) {this.max = this.maxDefault;}
+ this._instances = [];
+ };
+
+ /**
+ * Get an instance by index.
+ * #method get
+ * @param {Number} index The index to return.
+ * @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
+ */
+ p._get = function (index) {
+ return this._instances[index];
+ };
+
+ /**
+ * Add a new instance to the channel.
+ * #method add
+ * @param {AbstractSoundInstance} 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;}
+ this._instances.push(instance);
+ this.length++;
+ return true;
+ };
+
+ /**
+ * Remove an instance from the channel, either when it has finished playing, or it has been interrupted.
+ * #method remove
+ * @param {AbstractSoundInstance} instance The instance to remove
+ * @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) {
+ var index = createjs.indexOf(this._instances, instance);
+ if (index == -1) {return false;}
+ this._instances.splice(index, 1);
+ this.length--;
+ return true;
+ };
+
+ /**
+ * 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
+ for (var i=this.length-1; i>=0; i--) {
+ this._instances[i].stop();
+ }
+ };
+
+ /**
+ * Get an available slot depending on interrupt value and if slots are available.
+ * #method getSlot
+ * @param {String} interrupt The interrupt value to use.
+ * @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful.
+ * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
+ * an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false.
+ */
+ p._getSlot = function (interrupt, instance) {
+ var target, replacement;
+
+ if (interrupt != Sound.INTERRUPT_NONE) {
+ // First replacement candidate
+ replacement = this._get(0);
+ if (replacement == null) {
+ return true;
+ }
+ }
+
+ for (var i = 0, l = this.max; i < l; i++) {
+ target = this._get(i);
+
+ // Available Space
+ if (target == null) {
+ return true;
+ }
+
+ // Audio is complete or not playing
+ if (target.playState == Sound.PLAY_FINISHED ||
+ target.playState == Sound.PLAY_INTERRUPTED ||
+ target.playState == Sound.PLAY_FAILED) {
+ replacement = target;
+ break;
+ }
+
+ if (interrupt == Sound.INTERRUPT_NONE) {
+ continue;
+ }
+
+ // Audio is a better candidate than the current target, according to playhead
+ 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);
+ return true;
+ }
+ return false;
+ };
+
+ p.toString = function () {
+ return "[Sound SoundChannel]";
+ };
+ // do not add SoundChannel to namespace
+
}());
//##############################################################################
-// AbstractPlugin.js
+// AbstractSoundInstance.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
-
-// constructor:
- /**
- * A default plugin class used as a base for all other plugins.
- * @class AbstractPlugin
- * @constructor
- * @since 0.6.0
- */
-
- var AbstractPlugin = function () {
- // private properties:
- /**
- * The capabilities of the plugin.
- * method and is used internally.
- * @property _capabilities
- * @type {Object}
- * @default null
- * @protected
- * @static
- */
- this._capabilities = null;
-
- /**
- * Object hash indexed by the source URI of all created loaders, used to properly destroy them if sources are removed.
- * @type {Object}
- * @protected
- */
- this._loaders = {};
-
- /**
- * Object hash indexed by the source URI of each file to indicate if an audio source has begun loading,
- * is currently loading, or has completed loading. Can be used to store non boolean data after loading
- * is complete (for example arrayBuffers for web audio).
- * @property _audioSources
- * @type {Object}
- * @protected
- */
- this._audioSources = {};
-
- /**
- * Object hash indexed by the source URI of all created SoundInstances, updates the playbackResource if it loads after they are created,
- * and properly destroy them if sources are removed
- * @type {Object}
- * @protected
- */
- this._soundInstances = {};
-
- /**
- * The internal master volume value of the plugin.
- * @property _volume
- * @type {Number}
- * @default 1
- * @protected
- */
- this._volume = 1;
-
- /**
- * A reference to a loader class used by a plugin that must be set.
- * @type {Object}
- * @protected
- */
- this._loaderClass;
-
- /**
- * A reference to an AbstractSoundInstance class used by a plugin that must be set.
- * @type {Object}
- * @protected;
- */
- this._soundInstanceClass;
- };
- var p = AbstractPlugin.prototype;
-
- /**
- * REMOVED. Removed in favor of using `MySuperClass_constructor`.
- * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
- * for details.
- *
- * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
- *
- * @method initialize
- * @protected
- * @deprecated
- */
- // p.initialize = function() {}; // searchable for devs wondering where it is.
-
-
-// Static Properties:
-// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
- /**
- * The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
- * @property _capabilities
- * @type {Object}
- * @default null
- * @protected
- * @static
- */
- AbstractPlugin._capabilities = null;
-
- /**
- * Determine if the plugin can be used in the current browser/OS.
- * @method isSupported
- * @return {Boolean} If the plugin can be initialized.
- * @static
- */
- AbstractPlugin.isSupported = function () {
- return true;
- };
-
-
-// public methods:
- /**
- * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}.
- * Note all plugins provide a Loader instance, which PreloadJS
- * can use to assist with preloading.
- * @method register
- * @param {String} loadItem An Object containing the source of the audio
- * Note that not every plugin will manage this value.
- * @return {Object} A result object, containing a "tag" for preloading purposes.
- */
- p.register = function (loadItem) {
- var loader = this._loaders[loadItem.src];
- if(loader && !loader.canceled) {return this._loaders[loadItem.src];} // already loading/loaded this, so don't load twice
- // OJR potential issue that we won't be firing loaded event, might need to trigger if this is already loaded?
- this._audioSources[loadItem.src] = true;
- this._soundInstances[loadItem.src] = [];
- loader = new this._loaderClass(loadItem);
- loader.on("complete", createjs.proxy(this._handlePreloadComplete, this));
- this._loaders[loadItem.src] = loader;
- return loader;
- };
-
- // note sound calls register before calling preload
- /**
- * Internally preload a sound.
- * @method preload
- * @param {Loader} loader The sound URI to load.
- */
- p.preload = function (loader) {
- loader.on("error", createjs.proxy(this._handlePreloadError, this));
- loader.load();
- };
-
- /**
- * Checks if preloading has started for a specific source. If the source is found, we can assume it is loading,
- * or has already finished loading.
- * @method isPreloadStarted
- * @param {String} src The sound URI to check.
- * @return {Boolean}
- */
- p.isPreloadStarted = function (src) {
- return (this._audioSources[src] != null);
- };
-
- /**
- * Checks if preloading has finished for a specific source.
- * @method isPreloadComplete
- * @param {String} src The sound URI to load.
- * @return {Boolean}
- */
- p.isPreloadComplete = function (src) {
- return (!(this._audioSources[src] == null || this._audioSources[src] == true));
- };
-
- /**
- * Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
- * @method removeSound
- * @param {String} src The sound URI to unload.
- */
- p.removeSound = function (src) {
- if (!this._soundInstances[src]) { return; }
- for (var i = this._soundInstances[src].length; i--; ) {
- var item = this._soundInstances[src][i];
- item.destroy();
- }
- delete(this._soundInstances[src]);
- delete(this._audioSources[src]);
- if(this._loaders[src]) { this._loaders[src].destroy(); }
- delete(this._loaders[src]);
- };
-
- /**
- * Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
- * @method removeAllSounds
- * @param {String} src The sound URI to unload.
- */
- p.removeAllSounds = function () {
- for(var key in this._audioSources) {
- this.removeSound(key);
- }
- };
-
- /**
- * 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 {AbstractSoundInstance} A sound instance for playback and control.
- */
- p.create = function (src, startTime, duration) {
- if (!this.isPreloadStarted(src)) {
- this.preload(this.register(src));
- }
- var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
- this._soundInstances[src].push(si);
- return si;
- };
-
- // if a plugin does not support volume and mute, it should set these to null
- /**
- * Set the master volume of the plugin, which affects all SoundInstances.
- * @method setVolume
- * @param {Number} value The volume to set, between 0 and 1.
- * @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the
- * instances manually otherwise.
- */
- p.setVolume = function (value) {
- this._volume = value;
- this._updateVolume();
- return true;
- };
-
- /**
- * Get the master volume of the plugin, which affects all SoundInstances.
- * @method getVolume
- * @return {Number} The volume level, between 0 and 1.
- */
- p.getVolume = function () {
- return this._volume;
- };
-
- /**
- * Mute all sounds via the plugin.
- * @method setMute
- * @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up
- * the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here.
- * @return {Boolean} If the mute call succeeds.
- */
- p.setMute = function (value) {
- this._updateVolume();
- return true;
- };
-
- // plugins should overwrite this method
- p.toString = function () {
- return "[AbstractPlugin]";
- };
-
-
-// private methods:
- /**
- * Handles internal preload completion.
- * @method _handlePreloadComplete
- * @protected
- */
- p._handlePreloadComplete = function (event) {
- var src = event.target.getItem().src;
- this._audioSources[src] = event.result;
- for (var i = 0, l = this._soundInstances[src].length; i < l; i++) {
- var item = this._soundInstances[src][i];
- item.setPlaybackResource(this._audioSources[src]);
- // ToDo consider adding play call here if playstate == playfailed
- }
- };
-
- /**
- * Handles internal preload erros
- * @method _handlePreloadError
- * @param event
- * @protected
- */
- p._handlePreloadError = function(event) {
- //delete(this._audioSources[src]);
- };
-
- /**
- * Set the gain value for master audio. Should not be called externally.
- * @method _updateVolume
- * @protected
- */
- p._updateVolume = function () {
- // Plugin Specific code
- };
-
- createjs.AbstractPlugin = AbstractPlugin;
+this.createjs = this.createjs || {};
+
+/**
+ * A AbstractSoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or
+ * {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The AbstractSoundInstance is returned by the active plugin
+ * for control by the user.
+ *
+ *
Example
+ *
+ * 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
+ * API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments.
+ *
+ * Once a AbstractSoundInstance is created, a reference can be stored that can be used to control the audio directly through
+ * the AbstractSoundInstance. If the reference is not stored, the AbstractSoundInstance will play out its audio (and any loops), and
+ * is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio
+ * playback has completed, a simple call to the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} instance method
+ * will rebuild the references the Sound class need to control it.
+ *
+ * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2});
+ * myInstance.on("loop", handleLoop);
+ * function handleLoop(event) {
+ * myInstance.volume = myInstance.volume * 0.5;
+ * }
+ *
+ * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails
+ *
+ * var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
+ * myInstance.on("complete", handleComplete);
+ * myInstance.on("loop", handleLoop);
+ * myInstance.on("failed", handleFailed);
+ *
+ *
+ * @class AbstractSoundInstance
+ * @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} playbackResource Any resource needed by plugin to support audio playback.
+ * @extends EventDispatcher
+ * @constructor
+ */
+
+(function () {
+ "use strict";
+
+
+// Constructor:
+ var AbstractSoundInstance = function (src, startTime, duration, playbackResource) {
+ this.EventDispatcher_constructor();
+
+
+ // public properties:
+ /**
+ * The source of the sound.
+ * @property src
+ * @type {String}
+ * @default null
+ */
+ this.src = src;
+
+ /**
+ * The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}.
+ * @property uniqueId
+ * @type {String} | Number
+ * @default -1
+ */
+ this.uniqueId = -1;
+
+ /**
+ * The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}.
+ * @property playState
+ * @type {String}
+ * @default null
+ */
+ this.playState = null;
+
+ /**
+ * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this AbstractSoundInstance is played with a delay.
+ * This allows AbstractSoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins.
+ * @property delayTimeoutId
+ * @type {timeoutVariable}
+ * @default null
+ * @protected
+ * @since 0.4.0
+ */
+ this.delayTimeoutId = null;
+ // TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins
+
+
+ // private properties
+ // Getter / Setter Properties
+ // OJR TODO find original reason that we didn't use defined functions. I think it was performance related
+ /**
+ * The volume of the sound, between 0 and 1.
+ *
+ * The actual output volume of a sound can be calculated using:
+ * myInstance.volume * createjs.Sound.getVolume();
+ *
+ * @property volume
+ * @type {Number}
+ * @default 1
+ */
+ this._volume = 1;
+ Object.defineProperty(this, "volume", {
+ get: this.getVolume,
+ set: this.setVolume
+ });
+
+ /**
+ * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
+ *
+ * Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio.
+ *
+ * @property pan
+ * @type {Number}
+ * @default 0
+ */
+ this._pan = 0;
+ Object.defineProperty(this, "pan", {
+ get: this.getPan,
+ set: this.setPan
+ });
+
+ /**
+ * Audio sprite property used to determine the starting offset.
+ * @property startTime
+ * @type {Number}
+ * @default 0
+ * @since 0.6.1
+ */
+ this._startTime = Math.max(0, startTime || 0);
+ Object.defineProperty(this, "startTime", {
+ get: this.getStartTime,
+ set: this.setStartTime
+ });
+
+ /**
+ * The length of the audio clip, in milliseconds.
+ *
+ * @property duration
+ * @type {Number}
+ * @default 0
+ * @since 0.6.0
+ */
+ this._duration = Math.max(0, duration || 0);
+ Object.defineProperty(this, "duration", {
+ get: this.getDuration,
+ set: this.setDuration
+ });
+
+ /**
+ * Object that holds plugin specific resource need for audio playback.
+ * This is set internally by the plugin. For example, WebAudioPlugin will set an array buffer,
+ * HTMLAudioPlugin will set a tag, FlashAudioPlugin will set a flash reference.
+ *
+ * @property playbackResource
+ * @type {Object}
+ * @default null
+ */
+ this._playbackResource = null;
+ Object.defineProperty(this, "playbackResource", {
+ get: this.getPlaybackResource,
+ set: this.setPlaybackResource
+ });
+ if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
+
+ /**
+ * The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
+ *
+ * @property position
+ * @type {Number}
+ * @default 0
+ * @since 0.6.0
+ */
+ this._position = 0;
+ Object.defineProperty(this, "position", {
+ get: this.getPosition,
+ set: this.setPosition
+ });
+
+ /**
+ * The number of play loops remaining. Negative values will loop infinitely.
+ *
+ * @property loop
+ * @type {Number}
+ * @default 0
+ * @public
+ * @since 0.6.0
+ */
+ this._loop = 0;
+ Object.defineProperty(this, "loop", {
+ get: this.getLoop,
+ set: this.setLoop
+ });
+
+ /**
+ * Determines if the audio is currently muted.
+ *
+ * @property muted
+ * @type {Boolean}
+ * @default false
+ * @since 0.6.0
+ */
+ this._muted = false;
+ Object.defineProperty(this, "muted", {
+ get: this.getMuted,
+ set: this.setMuted
+ });
+
+ /**
+ * Tells you if the audio is currently paused.
+ *
+ * @property paused
+ * @type {Boolean}
+ */
+ this._paused = false;
+ Object.defineProperty(this, "paused", {
+ get: this.getPaused,
+ set: this.setPaused
+ });
+
+
+ // Events
+ /**
+ * The event that is fired when playback has started successfully.
+ * @event succeeded
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.4.0
+ */
+
+ /**
+ * The event that is fired when playback is interrupted. This happens when another sound with the same
+ * src property is played using an interrupt value that causes this instance to stop playing.
+ * @event interrupted
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.4.0
+ */
+
+ /**
+ * The event that is fired when playback has failed. This happens when there are too many channels with the same
+ * src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or
+ * the sound could not be played, perhaps due to a 404 error.
+ * @event failed
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.4.0
+ */
+
+ /**
+ * The event that is fired when a sound has completed playing but has loops remaining.
+ * @event loop
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.4.0
+ */
+
+ /**
+ * The event that is fired when playback completes. This means that the sound has finished playing in its
+ * entirety, including its loop iterations.
+ * @event complete
+ * @param {Object} target The object that dispatched the event.
+ * @param {String} type The event type.
+ * @since 0.4.0
+ */
+ };
+
+ var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// Public Methods:
+ /**
+ * Play an instance. This method is intended to be called on SoundInstances that already exist (created
+ * with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}).
+ *
+ *
Example
+ *
+ * var myInstance = createjs.Sound.createInstance(mySrc);
+ * myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
+ *
+ * Note that if this sound is already playing, this call will still set the passed in parameters.
+
+ * Parameters Deprecated
+ * The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
+ *
+ * @method play
+ * @param {String | Object} [interrupt="none"|options] This parameter will be renamed playProps in the next release.
+ * This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
+ * including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
+ * OR
+ * Deprecated How to interrupt any currently playing instances of audio with the same source,
+ * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
+ * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
+ * @param {Number} [delay=0] Deprecated The amount of time to delay the start of audio playback, in milliseconds.
+ * @param {Number} [offset=0] Deprecated The offset from the start of the audio to begin playback, in milliseconds.
+ * @param {Number} [loop=0] Deprecated How many times the audio loops when it reaches the end of playback. The default is 0 (no
+ * loops), and -1 can be used for infinite playback.
+ * @param {Number} [volume=1] Deprecated The volume of the sound, between 0 and 1. Note that the master volume is applied
+ * against the individual volume.
+ * @param {Number} [pan=0] Deprecated The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
+ * Note that pan is not supported for HTML Audio.
+ * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
+ */
+ p.play = function (interrupt, delay, offset, loop, volume, pan) {
+ var playProps;
+ if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
+ playProps = createjs.PlayPropsConfig.create(interrupt);
+ } else {
+ playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan});
+ }
+
+ if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this.applyPlayProps(playProps);
+ if (this._paused) { this.setPaused(false); }
+ return;
+ }
+ this._cleanUp();
+ createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
+ return this;
+ };
+
+ /**
+ * Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "AbstractSoundInstance/resume"}}{{/crossLink}}
+ * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
+ *
+ *
Example
+ *
+ * myInstance.stop();
+ *
+ * @method stop
+ * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
+ */
+ p.stop = function () {
+ this._position = 0;
+ this._paused = false;
+ this._handleStop();
+ this._cleanUp();
+ this.playState = createjs.Sound.PLAY_FINISHED;
+ return this;
+ };
+
+ /**
+ * Remove all external references and resources from AbstractSoundInstance. Note this is irreversible and AbstractSoundInstance will no longer work
+ * @method destroy
+ * @since 0.6.0
+ */
+ p.destroy = function() {
+ this._cleanUp();
+ this.src = null;
+ this.playbackResource = null;
+
+ this.removeAllEventListeners();
+ };
+
+ /**
+ * Takes an PlayPropsConfig or Object with the same properties and sets them on this instance.
+ * @method applyPlayProps
+ * @param {PlayPropsConfig | Object} playProps A PlayPropsConfig or object containing the same properties.
+ * @since 0.6.1
+ * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
+ */
+ p.applyPlayProps = function(playProps) {
+ if (playProps.offset != null) { this.setPosition(playProps.offset) }
+ if (playProps.loop != null) { this.setLoop(playProps.loop); }
+ if (playProps.volume != null) { this.setVolume(playProps.volume); }
+ if (playProps.pan != null) { this.setPan(playProps.pan); }
+ if (playProps.startTime != null) {
+ this.setStartTime(playProps.startTime);
+ this.setDuration(playProps.duration);
+ }
+ return this;
+ };
+
+ p.toString = function () {
+ return "[AbstractSoundInstance]";
+ };
+
+// get/set methods that allow support for IE8
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property,
+ *
+ * @deprecated
+ * @method getPaused
+ * @returns {boolean} If the instance is currently paused
+ * @since 0.6.0
+ */
+ p.getPaused = function() {
+ return this._paused;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setPaused
+ * @param {boolean} value
+ * @since 0.6.0
+ * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
+ */
+ p.setPaused = function (value) {
+ if ((value !== true && value !== false) || this._paused == value) {return;}
+ if (value == true && this.playState != createjs.Sound.PLAY_SUCCEEDED) {return;}
+ this._paused = value;
+ if(value) {
+ this._pause();
+ } else {
+ this._resume();
+ }
+ clearTimeout(this.delayTimeoutId);
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setVolume
+ * @param {Number} value The volume to set, between 0 and 1.
+ * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
+ */
+ p.setVolume = function (value) {
+ if (value == this._volume) { return this; }
+ this._volume = Math.max(0, Math.min(1, value));
+ if (!this._muted) {
+ this._updateVolume();
+ }
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getVolume
+ * @return {Number} The current volume of the sound instance.
+ */
+ p.getVolume = function () {
+ return this._volume;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setMuted
+ * @param {Boolean} value If the sound should be muted.
+ * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
+ * @since 0.6.0
+ */
+ p.setMuted = function (value) {
+ if (value !== true && value !== false) {return;}
+ this._muted = value;
+ this._updateVolume();
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getMuted
+ * @return {Boolean} If the sound is muted.
+ * @since 0.6.0
+ */
+ p.getMuted = function () {
+ return this._muted;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setPan
+ * @param {Number} value The pan value, between -1 (left) and 1 (right).
+ * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
+ */
+ p.setPan = function (value) {
+ if(value == this._pan) { return this; }
+ this._pan = Math.max(-1, Math.min(1, value));
+ this._updatePan();
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getPan
+ * @return {Number} The value of the pan, between -1 (left) and 1 (right).
+ */
+ p.getPan = function () {
+ return this._pan;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getPosition
+ * @return {Number} The position of the playhead in the sound, in milliseconds.
+ */
+ p.getPosition = function () {
+ if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._position = this._calculateCurrentPosition();
+ }
+ return this._position;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setPosition
+ * @param {Number} value The position to place the playhead, in milliseconds.
+ * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
+ */
+ p.setPosition = function (value) {
+ this._position = Math.max(0, value);
+ if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._updatePosition();
+ }
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getStartTime
+ * @return {Number} The startTime of the sound instance in milliseconds.
+ */
+ p.getStartTime = function () {
+ return this._startTime;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setStartTime
+ * @param {number} value The new startTime time in milli seconds.
+ * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
+ */
+ p.setStartTime = function (value) {
+ if (value == this._startTime) { return this; }
+ this._startTime = Math.max(0, value || 0);
+ this._updateStartTime();
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getDuration
+ * @return {Number} The duration of the sound instance in milliseconds.
+ */
+ p.getDuration = function () {
+ return this._duration;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setDuration
+ * @param {number} value The new duration time in milli seconds.
+ * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
+ * @since 0.6.0
+ */
+ p.setDuration = function (value) {
+ if (value == this._duration) { return this; }
+ this._duration = Math.max(0, value || 0);
+ this._updateDuration();
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setPlayback
+ * @param {Object} value The new playback resource.
+ * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
+ * @since 0.6.0
+ **/
+ p.setPlaybackResource = function (value) {
+ this._playbackResource = value;
+ if (this._duration == 0) { this._setDurationFromSource(); }
+ return this;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method setPlayback
+ * @param {Object} value The new playback resource.
+ * @return {Object} playback resource used for playing audio
+ * @since 0.6.0
+ **/
+ p.getPlaybackResource = function () {
+ return this._playbackResource;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ *
+ * @deprecated
+ * @method getLoop
+ * @return {number}
+ * @since 0.6.0
+ **/
+ p.getLoop = function () {
+ return this._loop;
+ };
+
+ /**
+ * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property,
+ *
+ * @deprecated
+ * @method setLoop
+ * @param {number} value The number of times to loop after play.
+ * @since 0.6.0
+ */
+ p.setLoop = function (value) {
+ if(this._playbackResource != null) {
+ // remove looping
+ if (this._loop != 0 && value == 0) {
+ this._removeLooping(value);
+ }
+ // add looping
+ else if (this._loop == 0 && value != 0) {
+ this._addLooping(value);
+ }
+ }
+ this._loop = value;
+ };
+
+
+// Private Methods:
+ /**
+ * A helper method that dispatches all events for AbstractSoundInstance.
+ * @method _sendEvent
+ * @param {String} type The event type
+ * @protected
+ */
+ p._sendEvent = function (type) {
+ var event = new createjs.Event(type);
+ this.dispatchEvent(event);
+ };
+
+ /**
+ * Clean up the instance. Remove references and clean up any additional properties such as timers.
+ * @method _cleanUp
+ * @protected
+ */
+ p._cleanUp = function () {
+ clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
+ this._handleCleanUp();
+ this._paused = false;
+
+ createjs.Sound._playFinished(this); // TODO change to an event
+ };
+
+ /**
+ * The sound has been interrupted.
+ * @method _interrupt
+ * @protected
+ */
+ p._interrupt = function () {
+ this._cleanUp();
+ this.playState = createjs.Sound.PLAY_INTERRUPTED;
+ this._sendEvent("interrupted");
+ };
+
+ /**
+ * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the
+ * src is loaded, otherwise playback will fail.
+ * @method _beginPlaying
+ * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
+ * @return {Boolean} If playback succeeded.
+ * @protected
+ */
+ // OJR FlashAudioSoundInstance overwrites
+ p._beginPlaying = function (playProps) {
+ this.setPosition(playProps.offset);
+ this.setLoop(playProps.loop);
+ this.setVolume(playProps.volume);
+ this.setPan(playProps.pan);
+ if (playProps.startTime != null) {
+ this.setStartTime(playProps.startTime);
+ this.setDuration(playProps.duration);
+ }
+
+ if (this._playbackResource != null && this._position < this._duration) {
+ this._paused = false;
+ this._handleSoundReady();
+ this.playState = createjs.Sound.PLAY_SUCCEEDED;
+ this._sendEvent("succeeded");
+ return true;
+ } else {
+ this._playFailed();
+ return false;
+ }
+ };
+
+ /**
+ * Play has failed, which can happen for a variety of reasons.
+ * Cleans up instance and dispatches failed event
+ * @method _playFailed
+ * @private
+ */
+ p._playFailed = function () {
+ this._cleanUp();
+ this.playState = createjs.Sound.PLAY_FAILED;
+ this._sendEvent("failed");
+ };
+
+ /**
+ * Audio has finished playing. Manually loop it if required.
+ * @method _handleSoundComplete
+ * @param event
+ * @protected
+ */
+ p._handleSoundComplete = function (event) {
+ this._position = 0; // have to set this as it can be set by pause during playback
+
+ if (this._loop != 0) {
+ this._loop--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1
+ this._handleLoop();
+ this._sendEvent("loop");
+ return;
+ }
+
+ this._cleanUp();
+ this.playState = createjs.Sound.PLAY_FINISHED;
+ this._sendEvent("complete");
+ };
+
+// Plugin specific code
+ /**
+ * Handles starting playback when the sound is ready for playing.
+ * @method _handleSoundReady
+ * @protected
+ */
+ p._handleSoundReady = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function used to update the volume based on the instance volume, master volume, instance mute value,
+ * and master mute value.
+ * @method _updateVolume
+ * @protected
+ */
+ p._updateVolume = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function used to update the pan
+ * @method _updatePan
+ * @protected
+ * @since 0.6.0
+ */
+ p._updatePan = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function used to update the startTime of the audio.
+ * @method _updateStartTime
+ * @protected
+ * @since 0.6.1
+ */
+ p._updateStartTime = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function used to update the duration of the audio.
+ * @method _updateDuration
+ * @protected
+ * @since 0.6.0
+ */
+ p._updateDuration = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function used to get the duration of the audio from the source we'll be playing.
+ * @method _updateDuration
+ * @protected
+ * @since 0.6.0
+ */
+ p._setDurationFromSource = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function that calculates the current position of the playhead and sets this._position to that value
+ * @method _calculateCurrentPosition
+ * @protected
+ * @since 0.6.0
+ */
+ p._calculateCurrentPosition = function () {
+ // plugin specific code that sets this.position
+ };
+
+ /**
+ * Internal function used to update the position of the playhead.
+ * @method _updatePosition
+ * @protected
+ * @since 0.6.0
+ */
+ p._updatePosition = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when looping is removed during playback.
+ * @method _removeLooping
+ * @param {number} value The number of times to loop after play.
+ * @protected
+ * @since 0.6.0
+ */
+ p._removeLooping = function (value) {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when looping is added during playback.
+ * @method _addLooping
+ * @param {number} value The number of times to loop after play.
+ * @protected
+ * @since 0.6.0
+ */
+ p._addLooping = function (value) {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when pausing playback
+ * @method _pause
+ * @protected
+ * @since 0.6.0
+ */
+ p._pause = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when resuming playback
+ * @method _resume
+ * @protected
+ * @since 0.6.0
+ */
+ p._resume = function () {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when stopping playback
+ * @method _handleStop
+ * @protected
+ * @since 0.6.0
+ */
+ p._handleStop = function() {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when AbstractSoundInstance is being cleaned up
+ * @method _handleCleanUp
+ * @protected
+ * @since 0.6.0
+ */
+ p._handleCleanUp = function() {
+ // plugin specific code
+ };
+
+ /**
+ * Internal function called when AbstractSoundInstance has played to end and is looping
+ * @method _handleLoop
+ * @protected
+ * @since 0.6.0
+ */
+ p._handleLoop = function () {
+ // plugin specific code
+ };
+
+ createjs.AbstractSoundInstance = createjs.promote(AbstractSoundInstance, "EventDispatcher");
+ createjs.DefaultSoundInstance = createjs.AbstractSoundInstance; // used when no plugin is supported
+}());
+
+//##############################################################################
+// AbstractPlugin.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+
+// constructor:
+ /**
+ * A default plugin class used as a base for all other plugins.
+ * @class AbstractPlugin
+ * @constructor
+ * @since 0.6.0
+ */
+
+ var AbstractPlugin = function () {
+ // private properties:
+ /**
+ * The capabilities of the plugin.
+ * method and is used internally.
+ * @property _capabilities
+ * @type {Object}
+ * @default null
+ * @protected
+ * @static
+ */
+ this._capabilities = null;
+
+ /**
+ * Object hash indexed by the source URI of all created loaders, used to properly destroy them if sources are removed.
+ * @type {Object}
+ * @protected
+ */
+ this._loaders = {};
+
+ /**
+ * Object hash indexed by the source URI of each file to indicate if an audio source has begun loading,
+ * is currently loading, or has completed loading. Can be used to store non boolean data after loading
+ * is complete (for example arrayBuffers for web audio).
+ * @property _audioSources
+ * @type {Object}
+ * @protected
+ */
+ this._audioSources = {};
+
+ /**
+ * Object hash indexed by the source URI of all created SoundInstances, updates the playbackResource if it loads after they are created,
+ * and properly destroy them if sources are removed
+ * @type {Object}
+ * @protected
+ */
+ this._soundInstances = {};
+
+ /**
+ * The internal master volume value of the plugin.
+ * @property _volume
+ * @type {Number}
+ * @default 1
+ * @protected
+ */
+ this._volume = 1;
+
+ /**
+ * A reference to a loader class used by a plugin that must be set.
+ * @type {Object}
+ * @protected
+ */
+ this._loaderClass;
+
+ /**
+ * A reference to an AbstractSoundInstance class used by a plugin that must be set.
+ * @type {Object}
+ * @protected;
+ */
+ this._soundInstanceClass;
+ };
+ var p = AbstractPlugin.prototype;
+
+ /**
+ * REMOVED. Removed in favor of using `MySuperClass_constructor`.
+ * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
+ * for details.
+ *
+ * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
+ *
+ * @method initialize
+ * @protected
+ * @deprecated
+ */
+ // p.initialize = function() {}; // searchable for devs wondering where it is.
+
+
+// Static Properties:
+// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
+ /**
+ * The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
+ * @property _capabilities
+ * @type {Object}
+ * @default null
+ * @protected
+ * @static
+ */
+ AbstractPlugin._capabilities = null;
+
+ /**
+ * Determine if the plugin can be used in the current browser/OS.
+ * @method isSupported
+ * @return {Boolean} If the plugin can be initialized.
+ * @static
+ */
+ AbstractPlugin.isSupported = function () {
+ return true;
+ };
+
+
+// public methods:
+ /**
+ * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}.
+ * Note all plugins provide a Loader instance, which PreloadJS
+ * can use to assist with preloading.
+ * @method register
+ * @param {String} loadItem An Object containing the source of the audio
+ * Note that not every plugin will manage this value.
+ * @return {Object} A result object, containing a "tag" for preloading purposes.
+ */
+ p.register = function (loadItem) {
+ var loader = this._loaders[loadItem.src];
+ if(loader && !loader.canceled) {return this._loaders[loadItem.src];} // already loading/loaded this, so don't load twice
+ // OJR potential issue that we won't be firing loaded event, might need to trigger if this is already loaded?
+ this._audioSources[loadItem.src] = true;
+ this._soundInstances[loadItem.src] = [];
+ loader = new this._loaderClass(loadItem);
+ loader.on("complete", createjs.proxy(this._handlePreloadComplete, this));
+ this._loaders[loadItem.src] = loader;
+ return loader;
+ };
+
+ // note sound calls register before calling preload
+ /**
+ * Internally preload a sound.
+ * @method preload
+ * @param {Loader} loader The sound URI to load.
+ */
+ p.preload = function (loader) {
+ loader.on("error", createjs.proxy(this._handlePreloadError, this));
+ loader.load();
+ };
+
+ /**
+ * Checks if preloading has started for a specific source. If the source is found, we can assume it is loading,
+ * or has already finished loading.
+ * @method isPreloadStarted
+ * @param {String} src The sound URI to check.
+ * @return {Boolean}
+ */
+ p.isPreloadStarted = function (src) {
+ return (this._audioSources[src] != null);
+ };
+
+ /**
+ * Checks if preloading has finished for a specific source.
+ * @method isPreloadComplete
+ * @param {String} src The sound URI to load.
+ * @return {Boolean}
+ */
+ p.isPreloadComplete = function (src) {
+ return (!(this._audioSources[src] == null || this._audioSources[src] == true));
+ };
+
+ /**
+ * Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
+ * @method removeSound
+ * @param {String} src The sound URI to unload.
+ */
+ p.removeSound = function (src) {
+ if (!this._soundInstances[src]) { return; }
+ for (var i = this._soundInstances[src].length; i--; ) {
+ var item = this._soundInstances[src][i];
+ item.destroy();
+ }
+ delete(this._soundInstances[src]);
+ delete(this._audioSources[src]);
+ if(this._loaders[src]) { this._loaders[src].destroy(); }
+ delete(this._loaders[src]);
+ };
+
+ /**
+ * Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
+ * @method removeAllSounds
+ * @param {String} src The sound URI to unload.
+ */
+ p.removeAllSounds = function () {
+ for(var key in this._audioSources) {
+ this.removeSound(key);
+ }
+ };
+
+ /**
+ * 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 {AbstractSoundInstance} A sound instance for playback and control.
+ */
+ p.create = function (src, startTime, duration) {
+ if (!this.isPreloadStarted(src)) {
+ this.preload(this.register(src));
+ }
+ var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
+ this._soundInstances[src].push(si);
+ return si;
+ };
+
+ // if a plugin does not support volume and mute, it should set these to null
+ /**
+ * Set the master volume of the plugin, which affects all SoundInstances.
+ * @method setVolume
+ * @param {Number} value The volume to set, between 0 and 1.
+ * @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the
+ * instances manually otherwise.
+ */
+ p.setVolume = function (value) {
+ this._volume = value;
+ this._updateVolume();
+ return true;
+ };
+
+ /**
+ * Get the master volume of the plugin, which affects all SoundInstances.
+ * @method getVolume
+ * @return {Number} The volume level, between 0 and 1.
+ */
+ p.getVolume = function () {
+ return this._volume;
+ };
+
+ /**
+ * Mute all sounds via the plugin.
+ * @method setMute
+ * @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up
+ * the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here.
+ * @return {Boolean} If the mute call succeeds.
+ */
+ p.setMute = function (value) {
+ this._updateVolume();
+ return true;
+ };
+
+ // plugins should overwrite this method
+ p.toString = function () {
+ return "[AbstractPlugin]";
+ };
+
+
+// private methods:
+ /**
+ * Handles internal preload completion.
+ * @method _handlePreloadComplete
+ * @protected
+ */
+ p._handlePreloadComplete = function (event) {
+ var src = event.target.getItem().src;
+ this._audioSources[src] = event.result;
+ for (var i = 0, l = this._soundInstances[src].length; i < l; i++) {
+ var item = this._soundInstances[src][i];
+ item.setPlaybackResource(this._audioSources[src]);
+ // ToDo consider adding play call here if playstate == playfailed
+ }
+ };
+
+ /**
+ * Handles internal preload erros
+ * @method _handlePreloadError
+ * @param event
+ * @protected
+ */
+ p._handlePreloadError = function(event) {
+ //delete(this._audioSources[src]);
+ };
+
+ /**
+ * Set the gain value for master audio. Should not be called externally.
+ * @method _updateVolume
+ * @protected
+ */
+ p._updateVolume = function () {
+ // Plugin Specific code
+ };
+
+ createjs.AbstractPlugin = AbstractPlugin;
}());
//##############################################################################
// WebAudioLoader.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- /**
- * Loader provides a mechanism to preload Web Audio content via PreloadJS or internally. Instances are returned to
- * the preloader, and the load method is called when the asset needs to be requested.
- *
- * @class WebAudioLoader
- * @param {String} loadItem The item to be loaded
- * @extends XHRRequest
- * @protected
- */
- function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
-
- };
- var p = createjs.extend(Loader, createjs.AbstractLoader);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
- /**
- * web audio context required for decoding audio
- * @property context
- * @type {AudioContext}
- * @static
- */
- Loader.context = null;
-
-
-// public methods
- p.toString = function () {
- return "[WebAudioLoader]";
- };
-
-
-// private methods
- p._createRequest = function() {
- this._request = new createjs.XHRRequest(this._item, false);
- this._request.setResponseType("arraybuffer");
- };
-
- p._sendComplete = function (event) {
- // OJR we leave this wrapped in Loader because we need to reference src and the handler only receives a single argument, the decodedAudio
- Loader.context.decodeAudioData(this._rawResult,
- createjs.proxy(this._handleAudioDecoded, this),
- createjs.proxy(this._sendError, this));
- };
-
-
- /**
- * The audio has been decoded.
- * @method handleAudioDecoded
- * @param decoded
- * @protected
- */
- p._handleAudioDecoded = function (decodedAudio) {
- this._result = decodedAudio;
- this.AbstractLoader__sendComplete();
- };
-
- createjs.WebAudioLoader = createjs.promote(Loader, "AbstractLoader");
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * Loader provides a mechanism to preload Web Audio content via PreloadJS or internally. Instances are returned to
+ * the preloader, and the load method is called when the asset needs to be requested.
+ *
+ * @class WebAudioLoader
+ * @param {String} loadItem The item to be loaded
+ * @extends XHRRequest
+ * @protected
+ */
+ function Loader(loadItem) {
+ this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
+
+ };
+ var p = createjs.extend(Loader, createjs.AbstractLoader);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+ /**
+ * web audio context required for decoding audio
+ * @property context
+ * @type {AudioContext}
+ * @static
+ */
+ Loader.context = null;
+
+
+// public methods
+ p.toString = function () {
+ return "[WebAudioLoader]";
+ };
+
+
+// private methods
+ p._createRequest = function() {
+ this._request = new createjs.XHRRequest(this._item, false);
+ this._request.setResponseType("arraybuffer");
+ };
+
+ p._sendComplete = function (event) {
+ // OJR we leave this wrapped in Loader because we need to reference src and the handler only receives a single argument, the decodedAudio
+ Loader.context.decodeAudioData(this._rawResult,
+ createjs.proxy(this._handleAudioDecoded, this),
+ createjs.proxy(this._sendError, this));
+ };
+
+
+ /**
+ * The audio has been decoded.
+ * @method handleAudioDecoded
+ * @param decoded
+ * @protected
+ */
+ p._handleAudioDecoded = function (decodedAudio) {
+ this._result = decodedAudio;
+ this.AbstractLoader__sendComplete();
+ };
+
+ createjs.WebAudioLoader = createjs.promote(Loader, "AbstractLoader");
}());
//##############################################################################
// WebAudioSoundInstance.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-/**
- * WebAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
- * {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
- *
- * WebAudioSoundInstance exposes audioNodes for advanced users.
- *
- * @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} playbackResource Any resource needed by plugin to support audio playback.
- * @class WebAudioSoundInstance
- * @extends AbstractSoundInstance
- * @constructor
- */
-(function () {
- "use strict";
-
- function WebAudioSoundInstance(src, startTime, duration, playbackResource) {
- this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
-
-
-// public properties
- /**
- * NOTE this is only intended for use by advanced users.
- * GainNode for controlling WebAudioSoundInstance volume. Connected to the {{#crossLink "WebAudioSoundInstance/destinationNode:property"}}{{/crossLink}}.
- * @property gainNode
- * @type {AudioGainNode}
- * @since 0.4.0
- *
- */
- this.gainNode = s.context.createGain();
-
- /**
- * NOTE this is only intended for use by advanced users.
- * A panNode allowing left and right audio channel panning only. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
- * @property panNode
- * @type {AudioPannerNode}
- * @since 0.4.0
- */
- this.panNode = s.context.createPanner();
- this.panNode.panningModel = s._panningModel;
- this.panNode.connect(this.gainNode);
-
- /**
- * NOTE this is only intended for use by advanced users.
- * sourceNode is the audio source. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/panNode:property"}}{{/crossLink}}.
- * @property sourceNode
- * @type {AudioNode}
- * @since 0.4.0
- *
- */
- this.sourceNode = null;
-
-
-// private properties
- /**
- * Timeout that is created internally to handle sound playing to completion.
- * Stored so we can remove it when stop, pause, or cleanup are called
- * @property _soundCompleteTimeout
- * @type {timeoutVariable}
- * @default null
- * @protected
- * @since 0.4.0
- */
- this._soundCompleteTimeout = null;
-
- /**
- * NOTE this is only intended for use by very advanced users.
- * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth
- * looping. Connected to {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
- * @property _sourceNodeNext
- * @type {AudioNode}
- * @default null
- * @protected
- * @since 0.4.1
- *
- */
- this._sourceNodeNext = null;
-
- /**
- * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
- * @property _playbackStartTime
- * @type {Number}
- * @default 0
- * @protected
- * @since 0.4.0
- */
- this._playbackStartTime = 0;
-
- // Proxies, make removing listeners easier.
- this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
- };
- var p = createjs.extend(WebAudioSoundInstance, createjs.AbstractSoundInstance);
- var s = WebAudioSoundInstance;
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
- /**
- * Note this is only intended for use by advanced users.
- * Audio context used to create nodes. This is and needs to be the same context used by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
- * @property context
- * @type {AudioContext}
- * @static
- * @since 0.6.0
- */
- s.context = null;
-
- /**
- * Note this is only intended for use by advanced users.
- * Audio node from WebAudioPlugin that sequences to context.destination
- * @property destinationNode
- * @type {AudioNode}
- * @static
- * @since 0.6.0
- */
- s.destinationNode = null;
-
- /**
- * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
- * @property _panningModel
- * @type {Number / String}
- * @protected
- * @static
- * @since 0.6.0
- */
- s._panningModel = "equalpower";
-
-
-// Public methods
- p.destroy = function() {
- this.AbstractSoundInstance_destroy();
-
- this.panNode.disconnect(0);
- this.panNode = null;
- this.gainNode.disconnect(0);
- this.gainNode = null;
- };
-
- p.toString = function () {
- return "[WebAudioSoundInstance]";
- };
-
-
-// Private Methods
- p._updatePan = function() {
- this.panNode.setPosition(this._pan, 0, -0.5);
- // z need to be -0.5 otherwise the sound only plays in left, right, or center
- };
-
- p._removeLooping = function(value) {
- this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
- };
-
- p._addLooping = function(value) {
- if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
- this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
- };
-
- p._setDurationFromSource = function () {
- this._duration = this.playbackResource.duration * 1000;
- };
-
- p._handleCleanUp = function () {
- if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
- this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
- }
-
- 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._soundCompleteTimeout);
-
- this._playbackStartTime = 0; // This is used by getPosition
- };
-
- /**
- * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection
- * @method _cleanUpAudioNode
- * @param audioNode
- * @return {audioNode}
- * @protected
- * @since 0.4.1
- */
- p._cleanUpAudioNode = function(audioNode) {
- if(audioNode) {
- audioNode.stop(0);
- audioNode.disconnect(0);
- audioNode = null;
- }
- return audioNode;
- };
-
- p._handleSoundReady = function (event) {
- this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
-
- var dur = this._duration * 0.001;
- var pos = this._position * 0.001;
- if (pos > dur) {pos = dur;}
- this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
- this._playbackStartTime = this.sourceNode.startTime - pos;
-
- this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
-
- if(this._loop != 0) {
- this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
- }
- };
-
- /**
- * Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
- * @method _createAndPlayAudioNode
- * @param {Number} startTime The time to add this to the web audio context, in seconds.
- * @param {Number} offset The amount of time into the src audio to start playback, in seconds.
- * @return {audioNode}
- * @protected
- * @since 0.4.1
- */
- p._createAndPlayAudioNode = function(startTime, offset) {
- var audioNode = s.context.createBufferSource();
- audioNode.buffer = this.playbackResource;
- audioNode.connect(this.panNode);
- var dur = this._duration * 0.001;
- audioNode.startTime = startTime + dur;
- audioNode.start(audioNode.startTime, offset+(this._startTime*0.001), dur - offset);
- return audioNode;
- };
-
- p._pause = function () {
- this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
- this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
- this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
-
- if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
-
- clearTimeout(this._soundCompleteTimeout);
- };
-
- p._resume = function () {
- this._handleSoundReady();
- };
-
- /*
- p._handleStop = function () {
- // web audio does not need to do anything extra
- };
- */
-
- p._updateVolume = function () {
- var newVolume = this._muted ? 0 : this._volume;
- if (newVolume != this.gainNode.gain.value) {
- this.gainNode.gain.value = newVolume;
- }
- };
-
- p._calculateCurrentPosition = function () {
- return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
- };
-
- p._updatePosition = function () {
- this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
- this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
- clearTimeout(this._soundCompleteTimeout);
-
- if (!this._paused) {this._handleSoundReady();}
- };
-
- // OJR we are using a look ahead approach to ensure smooth looping.
- // We add _sourceNodeNext to the audio context so that it starts playing even if this callback is delayed.
- // This technique is described here: http://www.html5rocks.com/en/tutorials/audio/scheduling/
- // NOTE the cost of this is that our audio loop may not always match the loop event timing precisely.
- p._handleLoop = function () {
- this._cleanUpAudioNode(this.sourceNode);
- this.sourceNode = this._sourceNodeNext;
- this._playbackStartTime = this.sourceNode.startTime;
- this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
- this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
- };
-
- p._updateDuration = function () {
- if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this._pause();
- this._resume();
- }
- };
-
- createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
+this.createjs = this.createjs || {};
+
+/**
+ * WebAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
+ * {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
+ *
+ * WebAudioSoundInstance exposes audioNodes for advanced users.
+ *
+ * @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} playbackResource Any resource needed by plugin to support audio playback.
+ * @class WebAudioSoundInstance
+ * @extends AbstractSoundInstance
+ * @constructor
+ */
+(function () {
+ "use strict";
+
+ function WebAudioSoundInstance(src, startTime, duration, playbackResource) {
+ this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
+
+
+// public properties
+ /**
+ * NOTE this is only intended for use by advanced users.
+ * GainNode for controlling WebAudioSoundInstance volume. Connected to the {{#crossLink "WebAudioSoundInstance/destinationNode:property"}}{{/crossLink}}.
+ * @property gainNode
+ * @type {AudioGainNode}
+ * @since 0.4.0
+ *
+ */
+ this.gainNode = s.context.createGain();
+
+ /**
+ * NOTE this is only intended for use by advanced users.
+ * A panNode allowing left and right audio channel panning only. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
+ * @property panNode
+ * @type {AudioPannerNode}
+ * @since 0.4.0
+ */
+ this.panNode = s.context.createPanner();
+ this.panNode.panningModel = s._panningModel;
+ this.panNode.connect(this.gainNode);
+
+ /**
+ * NOTE this is only intended for use by advanced users.
+ * sourceNode is the audio source. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/panNode:property"}}{{/crossLink}}.
+ * @property sourceNode
+ * @type {AudioNode}
+ * @since 0.4.0
+ *
+ */
+ this.sourceNode = null;
+
+
+// private properties
+ /**
+ * Timeout that is created internally to handle sound playing to completion.
+ * Stored so we can remove it when stop, pause, or cleanup are called
+ * @property _soundCompleteTimeout
+ * @type {timeoutVariable}
+ * @default null
+ * @protected
+ * @since 0.4.0
+ */
+ this._soundCompleteTimeout = null;
+
+ /**
+ * NOTE this is only intended for use by very advanced users.
+ * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth
+ * looping. Connected to {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
+ * @property _sourceNodeNext
+ * @type {AudioNode}
+ * @default null
+ * @protected
+ * @since 0.4.1
+ *
+ */
+ this._sourceNodeNext = null;
+
+ /**
+ * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
+ * @property _playbackStartTime
+ * @type {Number}
+ * @default 0
+ * @protected
+ * @since 0.4.0
+ */
+ this._playbackStartTime = 0;
+
+ // Proxies, make removing listeners easier.
+ this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
+ };
+ var p = createjs.extend(WebAudioSoundInstance, createjs.AbstractSoundInstance);
+ var s = WebAudioSoundInstance;
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+ /**
+ * Note this is only intended for use by advanced users.
+ * Audio context used to create nodes. This is and needs to be the same context used by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
+ * @property context
+ * @type {AudioContext}
+ * @static
+ * @since 0.6.0
+ */
+ s.context = null;
+
+ /**
+ * Note this is only intended for use by advanced users.
+ * Audio node from WebAudioPlugin that sequences to context.destination
+ * @property destinationNode
+ * @type {AudioNode}
+ * @static
+ * @since 0.6.0
+ */
+ s.destinationNode = null;
+
+ /**
+ * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
+ * @property _panningModel
+ * @type {Number / String}
+ * @protected
+ * @static
+ * @since 0.6.0
+ */
+ s._panningModel = "equalpower";
+
+
+// Public methods
+ p.destroy = function() {
+ this.AbstractSoundInstance_destroy();
+
+ this.panNode.disconnect(0);
+ this.panNode = null;
+ this.gainNode.disconnect(0);
+ this.gainNode = null;
+ };
+
+ p.toString = function () {
+ return "[WebAudioSoundInstance]";
+ };
+
+
+// Private Methods
+ p._updatePan = function() {
+ this.panNode.setPosition(this._pan, 0, -0.5);
+ // z need to be -0.5 otherwise the sound only plays in left, right, or center
+ };
+
+ p._removeLooping = function(value) {
+ this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
+ };
+
+ p._addLooping = function(value) {
+ if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
+ this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
+ };
+
+ p._setDurationFromSource = function () {
+ this._duration = this.playbackResource.duration * 1000;
+ };
+
+ p._handleCleanUp = function () {
+ if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
+ this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
+ }
+
+ 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._soundCompleteTimeout);
+
+ this._playbackStartTime = 0; // This is used by getPosition
+ };
+
+ /**
+ * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection
+ * @method _cleanUpAudioNode
+ * @param audioNode
+ * @return {audioNode}
+ * @protected
+ * @since 0.4.1
+ */
+ p._cleanUpAudioNode = function(audioNode) {
+ if(audioNode) {
+ audioNode.stop(0);
+ audioNode.disconnect(0);
+ audioNode = null;
+ }
+ return audioNode;
+ };
+
+ p._handleSoundReady = function (event) {
+ this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
+
+ var dur = this._duration * 0.001;
+ var pos = this._position * 0.001;
+ if (pos > dur) {pos = dur;}
+ this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
+ this._playbackStartTime = this.sourceNode.startTime - pos;
+
+ this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
+
+ if(this._loop != 0) {
+ this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
+ }
+ };
+
+ /**
+ * Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
+ * @method _createAndPlayAudioNode
+ * @param {Number} startTime The time to add this to the web audio context, in seconds.
+ * @param {Number} offset The amount of time into the src audio to start playback, in seconds.
+ * @return {audioNode}
+ * @protected
+ * @since 0.4.1
+ */
+ p._createAndPlayAudioNode = function(startTime, offset) {
+ var audioNode = s.context.createBufferSource();
+ audioNode.buffer = this.playbackResource;
+ audioNode.connect(this.panNode);
+ var dur = this._duration * 0.001;
+ audioNode.startTime = startTime + dur;
+ audioNode.start(audioNode.startTime, offset+(this._startTime*0.001), dur - offset);
+ return audioNode;
+ };
+
+ p._pause = function () {
+ this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
+ this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
+ this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
+
+ if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
+
+ clearTimeout(this._soundCompleteTimeout);
+ };
+
+ p._resume = function () {
+ this._handleSoundReady();
+ };
+
+ /*
+ p._handleStop = function () {
+ // web audio does not need to do anything extra
+ };
+ */
+
+ p._updateVolume = function () {
+ var newVolume = this._muted ? 0 : this._volume;
+ if (newVolume != this.gainNode.gain.value) {
+ this.gainNode.gain.value = newVolume;
+ }
+ };
+
+ p._calculateCurrentPosition = function () {
+ return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
+ };
+
+ p._updatePosition = function () {
+ this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
+ this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
+ clearTimeout(this._soundCompleteTimeout);
+
+ if (!this._paused) {this._handleSoundReady();}
+ };
+
+ // OJR we are using a look ahead approach to ensure smooth looping.
+ // We add _sourceNodeNext to the audio context so that it starts playing even if this callback is delayed.
+ // This technique is described here: http://www.html5rocks.com/en/tutorials/audio/scheduling/
+ // NOTE the cost of this is that our audio loop may not always match the loop event timing precisely.
+ p._handleLoop = function () {
+ this._cleanUpAudioNode(this.sourceNode);
+ this.sourceNode = this._sourceNodeNext;
+ this._playbackStartTime = this.sourceNode.startTime;
+ this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
+ this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
+ };
+
+ p._updateDuration = function () {
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._pause();
+ this._resume();
+ }
+ };
+
+ createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
}());
//##############################################################################
// WebAudioPlugin.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
-
- "use strict";
-
- /**
- * Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used
- * anywhere that it is supported. To change plugin priority, check out the Sound API
- * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
-
- *
Known Browser and OS issues for Web Audio
- * Firefox 25
- *
mp3 audio files do not load properly on all windows machines, reported
- * here.
- * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.
- *
- * Webkit (Chrome and Safari)
- *
AudioNode.disconnect does not always seem to work. This can cause the file size to grow over time if you
- * are playing a lot of audio files.
- *
- * iOS 6 limitations
- *
Sound is initially muted and will only unmute through play being called inside a user initiated event (touch/click).
- *
A bug exists that will distort uncached audio when a video element is present in the DOM. You can avoid this bug
- * by ensuring the audio and video audio share the same sampleRate.
- *
- * @class WebAudioPlugin
- * @extends AbstractPlugin
- * @constructor
- * @since 0.4.0
- */
- function WebAudioPlugin() {
- this.AbstractPlugin_constructor();
-
-
-// Private Properties
- /**
- * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
- * @property _panningModel
- * @type {Number / String}
- * @protected
- */
- this._panningModel = s._panningModel;;
-
- /**
- * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
- * need to be created within this context.
- * @property context
- * @type {AudioContext}
- */
- this.context = s.context;
-
- /**
- * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
- * It is connected to context.destination.
- *
- * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode.
- * @property dynamicsCompressorNode
- * @type {AudioNode}
- */
- this.dynamicsCompressorNode = this.context.createDynamicsCompressor();
- this.dynamicsCompressorNode.connect(this.context.destination);
-
- /**
- * 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}
- */
- this.gainNode = this.context.createGain();
- this.gainNode.connect(this.dynamicsCompressorNode);
- createjs.WebAudioSoundInstance.destinationNode = this.gainNode;
-
- this._capabilities = s._capabilities;
-
- this._loaderClass = createjs.WebAudioLoader;
- this._soundInstanceClass = createjs.WebAudioSoundInstance;
-
- this._addPropsToClasses();
- }
- var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// Static Properties
- var s = WebAudioPlugin;
- /**
- * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
- * method and is used internally.
- * @property _capabilities
- * @type {Object}
- * @default null
- * @protected
- * @static
- */
- s._capabilities = null;
-
- /**
- * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
- * @property _panningModel
- * @type {Number / String}
- * @protected
- * @static
- */
- s._panningModel = "equalpower";
-
- /**
- * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
- * need to be created within this context.
- *
- * Advanced users can set this to an existing context, but must do so before they call
- * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
- *
- * @property context
- * @type {AudioContext}
- * @static
- */
- s.context = null;
-
-
-// Static Public Methods
- /**
- * Determine if the plugin can be used in the current browser/OS.
- * @method isSupported
- * @return {Boolean} If the plugin can be initialized.
- * @static
- */
- s.isSupported = function () {
- // check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file
- var isMobilePhoneGap = createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry;
- // OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing.
- if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally
- s._generateCapabilities();
- if (s.context == null) {return false;}
- return true;
- };
-
- /**
- * Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they
- * require the first sound to be played inside of a user initiated event (touch/click). This is called when
- * {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}
- * for example).
- *
- *
Example
- *
- * function handleTouch(event) {
- * createjs.WebAudioPlugin.playEmptySound();
- * }
- *
- * @method playEmptySound
- * @static
- * @since 0.4.1
- */
- s.playEmptySound = function() {
- if (s.context == null) {return;}
- var source = s.context.createBufferSource();
- source.buffer = s.context.createBuffer(1, 1, 22050);
- source.connect(s.context.destination);
- source.start(0, 0, 0);
- };
-
-
-// Static Private Methods
- /**
- * Determine if XHR is supported, which is necessary for web audio.
- * @method _isFileXHRSupported
- * @return {Boolean} If XHR is supported.
- * @since 0.4.2
- * @protected
- * @static
- */
- s._isFileXHRSupported = function() {
- // it's much easier to detect when something goes wrong, so let's start optimistically
- var supported = true;
-
- var xhr = new XMLHttpRequest();
- try {
- 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;
- return supported;
- }
- xhr.onerror = function() { supported = false; }; // cause irrelevant
- // with security turned off, we can get empty success results, which is actually a failed read (status code 0?)
- xhr.onload = function() { supported = this.status == 404 || (this.status == 200 || (this.status == 0 && this.response != "")); };
- try {
- xhr.send();
- } catch (error) {
- // catch errors in cases where the onerror is passed by
- supported = false;
- }
-
- return supported;
- };
-
- /**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
- * method for an overview of plugin capabilities.
- * @method _generateCapabilities
- * @static
- * @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
- var t = document.createElement("audio");
- if (t.canPlayType == null) {return null;}
-
- if (s.context == null) {
- if (window.AudioContext) {
- s.context = new AudioContext();
- } else if (window.webkitAudioContext) {
- s.context = new webkitAudioContext();
- } else {
- return null;
- }
- }
-
- s._compatibilitySetUp();
-
- // playing this inside of a touch event will enable audio on iOS, which starts muted
- s.playEmptySound();
-
- s._capabilities = {
- panning:true,
- volume:true,
- tracks:-1
- };
-
- // determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
- var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
- var extensionMap = createjs.Sound.EXTENSION_MAP;
- for (var i = 0, l = supportedExtensions.length; i < l; i++) {
- var ext = supportedExtensions[i];
- var playType = extensionMap[ext] || ext;
- s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
- } // OJR another way to do this might be canPlayType:"m4a", codex: mp4
-
- // 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround.
- // See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels.
- if (s.context.destination.numberOfChannels < 2) {
- s._capabilities.panning = false;
- }
- };
-
- /**
- * Set up compatibility if only deprecated web audio calls are supported.
- * See http://www.w3.org/TR/webaudio/#DeprecationNotes
- * Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that
- * don't support new calls.
- *
- * @method _compatibilitySetUp
- * @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; }
-
- // simple name change, functionality the same
- s.context.createGain = s.context.createGainNode;
-
- // source node, add to prototype
- var audioNode = s.context.createBufferSource();
- audioNode.__proto__.start = audioNode.__proto__.noteGrainOn; // note that noteGrainOn requires all 3 parameters
- audioNode.__proto__.stop = audioNode.__proto__.noteOff;
-
- // panningModel
- s._panningModel = 0;
- };
-
-
-// Public Methods
- p.toString = function () {
- return "[WebAudioPlugin]";
- };
-
-
-// Private Methods
- /**
- * Set up needed properties on supported classes WebAudioSoundInstance and WebAudioLoader.
- * @method _addPropsToClasses
- * @static
- * @protected
- * @since 0.6.0
- */
- p._addPropsToClasses = function() {
- var c = this._soundInstanceClass;
- c.context = this.context;
- c.destinationNode = this.gainNode;
- c._panningModel = this._panningModel;
-
- this._loaderClass.context = this.context;
- };
-
-
- /**
- * Set the gain value for master audio. Should not be called externally.
- * @method _updateVolume
- * @protected
- */
- p._updateVolume = function () {
- var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
- if (newVolume != this.gainNode.gain.value) {
- this.gainNode.gain.value = newVolume;
- }
- };
-
- createjs.WebAudioPlugin = createjs.promote(WebAudioPlugin, "AbstractPlugin");
+this.createjs = this.createjs || {};
+
+(function () {
+
+ "use strict";
+
+ /**
+ * Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used
+ * anywhere that it is supported. To change plugin priority, check out the Sound API
+ * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
+
+ *
Known Browser and OS issues for Web Audio
+ * Firefox 25
+ *
mp3 audio files do not load properly on all windows machines, reported
+ * here.
+ * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if possible.
+ *
+ * Webkit (Chrome and Safari)
+ *
AudioNode.disconnect does not always seem to work. This can cause the file size to grow over time if you
+ * are playing a lot of audio files.
+ *
+ * iOS 6 limitations
+ *
Sound is initially muted and will only unmute through play being called inside a user initiated event (touch/click).
+ *
A bug exists that will distort uncached audio when a video element is present in the DOM. You can avoid this bug
+ * by ensuring the audio and video audio share the same sampleRate.
+ *
+ * @class WebAudioPlugin
+ * @extends AbstractPlugin
+ * @constructor
+ * @since 0.4.0
+ */
+ function WebAudioPlugin() {
+ this.AbstractPlugin_constructor();
+
+
+// Private Properties
+ /**
+ * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
+ * @property _panningModel
+ * @type {Number / String}
+ * @protected
+ */
+ this._panningModel = s._panningModel;;
+
+ /**
+ * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
+ * need to be created within this context.
+ * @property context
+ * @type {AudioContext}
+ */
+ this.context = s.context;
+
+ /**
+ * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
+ * It is connected to context.destination.
+ *
+ * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode.
+ * @property dynamicsCompressorNode
+ * @type {AudioNode}
+ */
+ this.dynamicsCompressorNode = this.context.createDynamicsCompressor();
+ this.dynamicsCompressorNode.connect(this.context.destination);
+
+ /**
+ * 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}
+ */
+ this.gainNode = this.context.createGain();
+ this.gainNode.connect(this.dynamicsCompressorNode);
+ createjs.WebAudioSoundInstance.destinationNode = this.gainNode;
+
+ this._capabilities = s._capabilities;
+
+ this._loaderClass = createjs.WebAudioLoader;
+ this._soundInstanceClass = createjs.WebAudioSoundInstance;
+
+ this._addPropsToClasses();
+ }
+ var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// Static Properties
+ var s = WebAudioPlugin;
+ /**
+ * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
+ * method and is used internally.
+ * @property _capabilities
+ * @type {Object}
+ * @default null
+ * @protected
+ * @static
+ */
+ s._capabilities = null;
+
+ /**
+ * Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
+ * @property _panningModel
+ * @type {Number / String}
+ * @protected
+ * @static
+ */
+ s._panningModel = "equalpower";
+
+ /**
+ * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
+ * need to be created within this context.
+ *
+ * Advanced users can set this to an existing context, but must do so before they call
+ * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
+ *
+ * @property context
+ * @type {AudioContext}
+ * @static
+ */
+ s.context = null;
+
+
+// Static Public Methods
+ /**
+ * Determine if the plugin can be used in the current browser/OS.
+ * @method isSupported
+ * @return {Boolean} If the plugin can be initialized.
+ * @static
+ */
+ s.isSupported = function () {
+ // check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file
+ var isMobilePhoneGap = createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry;
+ // OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing.
+ if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally
+ s._generateCapabilities();
+ if (s.context == null) {return false;}
+ return true;
+ };
+
+ /**
+ * Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they
+ * require the first sound to be played inside of a user initiated event (touch/click). This is called when
+ * {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}
+ * for example).
+ *
+ *
Example
+ *
+ * function handleTouch(event) {
+ * createjs.WebAudioPlugin.playEmptySound();
+ * }
+ *
+ * @method playEmptySound
+ * @static
+ * @since 0.4.1
+ */
+ s.playEmptySound = function() {
+ if (s.context == null) {return;}
+ var source = s.context.createBufferSource();
+ source.buffer = s.context.createBuffer(1, 1, 22050);
+ source.connect(s.context.destination);
+ source.start(0, 0, 0);
+ };
+
+
+// Static Private Methods
+ /**
+ * Determine if XHR is supported, which is necessary for web audio.
+ * @method _isFileXHRSupported
+ * @return {Boolean} If XHR is supported.
+ * @since 0.4.2
+ * @protected
+ * @static
+ */
+ s._isFileXHRSupported = function() {
+ // it's much easier to detect when something goes wrong, so let's start optimistically
+ var supported = true;
+
+ var xhr = new XMLHttpRequest();
+ try {
+ 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;
+ return supported;
+ }
+ xhr.onerror = function() { supported = false; }; // cause irrelevant
+ // with security turned off, we can get empty success results, which is actually a failed read (status code 0?)
+ xhr.onload = function() { supported = this.status == 404 || (this.status == 200 || (this.status == 0 && this.response != "")); };
+ try {
+ xhr.send();
+ } catch (error) {
+ // catch errors in cases where the onerror is passed by
+ supported = false;
+ }
+
+ return supported;
+ };
+
+ /**
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * method for an overview of plugin capabilities.
+ * @method _generateCapabilities
+ * @static
+ * @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
+ var t = document.createElement("audio");
+ if (t.canPlayType == null) {return null;}
+
+ if (s.context == null) {
+ if (window.AudioContext) {
+ s.context = new AudioContext();
+ } else if (window.webkitAudioContext) {
+ s.context = new webkitAudioContext();
+ } else {
+ return null;
+ }
+ }
+
+ s._compatibilitySetUp();
+
+ // playing this inside of a touch event will enable audio on iOS, which starts muted
+ s.playEmptySound();
+
+ s._capabilities = {
+ panning:true,
+ volume:true,
+ tracks:-1
+ };
+
+ // determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
+ var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
+ var extensionMap = createjs.Sound.EXTENSION_MAP;
+ for (var i = 0, l = supportedExtensions.length; i < l; i++) {
+ var ext = supportedExtensions[i];
+ var playType = extensionMap[ext] || ext;
+ s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
+ } // OJR another way to do this might be canPlayType:"m4a", codex: mp4
+
+ // 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround.
+ // See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels.
+ if (s.context.destination.numberOfChannels < 2) {
+ s._capabilities.panning = false;
+ }
+ };
+
+ /**
+ * Set up compatibility if only deprecated web audio calls are supported.
+ * See http://www.w3.org/TR/webaudio/#DeprecationNotes
+ * Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that
+ * don't support new calls.
+ *
+ * @method _compatibilitySetUp
+ * @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; }
+
+ // simple name change, functionality the same
+ s.context.createGain = s.context.createGainNode;
+
+ // source node, add to prototype
+ var audioNode = s.context.createBufferSource();
+ audioNode.__proto__.start = audioNode.__proto__.noteGrainOn; // note that noteGrainOn requires all 3 parameters
+ audioNode.__proto__.stop = audioNode.__proto__.noteOff;
+
+ // panningModel
+ s._panningModel = 0;
+ };
+
+
+// Public Methods
+ p.toString = function () {
+ return "[WebAudioPlugin]";
+ };
+
+
+// Private Methods
+ /**
+ * Set up needed properties on supported classes WebAudioSoundInstance and WebAudioLoader.
+ * @method _addPropsToClasses
+ * @static
+ * @protected
+ * @since 0.6.0
+ */
+ p._addPropsToClasses = function() {
+ var c = this._soundInstanceClass;
+ c.context = this.context;
+ c.destinationNode = this.gainNode;
+ c._panningModel = this._panningModel;
+
+ this._loaderClass.context = this.context;
+ };
+
+
+ /**
+ * Set the gain value for master audio. Should not be called externally.
+ * @method _updateVolume
+ * @protected
+ */
+ p._updateVolume = function () {
+ var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
+ if (newVolume != this.gainNode.gain.value) {
+ this.gainNode.gain.value = newVolume;
+ }
+ };
+
+ createjs.WebAudioPlugin = createjs.promote(WebAudioPlugin, "AbstractPlugin");
}());
//##############################################################################
// HTMLAudioTagPool.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- /**
- * HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
- * @class HTMLAudioTagPool
- * @param {String} src The source of the channel.
- * @protected
- */
- function HTMLAudioTagPool() {
- throw "HTMLAudioTagPool cannot be instantiated";
- }
-
- var s = HTMLAudioTagPool;
-
-// Static Properties
- /**
- * A hash lookup of each base audio tag, indexed by the audio source.
- * @property _tags
- * @type {{}}
- * @static
- * @protected
- */
- s._tags = {};
-
- /**
- * An object pool for html audio tags
- * @property _tagPool
- * @type {TagPool}
- * @static
- * @protected
- */
- s._tagPool = new TagPool();
-
- /**
- * A hash lookup of if a base audio tag is available, indexed by the audio source
- * @property _tagsUsed
- * @type {{}}
- * @protected
- * @static
- */
- s._tagUsed = {};
-
-// Static Methods
- /**
- * Get an audio tag with the given source.
- * @method get
- * @param {String} src The source file used by the audio tag.
- * @static
- */
- s.get = function (src) {
- var t = s._tags[src];
- if (t == null) {
- // create new base tag
- t = s._tags[src] = s._tagPool.get();
- t.src = src;
- } else {
- // get base or pool
- if (s._tagUsed[src]) {
- t = s._tagPool.get();
- t.src = src;
- } else {
- s._tagUsed[src] = true;
- }
- }
- return t;
- };
-
- /**
- * Return an audio tag to the pool.
- * @method set
- * @param {String} src The source file used by the audio tag.
- * @param {HTMLElement} tag Audio tag to set.
- * @static
- */
- s.set = function (src, tag) {
- // check if this is base, if yes set boolean if not return to pool
- if(tag == s._tags[src]) {
- s._tagUsed[src] = false;
- } else {
- s._tagPool.set(tag);
- }
- };
-
- /**
- * Delete stored tag reference and return them to pool. Note that if the tag reference does not exist, this will fail.
- * @method remove
- * @param {String} src The source for the tag
- * @return {Boolean} If the TagPool was deleted.
- * @static
- */
- s.remove = function (src) {
- var tag = s._tags[src];
- if (tag == null) {return false;}
- s._tagPool.set(tag);
- delete(s._tags[src]);
- delete(s._tagUsed[src]);
- return true;
- };
-
- /**
- * 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
- * @static
- */
- s.getDuration= function (src) {
- var t = s._tags[src];
- if (t == null) {return 0;}
- return t.duration * 1000;
- };
-
- createjs.HTMLAudioTagPool = HTMLAudioTagPool;
-
-
-// ************************************************************************************************************
- /**
- * The TagPool is an object pool for HTMLAudio tag instances.
- * #class TagPool
- * @param {String} src The source of the channel.
- * @protected
- */
- function TagPool(src) {
-
-// Public Properties
- /**
- * A list of all available tags in the pool.
- * #property tags
- * @type {Array}
- * @protected
- */
- this._tags = [];
- };
-
- var p = TagPool.prototype;
- p.constructor = TagPool;
-
-
-// Public Methods
- /**
- * Get an HTMLAudioElement for immediate playback. This takes it out of the pool.
- * #method get
- * @return {HTMLAudioElement} An HTML audio tag.
- */
- p.get = function () {
- var tag;
- if (this._tags.length == 0) {
- tag = this._createTag();
- } else {
- tag = this._tags.pop();
- }
- if (tag.parentNode == null) {document.body.appendChild(tag);}
- return tag;
- };
-
- /**
- * Put an HTMLAudioElement back in the pool for use.
- * #method set
- * @param {HTMLAudioElement} tag HTML audio tag
- */
- p.set = function (tag) {
- // OJR this first step seems unnecessary
- var index = createjs.indexOf(this._tags, tag);
- if (index == -1) {
- this._tags.src = null;
- this._tags.push(tag);
- }
- };
-
- p.toString = function () {
- return "[TagPool]";
- };
-
-
-// Private Methods
- /**
- * Create an HTML audio tag.
- * #method _createTag
- * @param {String} src The source file to set for the audio tag.
- * @return {HTMLElement} Returns an HTML audio tag.
- * @protected
- */
- p._createTag = function () {
- var tag = document.createElement("audio");
- tag.autoplay = false;
- tag.preload = "none";
- //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
- return tag;
- };
-
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
+ * @class HTMLAudioTagPool
+ * @param {String} src The source of the channel.
+ * @protected
+ */
+ function HTMLAudioTagPool() {
+ throw "HTMLAudioTagPool cannot be instantiated";
+ }
+
+ var s = HTMLAudioTagPool;
+
+// Static Properties
+ /**
+ * A hash lookup of each base audio tag, indexed by the audio source.
+ * @property _tags
+ * @type {{}}
+ * @static
+ * @protected
+ */
+ s._tags = {};
+
+ /**
+ * An object pool for html audio tags
+ * @property _tagPool
+ * @type {TagPool}
+ * @static
+ * @protected
+ */
+ s._tagPool = new TagPool();
+
+ /**
+ * A hash lookup of if a base audio tag is available, indexed by the audio source
+ * @property _tagsUsed
+ * @type {{}}
+ * @protected
+ * @static
+ */
+ s._tagUsed = {};
+
+// Static Methods
+ /**
+ * Get an audio tag with the given source.
+ * @method get
+ * @param {String} src The source file used by the audio tag.
+ * @static
+ */
+ s.get = function (src) {
+ var t = s._tags[src];
+ if (t == null) {
+ // create new base tag
+ t = s._tags[src] = s._tagPool.get();
+ t.src = src;
+ } else {
+ // get base or pool
+ if (s._tagUsed[src]) {
+ t = s._tagPool.get();
+ t.src = src;
+ } else {
+ s._tagUsed[src] = true;
+ }
+ }
+ return t;
+ };
+
+ /**
+ * Return an audio tag to the pool.
+ * @method set
+ * @param {String} src The source file used by the audio tag.
+ * @param {HTMLElement} tag Audio tag to set.
+ * @static
+ */
+ s.set = function (src, tag) {
+ // check if this is base, if yes set boolean if not return to pool
+ if(tag == s._tags[src]) {
+ s._tagUsed[src] = false;
+ } else {
+ s._tagPool.set(tag);
+ }
+ };
+
+ /**
+ * Delete stored tag reference and return them to pool. Note that if the tag reference does not exist, this will fail.
+ * @method remove
+ * @param {String} src The source for the tag
+ * @return {Boolean} If the TagPool was deleted.
+ * @static
+ */
+ s.remove = function (src) {
+ var tag = s._tags[src];
+ if (tag == null) {return false;}
+ s._tagPool.set(tag);
+ delete(s._tags[src]);
+ delete(s._tagUsed[src]);
+ return true;
+ };
+
+ /**
+ * 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
+ * @static
+ */
+ s.getDuration= function (src) {
+ var t = s._tags[src];
+ if (t == null) {return 0;}
+ return t.duration * 1000;
+ };
+
+ createjs.HTMLAudioTagPool = HTMLAudioTagPool;
+
+
+// ************************************************************************************************************
+ /**
+ * The TagPool is an object pool for HTMLAudio tag instances.
+ * #class TagPool
+ * @param {String} src The source of the channel.
+ * @protected
+ */
+ function TagPool(src) {
+
+// Public Properties
+ /**
+ * A list of all available tags in the pool.
+ * #property tags
+ * @type {Array}
+ * @protected
+ */
+ this._tags = [];
+ };
+
+ var p = TagPool.prototype;
+ p.constructor = TagPool;
+
+
+// Public Methods
+ /**
+ * Get an HTMLAudioElement for immediate playback. This takes it out of the pool.
+ * #method get
+ * @return {HTMLAudioElement} An HTML audio tag.
+ */
+ p.get = function () {
+ var tag;
+ if (this._tags.length == 0) {
+ tag = this._createTag();
+ } else {
+ tag = this._tags.pop();
+ }
+ if (tag.parentNode == null) {document.body.appendChild(tag);}
+ return tag;
+ };
+
+ /**
+ * Put an HTMLAudioElement back in the pool for use.
+ * #method set
+ * @param {HTMLAudioElement} tag HTML audio tag
+ */
+ p.set = function (tag) {
+ // OJR this first step seems unnecessary
+ var index = createjs.indexOf(this._tags, tag);
+ if (index == -1) {
+ this._tags.src = null;
+ this._tags.push(tag);
+ }
+ };
+
+ p.toString = function () {
+ return "[TagPool]";
+ };
+
+
+// Private Methods
+ /**
+ * Create an HTML audio tag.
+ * #method _createTag
+ * @param {String} src The source file to set for the audio tag.
+ * @return {HTMLElement} Returns an HTML audio tag.
+ * @protected
+ */
+ p._createTag = function () {
+ var tag = document.createElement("audio");
+ tag.autoplay = false;
+ tag.preload = "none";
+ //LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
+ return tag;
+ };
+
}());
//##############################################################################
// HTMLAudioSoundInstance.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
- "use strict";
-
- /**
- * HTMLAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
- * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
- *
- * @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} playbackResource Any resource needed by plugin to support audio playback.
- * @class HTMLAudioSoundInstance
- * @extends AbstractSoundInstance
- * @constructor
- */
- function HTMLAudioSoundInstance(src, startTime, duration, playbackResource) {
- this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
-
-
-// Private Properties
- this._audioSpriteStopTime = null;
- this._delayTimeoutId = null;
-
- // Proxies, make removing listeners easier.
- this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
- this._readyHandler = createjs.proxy(this._handleTagReady, this);
- this._stalledHandler = createjs.proxy(this._playFailed, this);
- this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
- this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
-
- if (duration) {
- this._audioSpriteStopTime = (startTime + duration) * 0.001;
- } else {
- this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
- }
- }
- var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// Public Methods
- /**
- * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
- * undoc'd because it is not meant to be used outside of Sound
- * #method setMasterVolume
- * @param value
- */
- p.setMasterVolume = function (value) {
- this._updateVolume();
- };
-
- /**
- * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master mute.
- * undoc'd because it is not meant to be used outside of Sound
- * #method setMasterMute
- * @param value
- */
- p.setMasterMute = function (isMuted) {
- this._updateVolume();
- };
-
- p.toString = function () {
- return "[HTMLAudioSoundInstance]";
- };
-
-//Private Methods
- p._removeLooping = function() {
- if(this._playbackResource == null) {return;}
- this._playbackResource.loop = false;
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- };
-
- p._addLooping = function() {
- if(this._playbackResource == null || this._audioSpriteStopTime) {return;}
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- this._playbackResource.loop = true;
- };
-
- p._handleCleanUp = function () {
- var tag = this._playbackResource;
- if (tag != null) {
- tag.pause();
- 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_STALLED, this._stalledHandler, false);
- tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
-
- try {
- tag.currentTime = this._startTime;
- } catch (e) {
- } // Reset Position
- createjs.HTMLAudioTagPool.set(this.src, tag);
- this._playbackResource = null;
- }
- };
-
- p._beginPlaying = function (playProps) {
- this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
- return this.AbstractSoundInstance__beginPlaying(playProps);
- };
-
- p._handleSoundReady = function (event) {
- if (this._playbackResource.readyState !== 4) {
- var tag = this._playbackResource;
- tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
- tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
- tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set.
- tag.load();
- return;
- }
-
- this._updateVolume();
- this._playbackResource.currentTime = (this._startTime + this._position) * 0.001;
- if (this._audioSpriteStopTime) {
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
- } else {
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
- if(this._loop != 0) {
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- this._playbackResource.loop = true;
- }
- }
-
- this._playbackResource.play();
- };
-
- /**
- * Used to handle when a tag is not ready for immediate playback when it is returned from the HTMLAudioTagPool.
- * @method _handleTagReady
- * @param event
- * @protected
- */
- p._handleTagReady = function (event) {
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
-
- this._handleSoundReady();
- };
-
- p._pause = function () {
- this._playbackResource.pause();
- };
-
- p._resume = function () {
- this._playbackResource.play();
- };
-
- p._updateVolume = function () {
- if (this._playbackResource != null) {
- var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
- if (newVolume != this._playbackResource.volume) {this._playbackResource.volume = newVolume;}
- }
- };
-
- p._calculateCurrentPosition = function() {
- return (this._playbackResource.currentTime * 1000) - this._startTime;
- };
-
- p._updatePosition = function() {
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
- try {
- this._playbackResource.currentTime = (this._position + this._startTime) * 0.001;
- } catch (error) { // Out of range
- this._handleSetPositionSeek(null);
- }
- };
-
- /**
- * Used to enable setting position, as we need to wait for that seek to be done before we add back our loop handling seek listener
- * @method _handleSetPositionSeek
- * @param event
- * @protected
- */
- p._handleSetPositionSeek = function(event) {
- if (this._playbackResource == null) { return; }
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- };
-
- /**
- * Timer used to loop audio sprites.
- * 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
- *
- * @method _handleAudioSpriteLoop
- * @param event
- * @private
- */
- p._handleAudioSpriteLoop = function (event) {
- if(this._playbackResource.currentTime <= this._audioSpriteStopTime) {return;}
- this._playbackResource.pause();
- if(this._loop == 0) {
- this._handleSoundComplete(null);
- } else {
- this._position = 0;
- this._loop--;
- this._playbackResource.currentTime = this._startTime * 0.001;
- if(!this._paused) {this._playbackResource.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._handleLoop = function (event) {
- if(this._loop == 0) {
- this._playbackResource.loop = false;
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
- }
- };
-
- p._updateStartTime = function () {
- this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
-
- if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
- }
- };
-
- p._updateDuration = function () {
- this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
-
- if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
- this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
- }
- };
-
- /* This should never change
- p._setDurationFromSource = function () {
- this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
- };
- */
-
- createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * HTMLAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
+ * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
+ *
+ * @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} playbackResource Any resource needed by plugin to support audio playback.
+ * @class HTMLAudioSoundInstance
+ * @extends AbstractSoundInstance
+ * @constructor
+ */
+ function HTMLAudioSoundInstance(src, startTime, duration, playbackResource) {
+ this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
+
+
+// Private Properties
+ this._audioSpriteStopTime = null;
+ this._delayTimeoutId = null;
+
+ // Proxies, make removing listeners easier.
+ this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
+ this._readyHandler = createjs.proxy(this._handleTagReady, this);
+ this._stalledHandler = createjs.proxy(this._playFailed, this);
+ this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
+ this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
+
+ if (duration) {
+ this._audioSpriteStopTime = (startTime + duration) * 0.001;
+ } else {
+ this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
+ }
+ }
+ var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// Public Methods
+ /**
+ * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
+ * undoc'd because it is not meant to be used outside of Sound
+ * #method setMasterVolume
+ * @param value
+ */
+ p.setMasterVolume = function (value) {
+ this._updateVolume();
+ };
+
+ /**
+ * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master mute.
+ * undoc'd because it is not meant to be used outside of Sound
+ * #method setMasterMute
+ * @param value
+ */
+ p.setMasterMute = function (isMuted) {
+ this._updateVolume();
+ };
+
+ p.toString = function () {
+ return "[HTMLAudioSoundInstance]";
+ };
+
+//Private Methods
+ p._removeLooping = function() {
+ if(this._playbackResource == null) {return;}
+ this._playbackResource.loop = false;
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ };
+
+ p._addLooping = function() {
+ if(this._playbackResource == null || this._audioSpriteStopTime) {return;}
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ this._playbackResource.loop = true;
+ };
+
+ p._handleCleanUp = function () {
+ var tag = this._playbackResource;
+ if (tag != null) {
+ tag.pause();
+ 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_STALLED, this._stalledHandler, false);
+ tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
+
+ try {
+ tag.currentTime = this._startTime;
+ } catch (e) {
+ } // Reset Position
+ createjs.HTMLAudioTagPool.set(this.src, tag);
+ this._playbackResource = null;
+ }
+ };
+
+ p._beginPlaying = function (playProps) {
+ this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
+ return this.AbstractSoundInstance__beginPlaying(playProps);
+ };
+
+ p._handleSoundReady = function (event) {
+ if (this._playbackResource.readyState !== 4) {
+ var tag = this._playbackResource;
+ tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
+ tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
+ tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set.
+ tag.load();
+ return;
+ }
+
+ this._updateVolume();
+ this._playbackResource.currentTime = (this._startTime + this._position) * 0.001;
+ if (this._audioSpriteStopTime) {
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
+ } else {
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
+ if(this._loop != 0) {
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ this._playbackResource.loop = true;
+ }
+ }
+
+ this._playbackResource.play();
+ };
+
+ /**
+ * Used to handle when a tag is not ready for immediate playback when it is returned from the HTMLAudioTagPool.
+ * @method _handleTagReady
+ * @param event
+ * @protected
+ */
+ p._handleTagReady = function (event) {
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
+
+ this._handleSoundReady();
+ };
+
+ p._pause = function () {
+ this._playbackResource.pause();
+ };
+
+ p._resume = function () {
+ this._playbackResource.play();
+ };
+
+ p._updateVolume = function () {
+ if (this._playbackResource != null) {
+ var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
+ if (newVolume != this._playbackResource.volume) {this._playbackResource.volume = newVolume;}
+ }
+ };
+
+ p._calculateCurrentPosition = function() {
+ return (this._playbackResource.currentTime * 1000) - this._startTime;
+ };
+
+ p._updatePosition = function() {
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
+ try {
+ this._playbackResource.currentTime = (this._position + this._startTime) * 0.001;
+ } catch (error) { // Out of range
+ this._handleSetPositionSeek(null);
+ }
+ };
+
+ /**
+ * Used to enable setting position, as we need to wait for that seek to be done before we add back our loop handling seek listener
+ * @method _handleSetPositionSeek
+ * @param event
+ * @protected
+ */
+ p._handleSetPositionSeek = function(event) {
+ if (this._playbackResource == null) { return; }
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ };
+
+ /**
+ * Timer used to loop audio sprites.
+ * 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
+ *
+ * @method _handleAudioSpriteLoop
+ * @param event
+ * @private
+ */
+ p._handleAudioSpriteLoop = function (event) {
+ if(this._playbackResource.currentTime <= this._audioSpriteStopTime) {return;}
+ this._playbackResource.pause();
+ if(this._loop == 0) {
+ this._handleSoundComplete(null);
+ } else {
+ this._position = 0;
+ this._loop--;
+ this._playbackResource.currentTime = this._startTime * 0.001;
+ if(!this._paused) {this._playbackResource.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._handleLoop = function (event) {
+ if(this._loop == 0) {
+ this._playbackResource.loop = false;
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
+ }
+ };
+
+ p._updateStartTime = function () {
+ this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
+
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
+ }
+ };
+
+ p._updateDuration = function () {
+ this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
+
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
+ this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
+ }
+ };
+
+ /* This should never change
+ p._setDurationFromSource = function () {
+ this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
+ };
+ */
+
+ createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());
//##############################################################################
// HTMLAudioPlugin.js
//##############################################################################
-this.createjs = this.createjs || {};
-
-(function () {
-
- "use strict";
-
- /**
- * Play sounds using HTML <audio> tags in the browser. This plugin is the second priority plugin installed
- * by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. For older browsers that do not support html
- * audio, include and install the {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
- *
- *
Known Browser and OS issues for HTML Audio
- * All browsers
- * Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed
- * this limit, you can expect to see unpredictable results. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as
- * a guide to how many total audio tags you can safely use in all browsers. This issue is primarily limited to IE9.
- *
- * IE html limitations
- *
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.
- *
MP3 encoding will not always work for audio tags if it's not default. We've found default encoding with
- * 64kbps works.
- *
Occasionally very short samples will get cut off.
- *
There is a limit to how many audio tags you can load or play at once, which appears to be determined by
- * hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate.
- * Note that audio sprites can be used as a solution to this issue.
- *
- * Safari limitations
- *
Safari requires Quicktime to be installed for audio playback.
- *
- * iOS 6 limitations
- *
can only have one <audio> tag
- *
can not preload or autoplay the audio
- *
can not cache the audio
- *
can not play the audio except inside a user initiated event.
- *
Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)
- *
audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS
- *
- *
- * Android Native Browser limitations
- *
We have no control over audio volume. Only the user can set volume on their device.
- *
We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.
- * Android Chrome 26.0.1410.58 specific limitations
- *
Can only play 1 sound at a time.
- *
Sound is not cached.
- *
Sound can only be loaded in a user initiated touch/click event.
- *
There is a delay before a sound is played, presumably while the src is loaded.
- *
- *
- * See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues.
- *
- * @class HTMLAudioPlugin
- * @extends AbstractPlugin
- * @constructor
- */
- function HTMLAudioPlugin() {
- this.AbstractPlugin_constructor();
-
-
- // Public Properties
- /**
- * This is no longer needed as we are now using object pooling for tags.
- *
- * NOTE this property only exists as a limitation of HTML audio.
- * @property defaultNumChannels
- * @type {Number}
- * @default 2
- * @since 0.4.0
- * @deprecated
- */
- this.defaultNumChannels = 2;
-
- this._capabilities = s._capabilities;
-
- this._loaderClass = createjs.SoundLoader;
- this._soundInstanceClass = createjs.HTMLAudioSoundInstance;
- }
-
- var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
- var s = HTMLAudioPlugin;
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
-// Static Properties
- /**
- * The maximum number of instances that can be loaded or played. This is a browser limitation, primarily limited to IE9.
- * The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate.
- * Audio sprites work around this limitation.
- * @property MAX_INSTANCES
- * @type {Number}
- * @default 30
- * @static
- */
- s.MAX_INSTANCES = 30;
-
- /**
- * Event constant for the "canPlayThrough" event for cleaner code.
- * @property _AUDIO_READY
- * @type {String}
- * @default canplaythrough
- * @static
- * @protected
- */
- s._AUDIO_READY = "canplaythrough";
-
- /**
- * Event constant for the "ended" event for cleaner code.
- * @property _AUDIO_ENDED
- * @type {String}
- * @default ended
- * @static
- * @protected
- */
- s._AUDIO_ENDED = "ended";
-
- /**
- * Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events.
- * @property _AUDIO_SEEKED
- * @type {String}
- * @default seeked
- * @static
- * @protected
- */
- s._AUDIO_SEEKED = "seeked";
-
- /**
- * Event constant for the "stalled" event for cleaner code.
- * @property _AUDIO_STALLED
- * @type {String}
- * @default stalled
- * @static
- * @protected
- */
- s._AUDIO_STALLED = "stalled";
-
- /**
- * 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 {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}}
- * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all
- * of the available properties.
- * @property _capabilities
- * @type {Object}
- * @protected
- * @static
- */
- s._capabilities = null;
-
-
-// Static Methods
- /**
- * Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
- * browsers, but is disabled in iOS because of its limitations.
- * @method isSupported
- * @return {Boolean} If the plugin can be initialized.
- * @static
- */
- s.isSupported = function () {
- s._generateCapabilities();
- return (s._capabilities != null);
- };
-
- /**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
- * method for an overview of plugin capabilities.
- * @method _generateCapabilities
- * @static
- * @protected
- */
- s._generateCapabilities = function () {
- if (s._capabilities != null) {return;}
- var t = document.createElement("audio");
- if (t.canPlayType == null) {return null;}
-
- s._capabilities = {
- panning:false,
- volume:true,
- tracks:-1
- };
-
- // determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
- var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
- var extensionMap = createjs.Sound.EXTENSION_MAP;
- for (var i = 0, l = supportedExtensions.length; i < l; i++) {
- var ext = supportedExtensions[i];
- var playType = extensionMap[ext] || ext;
- s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
- } // OJR another way to do this might be canPlayType:"m4a", codex: mp4
- };
-
-
-// public methods
- p.register = function (loadItem) {
- var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
- var loader = this.AbstractPlugin_register(loadItem);
- loader.setTag(tag);
-
- return loader;
- };
-
- p.removeSound = function (src) {
- this.AbstractPlugin_removeSound(src);
- createjs.HTMLAudioTagPool.remove(src);
- };
-
- p.create = function (src, startTime, duration) {
- var si = this.AbstractPlugin_create(src, startTime, duration);
- si.setPlaybackResource(null);
- return si;
- };
-
- p.toString = function () {
- return "[HTMLAudioPlugin]";
- };
-
- // plugin does not support these
- p.setVolume = p.getVolume = p.setMute = null;
-
-
- createjs.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
+this.createjs = this.createjs || {};
+
+(function () {
+
+ "use strict";
+
+ /**
+ * Play sounds using HTML <audio> tags in the browser. This plugin is the second priority plugin installed
+ * by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. For older browsers that do not support html
+ * audio, include and install the {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
+ *
+ *
Known Browser and OS issues for HTML Audio
+ * All browsers
+ * Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed
+ * this limit, you can expect to see unpredictable results. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as
+ * a guide to how many total audio tags you can safely use in all browsers. This issue is primarily limited to IE9.
+ *
+ * IE html limitations
+ *
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.
+ *
MP3 encoding will not always work for audio tags if it's not default. We've found default encoding with
+ * 64kbps works.
+ *
Occasionally very short samples will get cut off.
+ *
There is a limit to how many audio tags you can load or play at once, which appears to be determined by
+ * hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate.
+ * Note that audio sprites can be used as a solution to this issue.
+ *
+ * Safari limitations
+ *
Safari requires Quicktime to be installed for audio playback.
+ *
+ * iOS 6 limitations
+ *
can only have one <audio> tag
+ *
can not preload or autoplay the audio
+ *
can not cache the audio
+ *
can not play the audio except inside a user initiated event.
+ *
Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)
+ *
audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS
+ *
+ *
+ * Android Native Browser limitations
+ *
We have no control over audio volume. Only the user can set volume on their device.
+ *
We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.
+ * Android Chrome 26.0.1410.58 specific limitations
+ *
Can only play 1 sound at a time.
+ *
Sound is not cached.
+ *
Sound can only be loaded in a user initiated touch/click event.
+ *
There is a delay before a sound is played, presumably while the src is loaded.
+ *
+ *
+ * See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues.
+ *
+ * @class HTMLAudioPlugin
+ * @extends AbstractPlugin
+ * @constructor
+ */
+ function HTMLAudioPlugin() {
+ this.AbstractPlugin_constructor();
+
+
+ // Public Properties
+ /**
+ * This is no longer needed as we are now using object pooling for tags.
+ *
+ * NOTE this property only exists as a limitation of HTML audio.
+ * @property defaultNumChannels
+ * @type {Number}
+ * @default 2
+ * @since 0.4.0
+ * @deprecated
+ */
+ this.defaultNumChannels = 2;
+
+ this._capabilities = s._capabilities;
+
+ this._loaderClass = createjs.SoundLoader;
+ this._soundInstanceClass = createjs.HTMLAudioSoundInstance;
+ }
+
+ var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
+ var s = HTMLAudioPlugin;
+
+ // TODO: deprecated
+ // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+
+
+// Static Properties
+ /**
+ * The maximum number of instances that can be loaded or played. This is a browser limitation, primarily limited to IE9.
+ * The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate.
+ * Audio sprites work around this limitation.
+ * @property MAX_INSTANCES
+ * @type {Number}
+ * @default 30
+ * @static
+ */
+ s.MAX_INSTANCES = 30;
+
+ /**
+ * Event constant for the "canPlayThrough" event for cleaner code.
+ * @property _AUDIO_READY
+ * @type {String}
+ * @default canplaythrough
+ * @static
+ * @protected
+ */
+ s._AUDIO_READY = "canplaythrough";
+
+ /**
+ * Event constant for the "ended" event for cleaner code.
+ * @property _AUDIO_ENDED
+ * @type {String}
+ * @default ended
+ * @static
+ * @protected
+ */
+ s._AUDIO_ENDED = "ended";
+
+ /**
+ * Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events.
+ * @property _AUDIO_SEEKED
+ * @type {String}
+ * @default seeked
+ * @static
+ * @protected
+ */
+ s._AUDIO_SEEKED = "seeked";
+
+ /**
+ * Event constant for the "stalled" event for cleaner code.
+ * @property _AUDIO_STALLED
+ * @type {String}
+ * @default stalled
+ * @static
+ * @protected
+ */
+ s._AUDIO_STALLED = "stalled";
+
+ /**
+ * 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 {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}}
+ * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all
+ * of the available properties.
+ * @property _capabilities
+ * @type {Object}
+ * @protected
+ * @static
+ */
+ s._capabilities = null;
+
+
+// Static Methods
+ /**
+ * Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
+ * browsers, but is disabled in iOS because of its limitations.
+ * @method isSupported
+ * @return {Boolean} If the plugin can be initialized.
+ * @static
+ */
+ s.isSupported = function () {
+ s._generateCapabilities();
+ return (s._capabilities != null);
+ };
+
+ /**
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * method for an overview of plugin capabilities.
+ * @method _generateCapabilities
+ * @static
+ * @protected
+ */
+ s._generateCapabilities = function () {
+ if (s._capabilities != null) {return;}
+ var t = document.createElement("audio");
+ if (t.canPlayType == null) {return null;}
+
+ s._capabilities = {
+ panning:false,
+ volume:true,
+ tracks:-1
+ };
+
+ // determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
+ var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
+ var extensionMap = createjs.Sound.EXTENSION_MAP;
+ for (var i = 0, l = supportedExtensions.length; i < l; i++) {
+ var ext = supportedExtensions[i];
+ var playType = extensionMap[ext] || ext;
+ s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
+ } // OJR another way to do this might be canPlayType:"m4a", codex: mp4
+ };
+
+
+// public methods
+ p.register = function (loadItem) {
+ var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
+ var loader = this.AbstractPlugin_register(loadItem);
+ loader.setTag(tag);
+
+ return loader;
+ };
+
+ p.removeSound = function (src) {
+ this.AbstractPlugin_removeSound(src);
+ createjs.HTMLAudioTagPool.remove(src);
+ };
+
+ p.create = function (src, startTime, duration) {
+ var si = this.AbstractPlugin_create(src, startTime, duration);
+ si.setPlaybackResource(null);
+ return si;
+ };
+
+ p.toString = function () {
+ return "[HTMLAudioPlugin]";
+ };
+
+ // plugin does not support these
+ p.setVolume = p.getVolume = p.setMute = null;
+
+
+ createjs.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
}());
\ No newline at end of file
diff --git a/lib/soundjs-NEXT.min.js b/lib/soundjs-NEXT.min.js
index c1459dfa..e6fc6b04 100644
--- a/lib/soundjs-NEXT.min.js
+++ b/lib/soundjs-NEXT.min.js
@@ -2,7 +2,7 @@
* @license SoundJS
* Visit http://createjs.com/ for documentation, updates and examples.
*
-* Copyright (c) 2011-2013 gskinner.com, inc.
+* Copyright (c) 2011-2015 gskinner.com, inc.
*
* Distributed under the terms of the MIT license.
* http://www.opensource.org/licenses/mit-license.html
@@ -14,5 +14,5 @@
* SoundJS FlashAudioPlugin also includes swfobject (http://code.google.com/p/swfobject/)
*/
-this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="NEXT",a.buildDate="Tue, 19 May 2015 17:26:59 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;d>c;c++)if(b===a[c])return c;return-1},this.createjs=this.createjs||{},function(){"use strict";createjs.proxy=function(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){return a.apply(b,Array.prototype.slice.call(arguments,0).concat(c))}}}(),this.createjs=this.createjs||{},function(){"use strict";function BrowserDetect(){throw"BrowserDetect cannot be instantiated"}var a=BrowserDetect.agent=window.navigator.userAgent;BrowserDetect.isWindowPhone=a.indexOf("IEMobile")>-1||a.indexOf("Windows Phone")>-1,BrowserDetect.isFirefox=a.indexOf("Firefox")>-1,BrowserDetect.isOpera=null!=window.opera,BrowserDetect.isChrome=a.indexOf("Chrome")>-1,BrowserDetect.isIOS=(a.indexOf("iPod")>-1||a.indexOf("iPhone")>-1||a.indexOf("iPad")>-1)&&!BrowserDetect.isWindowPhone,BrowserDetect.isAndroid=a.indexOf("Android")>-1&&!BrowserDetect.isWindowPhone,BrowserDetect.isBlackberry=a.indexOf("Blackberry")>-1,createjs.BrowserDetect=BrowserDetect}(),this.createjs=this.createjs||{},function(){"use strict";function EventDispatcher(){this._listeners=null,this._captureListeners=null}var a=EventDispatcher.prototype;EventDispatcher.initialize=function(b){b.addEventListener=a.addEventListener,b.on=a.on,b.removeEventListener=b.off=a.removeEventListener,b.removeAllEventListeners=a.removeAllEventListeners,b.hasEventListener=a.hasEventListener,b.dispatchEvent=a.dispatchEvent,b._dispatchEvent=a._dispatchEvent,b.willTrigger=a.willTrigger},a.addEventListener=function(a,b,c){var d;d=c?this._captureListeners=this._captureListeners||{}:this._listeners=this._listeners||{};var e=d[a];return e&&this.removeEventListener(a,b,c),e=d[a],e?e.push(b):d[a]=[b],b},a.on=function(a,b,c,d,e,f){return b.handleEvent&&(c=c||b,b=b.handleEvent),c=c||this,this.addEventListener(a,function(a){b.call(c,a,e),d&&a.remove()},f)},a.removeEventListener=function(a,b,c){var d=c?this._captureListeners:this._listeners;if(d){var e=d[a];if(e)for(var f=0,g=e.length;g>f;f++)if(e[f]==b){1==g?delete d[a]:e.splice(f,1);break}}},a.off=a.removeEventListener,a.removeAllEventListeners=function(a){a?(this._listeners&&delete this._listeners[a],this._captureListeners&&delete this._captureListeners[a]):this._listeners=this._captureListeners=null},a.dispatchEvent=function(a){if("string"==typeof a){var b=this._listeners;if(!b||!b[a])return!1;a=new createjs.Event(a)}else a.target&&a.clone&&(a=a.clone());try{a.target=this}catch(c){}if(a.bubbles&&this.parent){for(var d=this,e=[d];d.parent;)e.push(d=d.parent);var f,g=e.length;for(f=g-1;f>=0&&!a.propagationStopped;f--)e[f]._dispatchEvent(a,1+(0==f));for(f=1;g>f&&!a.propagationStopped;f++)e[f]._dispatchEvent(a,3)}else this._dispatchEvent(a,2);return a.defaultPrevented},a.hasEventListener=function(a){var b=this._listeners,c=this._captureListeners;return!!(b&&b[a]||c&&c[a])},a.willTrigger=function(a){for(var b=this;b;){if(b.hasEventListener(a))return!0;b=b.parent}return!1},a.toString=function(){return"[EventDispatcher]"},a._dispatchEvent=function(a,b){var c,d=1==b?this._captureListeners:this._listeners;if(a&&d){var e=d[a.type];if(!e||!(c=e.length))return;try{a.currentTarget=this}catch(f){}try{a.eventPhase=b}catch(f){}a.removed=!1,e=e.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=e[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}},createjs.EventDispatcher=EventDispatcher}(),this.createjs=this.createjs||{},function(){"use strict";function Event(a,b,c){this.type=a,this.target=null,this.currentTarget=null,this.eventPhase=0,this.bubbles=!!b,this.cancelable=!!c,this.timeStamp=(new Date).getTime(),this.defaultPrevented=!1,this.propagationStopped=!1,this.immediatePropagationStopped=!1,this.removed=!1}var a=Event.prototype;a.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},a.stopPropagation=function(){this.propagationStopped=!0},a.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},a.remove=function(){this.removed=!0},a.clone=function(){return new Event(this.type,this.bubbles,this.cancelable)},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=Event}(),this.createjs=this.createjs||{},function(){"use strict";function ErrorEvent(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var a=createjs.extend(ErrorEvent,createjs.Event);a.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(ErrorEvent,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function ProgressEvent(a,b){this.Event_constructor("progress"),this.loaded=a,this.total=null==b?1:b,this.progress=0==b?0:this.loaded/this.total}var a=createjs.extend(ProgressEvent,createjs.Event);a.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(ProgressEvent,"Event")}(window),this.createjs=this.createjs||{},function(){"use strict";function LoadItem(){this.src=null,this.type=null,this.id=null,this.maintainOrder=!1,this.callback=null,this.data=null,this.method=createjs.LoadItem.GET,this.values=null,this.headers=null,this.withCredentials=!1,this.mimeType=null,this.crossOrigin=null,this.loadTimeout=b.LOAD_TIMEOUT_DEFAULT}var a=LoadItem.prototype={},b=LoadItem;b.LOAD_TIMEOUT_DEFAULT=8e3,b.create=function(a){if("string"==typeof a){var c=new LoadItem;return c.src=a,c}if(a instanceof b)return a;if(a instanceof Object&&a.src)return null==a.loadTimeout&&(a.loadTimeout=b.LOAD_TIMEOUT_DEFAULT),a;throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=b}(),function(){var a={};a.ABSOLUTE_PATT=/^(?:\w+:)?\/{2}/i,a.RELATIVE_PATT=/^[./]*?\//i,a.EXTENSION_PATT=/\/?[^/]+\.(\w{1,5})$/i,a.parseURI=function(b){var c={absolute:!1,relative:!1};if(null==b)return c;var d=b.indexOf("?");d>-1&&(b=b.substr(0,d));var e;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(e=b.match(a.EXTENSION_PATT))&&(c.extension=e[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildPath=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this._formatQueryString(b,c):a+"?"+this._formatQueryString(b,c)},a.isCrossDomain=function(a){var b=document.createElement("a");b.href=a.src;var c=document.createElement("a");c.href=location.href;var d=""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname);return d},a.isLocal=function(a){var b=document.createElement("a");return b.href=a.src,""==b.hostname&&"file:"==b.protocol},a.isBinary=function(a){switch(a){case createjs.AbstractLoader.IMAGE:case createjs.AbstractLoader.BINARY:return!0;default:return!1}},a.isImageTag=function(a){return a instanceof HTMLImageElement},a.isAudioTag=function(a){return window.HTMLAudioElement?a instanceof HTMLAudioElement:!1},a.isVideoTag=function(a){return window.HTMLVideoElement?a instanceof HTMLVideoElement:!1},a.isText=function(a){switch(a){case createjs.AbstractLoader.TEXT:case createjs.AbstractLoader.JSON:case createjs.AbstractLoader.MANIFEST:case createjs.AbstractLoader.XML:case createjs.AbstractLoader.CSS:case createjs.AbstractLoader.SVG:case createjs.AbstractLoader.JAVASCRIPT:case createjs.AbstractLoader.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.AbstractLoader.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.AbstractLoader.IMAGE;case"ogg":case"mp3":case"webm":return createjs.AbstractLoader.SOUND;case"mp4":case"webm":case"ts":return createjs.AbstractLoader.VIDEO;case"json":return createjs.AbstractLoader.JSON;case"xml":return createjs.AbstractLoader.XML;case"css":return createjs.AbstractLoader.CSS;case"js":return createjs.AbstractLoader.JAVASCRIPT;case"svg":return createjs.AbstractLoader.SVG;default:return createjs.AbstractLoader.TEXT}},createjs.RequestUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;b.POST="POST",b.GET="GET",b.BINARY="binary",b.CSS="css",b.IMAGE="image",b.JAVASCRIPT="javascript",b.JSON="json",b.JSONP="jsonp",b.MANIFEST="manifest",b.SOUND="sound",b.VIDEO="video",b.SPRITESHEET="spritesheet",b.SVG="svg",b.TEXT="text",b.XML="xml",a.getItem=function(){return this._item},a.getResult=function(a){return a?this._rawResult:this._result},a.getTag=function(){return this._tag},a.setTag=function(a){this._tag=a},a.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},a.cancel=function(){this.canceled=!0,this.destroy()},a.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},a.getLoadedItems=function(){return this._loadedItems},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._createTag=function(){return null},a._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},a._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||1/0==this.progress)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},a._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},a._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},a._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},a.resultFormatter=null,a.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this),c=this;b instanceof Function?b(function(a){c._result=a,c._sendComplete()}):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(a.type)}},a.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src"}var a=createjs.extend(AbstractMediaLoader,createjs.AbstractLoader);a.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},a._createTag=function(){},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._formatResult=function(a){return this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR&&(a.getTag().src=a.getResult(!0)),a.getTag()},createjs.AbstractMediaLoader=createjs.promote(AbstractMediaLoader,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractRequest=function(a){this._item=a},a=createjs.extend(AbstractRequest,createjs.EventDispatcher);a.load=function(){},a.destroy=function(){},a.cancel=function(){},createjs.AbstractRequest=createjs.promote(AbstractRequest,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function TagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1,this._startTagVisibility=null}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},a._showTag=function(){this._tag.style.visibility=this._startTagVisibility},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values&&this._item.method!=createjs.AbstractLoader.GET?this._item.method==createjs.AbstractLoader.POST&&this._request.send(createjs.RequestUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){this._request.responseType=a},a.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},a.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._handleLoadStart=function(){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},a._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},a._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},a._handleReadyStateChange=function(){4==this._request.readyState&&this._handleLoad()},a._handleLoad=function(){if(!this.loaded){this.loaded=!0;var a=this._checkError();if(a)return void this._handleError(a);this._response=this._getResponse(),this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},a._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},a._checkError=function(){var a=parseInt(this._request.status);switch(a){case 404:case 0:return new Error(a)}return null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.RequestUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){{s.ACTIVEX_VERSIONS[e]}try{d=new ActiveXObject(axVersions);break}catch(g){}}if(null==d)return!1}null==a.mimeType&&createjs.RequestUtils.isText(a.type)&&(a.mimeType="text/plain; charset=utf-8"),a.mimeType&&d.overrideMimeType&&d.overrideMimeType(a.mimeType),this._xhrLevel="string"==typeof d.responseType?2:1;var h=null;if(h=a.method==createjs.AbstractLoader.GET?createjs.RequestUtils.buildPath(a.src,a.values):a.src,d.open(a.method||createjs.AbstractLoader.GET,h,!0),b&&d instanceof XMLHttpRequest&&1==this._xhrLevel&&(c.Origin=location.origin),a.values&&a.method==createjs.AbstractLoader.POST&&(c["Content-Type"]="application/x-www-form-urlencoded"),b||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest"),a.headers)for(var i in a.headers)c[i]=a.headers[i];for(i in c)d.setRequestHeader(i,c[i]);return d instanceof XMLHttpRequest&&void 0!==a.withCredentials&&(d.withCredentials=a.withCredentials),this._request=d,!0},a._clean=function(){clearTimeout(this._loadTimeout),this._request.removeEventListener("loadstart",this._handleLoadStartProxy),this._request.removeEventListener("progress",this._handleProgressProxy),this._request.removeEventListener("abort",this._handleAbortProxy),this._request.removeEventListener("error",this._handleErrorProxy),this._request.removeEventListener("timeout",this._handleTimeoutProxy),this._request.removeEventListener("load",this._handleLoadProxy),this._request.removeEventListener("readystatechange",this._handleReadyStateChangeProxy)},a.toString=function(){return"[PreloadJS XHRRequest]"},createjs.XHRRequest=createjs.promote(XHRRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function SoundLoader(a,b){this.AbstractMediaLoader_constructor(a,b,createjs.AbstractLoader.SOUND),createjs.RequestUtils.isAudioTag(a)?this._tag=a:createjs.RequestUtils.isAudioTag(a.src)?this._tag=a:createjs.RequestUtils.isAudioTag(a.tag)&&(this._tag=createjs.RequestUtils.isAudioTag(a)?a:a.src),null!=this._tag&&(this._preferXHR=!1)}var a=createjs.extend(SoundLoader,createjs.AbstractMediaLoader),b=SoundLoader;b.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},a._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(SoundLoader,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var PlayPropsConfig=function(){this.interrupt=null,this.delay=null,this.offset=null,this.loop=null,this.volume=null,this.pan=null,this.startTime=null,this.duration=null},a=PlayPropsConfig.prototype={},b=PlayPropsConfig;b.create=function(a){if(a instanceof b||a instanceof Object){var c=new createjs.PlayPropsConfig;return c.set(a),c}throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[PlayPropsConfig]"},createjs.PlayPropsConfig=b}(),this.createjs=this.createjs||{},function(){"use strict";function Sound(){throw"Sound cannot be instantiated"}function a(a,b){this.init(a,b)}var b=Sound;b.INTERRUPT_ANY="any",b.INTERRUPT_EARLY="early",b.INTERRUPT_LATE="late",b.INTERRUPT_NONE="none",b.PLAY_INITED="playInited",b.PLAY_SUCCEEDED="playSucceeded",b.PLAY_INTERRUPTED="playInterrupted",b.PLAY_FINISHED="playFinished",b.PLAY_FAILED="playFailed",b.SUPPORTED_EXTENSIONS=["mp3","ogg","opus","mpeg","wav","m4a","mp4","aiff","wma","mid"],b.EXTENSION_MAP={m4a:"mp4"},b.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,b.defaultInterruptBehavior=b.INTERRUPT_NONE,b.alternateExtensions=[],b.activePlugin=null,b._masterVolume=1,Object.defineProperty(b,"volume",{get:function(){return this._masterVolume},set:function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)}}),b._masterMute=!1,Object.defineProperty(b,"muted",{get:function(){return this._masterMute},set:function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0}}),Object.defineProperty(b,"capabilities",{get:function(){return null==b.activePlugin?null:b.activePlugin._capabilities},set:function(){return!1}}),b._pluginsRegistered=!1,b._lastID=0,b._instances=[],b._idHash={},b._preloadHash={},b._defaultPlayPropsHash={},b.addEventListener=null,b.removeEventListener=null,b.removeAllEventListeners=null,b.dispatchEvent=null,b.hasEventListener=null,b._listeners=null,createjs.EventDispatcher.initialize(b),b.getPreloadHandlers=function(){return{callback:createjs.proxy(b.initLoad,b),types:["sound"],extensions:b.SUPPORTED_EXTENSIONS}},b._handleLoadComplete=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!0,b.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._handleLoadError=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!1,b.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._registerPlugin=function(a){return a.isSupported()?(b.activePlugin=new a,!0):!1},b.registerPlugins=function(a){b._pluginsRegistered=!0;for(var c=0,d=a.length;d>c;c++)if(b._registerPlugin(a[c]))return!0;return!1},b.initializeDefaultPlugins=function(){return null!=b.activePlugin?!0:b._pluginsRegistered?!1:b.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},b.isReady=function(){return null!=b.activePlugin},b.getCapabilities=function(){return null==b.activePlugin?null:b.activePlugin._capabilities},b.getCapability=function(a){return null==b.activePlugin?null:b.activePlugin._capabilities[a]},b.initLoad=function(a){return b._registerSound(a)},b._registerSound=function(c){if(!b.initializeDefaultPlugins())return!1;var d;if(c.src instanceof Object?(d=b._parseSrc(c.src),d.src=c.path+d.src):d=b._parsePath(c.src),null==d)return!1;c.src=d.src,c.type="sound";var e=c.data,f=null;if(null!=e&&(isNaN(e.channels)?isNaN(e)||(f=parseInt(e)):f=parseInt(e.channels),e.audioSprite))for(var g,h=e.audioSprite.length;h--;)g=e.audioSprite[h],b._idHash[g.id]={src:c.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)},g.defaultPlayProps&&(b._defaultPlayPropsHash[g.id]=createjs.PlayPropsConfig.create(g.defaultPlayProps));null!=c.id&&(b._idHash[c.id]={src:c.src});var i=b.activePlugin.register(c);return a.create(c.src,f),null!=e&&isNaN(e)?c.data.channels=f||a.maxPerChannel():c.data=f||a.maxPerChannel(),i.type&&(c.type=i.type),c.defaultPlayProps&&(b._defaultPlayPropsHash[c.src]=createjs.PlayPropsConfig.create(c.defaultPlayProps)),i},b.registerSound=function(a,c,d,e,f){var g={src:a,id:c,data:d,defaultPlayProps:f};a instanceof Object&&a.src&&(e=c,g=a),g=createjs.LoadItem.create(g),g.path=e,null==e||g.src instanceof Object||(g.src=e+a);var h=b._registerSound(g);if(!h)return!1;if(b._preloadHash[g.src]||(b._preloadHash[g.src]=[]),b._preloadHash[g.src].push(g),1==b._preloadHash[g.src].length)h.on("complete",createjs.proxy(this._handleLoadComplete,this)),h.on("error",createjs.proxy(this._handleLoadError,this)),b.activePlugin.preload(h);else if(1==b._preloadHash[g.src][0])return!0;return g},b.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.registerSound(a[d].src,a[d].id,a[d].data,b,a[d].defaultPlayProps);return c},b.removeSound=function(c,d){if(null==b.activePlugin)return!1;c instanceof Object&&c.src&&(c=c.src);var e;if(c instanceof Object?e=b._parseSrc(c):(c=b._getSrcById(c).src,e=b._parsePath(c)),null==e)return!1;c=e.src,null!=d&&(c=d+c);for(var f in b._idHash)b._idHash[f].src==c&&delete b._idHash[f];return a.removeSrc(c),delete b._preloadHash[c],b.activePlugin.removeSound(c),!0},b.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},b.removeAllSounds=function(){b._idHash={},b._preloadHash={},a.removeAll(),b.activePlugin&&b.activePlugin.removeAllSounds()},b.loadComplete=function(a){if(!b.isReady())return!1;var c=b._parsePath(a);return a=c?b._getSrcById(c.src).src:b._getSrcById(a).src,void 0==b._preloadHash[a]?!1:1==b._preloadHash[a][0]},b._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var c=a.match(b.FILE_PATTERN);if(null==c)return!1;for(var d=c[4],e=c[5],f=b.capabilities,g=0;!f[e];)if(e=b.alternateExtensions[g++],g>b.alternateExtensions.length)return null;a=a.replace("."+c[5],"."+e);var h={name:d,src:a,extension:e};return h},b._parseSrc=function(a){var c={name:void 0,src:void 0,extension:void 0},d=b.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){c.src=a[e],c.extension=e;break}if(!c.src)return!1;var f=c.src.lastIndexOf("/");return c.name=-1!=f?c.src.slice(f+1):c.src,c},b.play=function(a,c,d,e,f,g,h,i,j){var k;k=createjs.PlayPropsConfig.create(c instanceof Object||c instanceof createjs.PlayPropsConfig?c:{interrupt:c,delay:d,offset:e,loop:f,volume:g,pan:h,startTime:i,duration:j});var l=b.createInstance(a,k.startTime,k.duration),m=b._playInstance(l,k);return m||l._playFailed(),l},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),f=f||b._defaultPlayPropsHash[g.src],f&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(c,d,e),h.uniqueId=b._lastID++,h},b.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},b.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)},b.getVolume=function(){return this._masterVolume},b.setMute=function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0},b.getMute=function(){return this._masterMute},b.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.getPosition()),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){var e=b._beginPlaying(a,c);
+this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="NEXT",a.buildDate="Wed, 27 May 2015 18:12:38 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;d>c;c++)if(b===a[c])return c;return-1},this.createjs=this.createjs||{},function(){"use strict";createjs.proxy=function(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){return a.apply(b,Array.prototype.slice.call(arguments,0).concat(c))}}}(),this.createjs=this.createjs||{},function(){"use strict";function BrowserDetect(){throw"BrowserDetect cannot be instantiated"}var a=BrowserDetect.agent=window.navigator.userAgent;BrowserDetect.isWindowPhone=a.indexOf("IEMobile")>-1||a.indexOf("Windows Phone")>-1,BrowserDetect.isFirefox=a.indexOf("Firefox")>-1,BrowserDetect.isOpera=null!=window.opera,BrowserDetect.isChrome=a.indexOf("Chrome")>-1,BrowserDetect.isIOS=(a.indexOf("iPod")>-1||a.indexOf("iPhone")>-1||a.indexOf("iPad")>-1)&&!BrowserDetect.isWindowPhone,BrowserDetect.isAndroid=a.indexOf("Android")>-1&&!BrowserDetect.isWindowPhone,BrowserDetect.isBlackberry=a.indexOf("Blackberry")>-1,createjs.BrowserDetect=BrowserDetect}(),this.createjs=this.createjs||{},function(){"use strict";function EventDispatcher(){this._listeners=null,this._captureListeners=null}var a=EventDispatcher.prototype;EventDispatcher.initialize=function(b){b.addEventListener=a.addEventListener,b.on=a.on,b.removeEventListener=b.off=a.removeEventListener,b.removeAllEventListeners=a.removeAllEventListeners,b.hasEventListener=a.hasEventListener,b.dispatchEvent=a.dispatchEvent,b._dispatchEvent=a._dispatchEvent,b.willTrigger=a.willTrigger},a.addEventListener=function(a,b,c){var d;d=c?this._captureListeners=this._captureListeners||{}:this._listeners=this._listeners||{};var e=d[a];return e&&this.removeEventListener(a,b,c),e=d[a],e?e.push(b):d[a]=[b],b},a.on=function(a,b,c,d,e,f){return b.handleEvent&&(c=c||b,b=b.handleEvent),c=c||this,this.addEventListener(a,function(a){b.call(c,a,e),d&&a.remove()},f)},a.removeEventListener=function(a,b,c){var d=c?this._captureListeners:this._listeners;if(d){var e=d[a];if(e)for(var f=0,g=e.length;g>f;f++)if(e[f]==b){1==g?delete d[a]:e.splice(f,1);break}}},a.off=a.removeEventListener,a.removeAllEventListeners=function(a){a?(this._listeners&&delete this._listeners[a],this._captureListeners&&delete this._captureListeners[a]):this._listeners=this._captureListeners=null},a.dispatchEvent=function(a){if("string"==typeof a){var b=this._listeners;if(!b||!b[a])return!1;a=new createjs.Event(a)}else a.target&&a.clone&&(a=a.clone());try{a.target=this}catch(c){}if(a.bubbles&&this.parent){for(var d=this,e=[d];d.parent;)e.push(d=d.parent);var f,g=e.length;for(f=g-1;f>=0&&!a.propagationStopped;f--)e[f]._dispatchEvent(a,1+(0==f));for(f=1;g>f&&!a.propagationStopped;f++)e[f]._dispatchEvent(a,3)}else this._dispatchEvent(a,2);return a.defaultPrevented},a.hasEventListener=function(a){var b=this._listeners,c=this._captureListeners;return!!(b&&b[a]||c&&c[a])},a.willTrigger=function(a){for(var b=this;b;){if(b.hasEventListener(a))return!0;b=b.parent}return!1},a.toString=function(){return"[EventDispatcher]"},a._dispatchEvent=function(a,b){var c,d=1==b?this._captureListeners:this._listeners;if(a&&d){var e=d[a.type];if(!e||!(c=e.length))return;try{a.currentTarget=this}catch(f){}try{a.eventPhase=b}catch(f){}a.removed=!1,e=e.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=e[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}},createjs.EventDispatcher=EventDispatcher}(),this.createjs=this.createjs||{},function(){"use strict";function Event(a,b,c){this.type=a,this.target=null,this.currentTarget=null,this.eventPhase=0,this.bubbles=!!b,this.cancelable=!!c,this.timeStamp=(new Date).getTime(),this.defaultPrevented=!1,this.propagationStopped=!1,this.immediatePropagationStopped=!1,this.removed=!1}var a=Event.prototype;a.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},a.stopPropagation=function(){this.propagationStopped=!0},a.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},a.remove=function(){this.removed=!0},a.clone=function(){return new Event(this.type,this.bubbles,this.cancelable)},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=Event}(),this.createjs=this.createjs||{},function(){"use strict";function ErrorEvent(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var a=createjs.extend(ErrorEvent,createjs.Event);a.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(ErrorEvent,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function ProgressEvent(a,b){this.Event_constructor("progress"),this.loaded=a,this.total=null==b?1:b,this.progress=0==b?0:this.loaded/this.total}var a=createjs.extend(ProgressEvent,createjs.Event);a.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(ProgressEvent,"Event")}(window),this.createjs=this.createjs||{},function(){"use strict";function LoadItem(){this.src=null,this.type=null,this.id=null,this.maintainOrder=!1,this.callback=null,this.data=null,this.method=createjs.LoadItem.GET,this.values=null,this.headers=null,this.withCredentials=!1,this.mimeType=null,this.crossOrigin=null,this.loadTimeout=b.LOAD_TIMEOUT_DEFAULT}var a=LoadItem.prototype={},b=LoadItem;b.LOAD_TIMEOUT_DEFAULT=8e3,b.create=function(a){if("string"==typeof a){var c=new LoadItem;return c.src=a,c}if(a instanceof b)return a;if(a instanceof Object&&a.src)return null==a.loadTimeout&&(a.loadTimeout=b.LOAD_TIMEOUT_DEFAULT),a;throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=b}(),function(){var a={};a.ABSOLUTE_PATT=/^(?:\w+:)?\/{2}/i,a.RELATIVE_PATT=/^[./]*?\//i,a.EXTENSION_PATT=/\/?[^/]+\.(\w{1,5})$/i,a.parseURI=function(b){var c={absolute:!1,relative:!1};if(null==b)return c;var d=b.indexOf("?");d>-1&&(b=b.substr(0,d));var e;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(e=b.match(a.EXTENSION_PATT))&&(c.extension=e[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildPath=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this._formatQueryString(b,c):a+"?"+this._formatQueryString(b,c)},a.isCrossDomain=function(a){var b=document.createElement("a");b.href=a.src;var c=document.createElement("a");c.href=location.href;var d=""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname);return d},a.isLocal=function(a){var b=document.createElement("a");return b.href=a.src,""==b.hostname&&"file:"==b.protocol},a.isBinary=function(a){switch(a){case createjs.AbstractLoader.IMAGE:case createjs.AbstractLoader.BINARY:return!0;default:return!1}},a.isImageTag=function(a){return a instanceof HTMLImageElement},a.isAudioTag=function(a){return window.HTMLAudioElement?a instanceof HTMLAudioElement:!1},a.isVideoTag=function(a){return window.HTMLVideoElement?a instanceof HTMLVideoElement:!1},a.isText=function(a){switch(a){case createjs.AbstractLoader.TEXT:case createjs.AbstractLoader.JSON:case createjs.AbstractLoader.MANIFEST:case createjs.AbstractLoader.XML:case createjs.AbstractLoader.CSS:case createjs.AbstractLoader.SVG:case createjs.AbstractLoader.JAVASCRIPT:case createjs.AbstractLoader.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.AbstractLoader.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.AbstractLoader.IMAGE;case"ogg":case"mp3":case"webm":return createjs.AbstractLoader.SOUND;case"mp4":case"webm":case"ts":return createjs.AbstractLoader.VIDEO;case"json":return createjs.AbstractLoader.JSON;case"xml":return createjs.AbstractLoader.XML;case"css":return createjs.AbstractLoader.CSS;case"js":return createjs.AbstractLoader.JAVASCRIPT;case"svg":return createjs.AbstractLoader.SVG;default:return createjs.AbstractLoader.TEXT}},createjs.RequestUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;b.POST="POST",b.GET="GET",b.BINARY="binary",b.CSS="css",b.IMAGE="image",b.JAVASCRIPT="javascript",b.JSON="json",b.JSONP="jsonp",b.MANIFEST="manifest",b.SOUND="sound",b.VIDEO="video",b.SPRITESHEET="spritesheet",b.SVG="svg",b.TEXT="text",b.XML="xml",a.getItem=function(){return this._item},a.getResult=function(a){return a?this._rawResult:this._result},a.getTag=function(){return this._tag},a.setTag=function(a){this._tag=a},a.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},a.cancel=function(){this.canceled=!0,this.destroy()},a.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},a.getLoadedItems=function(){return this._loadedItems},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._createTag=function(){return null},a._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},a._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||1/0==this.progress)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},a._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},a._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},a._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},a.resultFormatter=null,a.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this),c=this;b instanceof Function?b(function(a){c._result=a,c._sendComplete()}):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(a.type)}},a.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src"}var a=createjs.extend(AbstractMediaLoader,createjs.AbstractLoader);a.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},a._createTag=function(){},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._formatResult=function(a){return this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR&&(a.getTag().src=a.getResult(!0)),a.getTag()},createjs.AbstractMediaLoader=createjs.promote(AbstractMediaLoader,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractRequest=function(a){this._item=a},a=createjs.extend(AbstractRequest,createjs.EventDispatcher);a.load=function(){},a.destroy=function(){},a.cancel=function(){},createjs.AbstractRequest=createjs.promote(AbstractRequest,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function TagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1,this._startTagVisibility=null}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},a._showTag=function(){this._tag.style.visibility=this._startTagVisibility},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values&&this._item.method!=createjs.AbstractLoader.GET?this._item.method==createjs.AbstractLoader.POST&&this._request.send(createjs.RequestUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){this._request.responseType=a},a.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},a.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._handleLoadStart=function(){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},a._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},a._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},a._handleReadyStateChange=function(){4==this._request.readyState&&this._handleLoad()},a._handleLoad=function(){if(!this.loaded){this.loaded=!0;var a=this._checkError();if(a)return void this._handleError(a);this._response=this._getResponse(),this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},a._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},a._checkError=function(){var a=parseInt(this._request.status);switch(a){case 404:case 0:return new Error(a)}return null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.RequestUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){{s.ACTIVEX_VERSIONS[e]}try{d=new ActiveXObject(axVersions);break}catch(g){}}if(null==d)return!1}null==a.mimeType&&createjs.RequestUtils.isText(a.type)&&(a.mimeType="text/plain; charset=utf-8"),a.mimeType&&d.overrideMimeType&&d.overrideMimeType(a.mimeType),this._xhrLevel="string"==typeof d.responseType?2:1;var h=null;if(h=a.method==createjs.AbstractLoader.GET?createjs.RequestUtils.buildPath(a.src,a.values):a.src,d.open(a.method||createjs.AbstractLoader.GET,h,!0),b&&d instanceof XMLHttpRequest&&1==this._xhrLevel&&(c.Origin=location.origin),a.values&&a.method==createjs.AbstractLoader.POST&&(c["Content-Type"]="application/x-www-form-urlencoded"),b||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest"),a.headers)for(var i in a.headers)c[i]=a.headers[i];for(i in c)d.setRequestHeader(i,c[i]);return d instanceof XMLHttpRequest&&void 0!==a.withCredentials&&(d.withCredentials=a.withCredentials),this._request=d,!0},a._clean=function(){clearTimeout(this._loadTimeout),this._request.removeEventListener("loadstart",this._handleLoadStartProxy),this._request.removeEventListener("progress",this._handleProgressProxy),this._request.removeEventListener("abort",this._handleAbortProxy),this._request.removeEventListener("error",this._handleErrorProxy),this._request.removeEventListener("timeout",this._handleTimeoutProxy),this._request.removeEventListener("load",this._handleLoadProxy),this._request.removeEventListener("readystatechange",this._handleReadyStateChangeProxy)},a.toString=function(){return"[PreloadJS XHRRequest]"},createjs.XHRRequest=createjs.promote(XHRRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function SoundLoader(a,b){this.AbstractMediaLoader_constructor(a,b,createjs.AbstractLoader.SOUND),createjs.RequestUtils.isAudioTag(a)?this._tag=a:createjs.RequestUtils.isAudioTag(a.src)?this._tag=a:createjs.RequestUtils.isAudioTag(a.tag)&&(this._tag=createjs.RequestUtils.isAudioTag(a)?a:a.src),null!=this._tag&&(this._preferXHR=!1)}var a=createjs.extend(SoundLoader,createjs.AbstractMediaLoader),b=SoundLoader;b.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},a._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(SoundLoader,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var PlayPropsConfig=function(){this.interrupt=null,this.delay=null,this.offset=null,this.loop=null,this.volume=null,this.pan=null,this.startTime=null,this.duration=null},a=PlayPropsConfig.prototype={},b=PlayPropsConfig;b.create=function(a){if(a instanceof b||a instanceof Object){var c=new createjs.PlayPropsConfig;return c.set(a),c}throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[PlayPropsConfig]"},createjs.PlayPropsConfig=b}(),this.createjs=this.createjs||{},function(){"use strict";function Sound(){throw"Sound cannot be instantiated"}function a(a,b){this.init(a,b)}var b=Sound;b.INTERRUPT_ANY="any",b.INTERRUPT_EARLY="early",b.INTERRUPT_LATE="late",b.INTERRUPT_NONE="none",b.PLAY_INITED="playInited",b.PLAY_SUCCEEDED="playSucceeded",b.PLAY_INTERRUPTED="playInterrupted",b.PLAY_FINISHED="playFinished",b.PLAY_FAILED="playFailed",b.SUPPORTED_EXTENSIONS=["mp3","ogg","opus","mpeg","wav","m4a","mp4","aiff","wma","mid"],b.EXTENSION_MAP={m4a:"mp4"},b.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,b.defaultInterruptBehavior=b.INTERRUPT_NONE,b.alternateExtensions=[],b.activePlugin=null,b._masterVolume=1,Object.defineProperty(b,"volume",{get:function(){return this._masterVolume},set:function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)}}),b._masterMute=!1,Object.defineProperty(b,"muted",{get:function(){return this._masterMute},set:function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0}}),Object.defineProperty(b,"capabilities",{get:function(){return null==b.activePlugin?null:b.activePlugin._capabilities},set:function(){return!1}}),b._pluginsRegistered=!1,b._lastID=0,b._instances=[],b._idHash={},b._preloadHash={},b._defaultPlayPropsHash={},b.addEventListener=null,b.removeEventListener=null,b.removeAllEventListeners=null,b.dispatchEvent=null,b.hasEventListener=null,b._listeners=null,createjs.EventDispatcher.initialize(b),b.getPreloadHandlers=function(){return{callback:createjs.proxy(b.initLoad,b),types:["sound"],extensions:b.SUPPORTED_EXTENSIONS}},b._handleLoadComplete=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!0,b.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._handleLoadError=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!1,b.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._registerPlugin=function(a){return a.isSupported()?(b.activePlugin=new a,!0):!1},b.registerPlugins=function(a){b._pluginsRegistered=!0;for(var c=0,d=a.length;d>c;c++)if(b._registerPlugin(a[c]))return!0;return!1},b.initializeDefaultPlugins=function(){return null!=b.activePlugin?!0:b._pluginsRegistered?!1:b.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},b.isReady=function(){return null!=b.activePlugin},b.getCapabilities=function(){return null==b.activePlugin?null:b.activePlugin._capabilities},b.getCapability=function(a){return null==b.activePlugin?null:b.activePlugin._capabilities[a]},b.initLoad=function(a){return b._registerSound(a)},b._registerSound=function(c){if(!b.initializeDefaultPlugins())return!1;var d;if(c.src instanceof Object?(d=b._parseSrc(c.src),d.src=c.path+d.src):d=b._parsePath(c.src),null==d)return!1;c.src=d.src,c.type="sound";var e=c.data,f=null;if(null!=e&&(isNaN(e.channels)?isNaN(e)||(f=parseInt(e)):f=parseInt(e.channels),e.audioSprite))for(var g,h=e.audioSprite.length;h--;)g=e.audioSprite[h],b._idHash[g.id]={src:c.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)},g.defaultPlayProps&&(b._defaultPlayPropsHash[g.id]=createjs.PlayPropsConfig.create(g.defaultPlayProps));null!=c.id&&(b._idHash[c.id]={src:c.src});var i=b.activePlugin.register(c);return a.create(c.src,f),null!=e&&isNaN(e)?c.data.channels=f||a.maxPerChannel():c.data=f||a.maxPerChannel(),i.type&&(c.type=i.type),c.defaultPlayProps&&(b._defaultPlayPropsHash[c.src]=createjs.PlayPropsConfig.create(c.defaultPlayProps)),i},b.registerSound=function(a,c,d,e,f){var g={src:a,id:c,data:d,defaultPlayProps:f};a instanceof Object&&a.src&&(e=c,g=a),g=createjs.LoadItem.create(g),g.path=e,null==e||g.src instanceof Object||(g.src=e+a);var h=b._registerSound(g);if(!h)return!1;if(b._preloadHash[g.src]||(b._preloadHash[g.src]=[]),b._preloadHash[g.src].push(g),1==b._preloadHash[g.src].length)h.on("complete",createjs.proxy(this._handleLoadComplete,this)),h.on("error",createjs.proxy(this._handleLoadError,this)),b.activePlugin.preload(h);else if(1==b._preloadHash[g.src][0])return!0;return g},b.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.registerSound(a[d].src,a[d].id,a[d].data,b,a[d].defaultPlayProps);return c},b.removeSound=function(c,d){if(null==b.activePlugin)return!1;c instanceof Object&&c.src&&(c=c.src);var e;if(c instanceof Object?e=b._parseSrc(c):(c=b._getSrcById(c).src,e=b._parsePath(c)),null==e)return!1;c=e.src,null!=d&&(c=d+c);for(var f in b._idHash)b._idHash[f].src==c&&delete b._idHash[f];return a.removeSrc(c),delete b._preloadHash[c],b.activePlugin.removeSound(c),!0},b.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},b.removeAllSounds=function(){b._idHash={},b._preloadHash={},a.removeAll(),b.activePlugin&&b.activePlugin.removeAllSounds()},b.loadComplete=function(a){if(!b.isReady())return!1;var c=b._parsePath(a);return a=c?b._getSrcById(c.src).src:b._getSrcById(a).src,void 0==b._preloadHash[a]?!1:1==b._preloadHash[a][0]},b._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var c=a.match(b.FILE_PATTERN);if(null==c)return!1;for(var d=c[4],e=c[5],f=b.capabilities,g=0;!f[e];)if(e=b.alternateExtensions[g++],g>b.alternateExtensions.length)return null;a=a.replace("."+c[5],"."+e);var h={name:d,src:a,extension:e};return h},b._parseSrc=function(a){var c={name:void 0,src:void 0,extension:void 0},d=b.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){c.src=a[e],c.extension=e;break}if(!c.src)return!1;var f=c.src.lastIndexOf("/");return c.name=-1!=f?c.src.slice(f+1):c.src,c},b.play=function(a,c,d,e,f,g,h,i,j){var k;k=createjs.PlayPropsConfig.create(c instanceof Object||c instanceof createjs.PlayPropsConfig?c:{interrupt:c,delay:d,offset:e,loop:f,volume:g,pan:h,startTime:i,duration:j});var l=b.createInstance(a,k.startTime,k.duration),m=b._playInstance(l,k);return m||l._playFailed(),l},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),f=f||b._defaultPlayPropsHash[g.src],f&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(c,d,e),h.uniqueId=b._lastID++,h},b.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},b.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)},b.getVolume=function(){return this._masterVolume},b.setMute=function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0},b.getMute=function(){return this._masterMute},b.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.getPosition()),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){var e=b._beginPlaying(a,c);
if(!e)return!1}else{var f=setTimeout(function(){b._beginPlaying(a,c)},c.delay);a.delayTimeoutId=f}return this._instances.push(a),!0},b._beginPlaying=function(b,c){if(!a.add(b,c.interrupt))return!1;var d=b._beginPlaying(c);if(!d){var e=createjs.indexOf(this._instances,b);return e>-1&&this._instances.splice(e,1),!1}return!0},b._getSrcById=function(a){return b._idHash[a]||{src:a}},b._playFinished=function(b){a.remove(b);var c=createjs.indexOf(this._instances,b);c>-1&&this._instances.splice(c,1)},createjs.Sound=Sound,a.channels={},a.create=function(b,c){var d=a.get(b);return null==d?(a.channels[b]=new a(b,c),!0):!1},a.removeSrc=function(b){var c=a.get(b);return null==c?!1:(c._removeAll(),delete a.channels[b],!0)},a.removeAll=function(){for(var b in a.channels)a.channels[b]._removeAll();a.channels={}},a.add=function(b,c){var d=a.get(b.src);return null==d?!1:d._add(b,c)},a.remove=function(b){var c=a.get(b.src);return null==c?!1:(c._remove(b),!0)},a.maxPerChannel=function(){return c.maxDefault},a.get=function(b){return a.channels[b]};var c=a.prototype;c.constructor=a,c.src=null,c.max=null,c.maxDefault=100,c.length=0,c.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},c._get=function(a){return this._instances[a]},c._add=function(a,b){return this._getSlot(b,a)?(this._instances.push(a),this.length++,!0):!1},c._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1==b?!1:(this._instances.splice(b,1),this.length--,!0)},c._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},c._getSlot=function(a){var b,c;if(a!=Sound.INTERRUPT_NONE&&(c=this._get(0),null==c))return!0;for(var d=0,e=this.max;e>d;d++){if(b=this._get(d),null==b)return!0;if(b.playState==Sound.PLAY_FINISHED||b.playState==Sound.PLAY_INTERRUPTED||b.playState==Sound.PLAY_FAILED){c=b;break}a!=Sound.INTERRUPT_NONE&&(a==Sound.INTERRUPT_EARLY&&b.getPosition()c.getPosition())&&(c=b)}return null!=c?(c._interrupt(),this._remove(c),!0):!1},c.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractSoundInstance=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._volume=1,Object.defineProperty(this,"volume",{get:this.getVolume,set:this.setVolume}),this._pan=0,Object.defineProperty(this,"pan",{get:this.getPan,set:this.setPan}),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this.getStartTime,set:this.setStartTime}),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this.getDuration,set:this.setDuration}),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this.getPlaybackResource,set:this.setPlaybackResource}),d!==!1&&d!==!0&&this.setPlaybackResource(d),this._position=0,Object.defineProperty(this,"position",{get:this.getPosition,set:this.setPosition}),this._loop=0,Object.defineProperty(this,"loop",{get:this.getLoop,set:this.setLoop}),this._muted=!1,Object.defineProperty(this,"muted",{get:this.getMuted,set:this.setMuted}),this._paused=!1,Object.defineProperty(this,"paused",{get:this.getPaused,set:this.setPaused})},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a,b,c,d,e,f){var g;return g=createjs.PlayPropsConfig.create(a instanceof Object||a instanceof createjs.PlayPropsConfig?a:{interrupt:a,delay:b,offset:c,loop:d,volume:e,pan:f}),this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(g),void(this._paused&&this.setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,g),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this.setPosition(a.offset),null!=a.loop&&this.setLoop(a.loop),null!=a.volume&&this.setVolume(a.volume),null!=a.pan&&this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),this},a.toString=function(){return"[AbstractSoundInstance]"},a.getPaused=function(){return this._paused},a.setPaused=function(a){return a!==!0&&a!==!1||this._paused==a||1==a&&this.playState!=createjs.Sound.PLAY_SUCCEEDED?void 0:(this._paused=a,a?this._pause():this._resume(),clearTimeout(this.delayTimeoutId),this)},a.setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},a.getVolume=function(){return this._volume},a.setMuted=function(a){return a===!0||a===!1?(this._muted=a,this._updateVolume(),this):void 0},a.getMuted=function(){return this._muted},a.setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},a.getPan=function(){return this._pan},a.getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED||(this._position=this._calculateCurrentPosition()),this._position},a.setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},a.getStartTime=function(){return this._startTime},a.setStartTime=function(a){return a==this._startTime?this:(this._startTime=Math.max(0,a||0),this._updateStartTime(),this)},a.getDuration=function(){return this._duration},a.setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},a.setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._setDurationFromSource(),this},a.getPlaybackResource=function(){return this._playbackResource},a.getLoop=function(){return this._loop},a.setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this.setPosition(a.offset),this.setLoop(a.loop),this.setVolume(a.volume),this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),null!=this._playbackResource&&this._positionc;c++){var e=this._soundInstances[b][c];e.setPlaybackResource(this._audioSources[b])}},a._handlePreloadError=function(){},a._updateVolume=function(){},createjs.AbstractPlugin=AbstractPlugin}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.SOUND)}var b=createjs.extend(a,createjs.AbstractLoader);a.context=null,b.toString=function(){return"[WebAudioLoader]"},b._createRequest=function(){this._request=new createjs.XHRRequest(this._item,!1),this._request.setResponseType("arraybuffer")},b._sendComplete=function(){a.context.decodeAudioData(this._rawResult,createjs.proxy(this._handleAudioDecoded,this),createjs.proxy(this._sendError,this))},b._handleAudioDecoded=function(a){this._result=a,this.AbstractLoader__sendComplete()},createjs.WebAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioSoundInstance(a,c,d,e){this.AbstractSoundInstance_constructor(a,c,d,e),this.gainNode=b.context.createGain(),this.panNode=b.context.createPanner(),this.panNode.panningModel=b._panningModel,this.panNode.connect(this.gainNode),this.sourceNode=null,this._soundCompleteTimeout=null,this._sourceNodeNext=null,this._playbackStartTime=0,this._endedHandler=createjs.proxy(this._handleSoundComplete,this)}var a=createjs.extend(WebAudioSoundInstance,createjs.AbstractSoundInstance),b=WebAudioSoundInstance;b.context=null,b.destinationNode=null,b._panningModel="equalpower",a.destroy=function(){this.AbstractSoundInstance_destroy(),this.panNode.disconnect(0),this.panNode=null,this.gainNode.disconnect(0),this.gainNode=null},a.toString=function(){return"[WebAudioSoundInstance]"},a._updatePan=function(){this.panNode.setPosition(this._pan,0,-.5)},a._removeLooping=function(){this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext)},a._addLooping=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},a._setDurationFromSource=function(){this._duration=1e3*this.playbackResource.duration},a._handleCleanUp=function(){this.sourceNode&&this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext)),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout),this._playbackStartTime=0},a._cleanUpAudioNode=function(a){return a&&(a.stop(0),a.disconnect(0),a=null),a},a._handleSoundReady=function(){this.gainNode.connect(b.destinationNode);var a=.001*this._duration,c=.001*this._position;c>a&&(c=a),this.sourceNode=this._createAndPlayAudioNode(b.context.currentTime-a,c),this._playbackStartTime=this.sourceNode.startTime-c,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(a-c)),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},a._createAndPlayAudioNode=function(a,c){var d=b.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,c+.001*this._startTime,e-c),d},a._pause=function(){this._position=1e3*(b.context.currentTime-this._playbackStartTime),this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout)},a._resume=function(){this._handleSoundReady()},a._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},a._calculateCurrentPosition=function(){return 1e3*(b.context.currentTime-this._playbackStartTime)},a._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},a._handleLoop=function(){this._cleanUpAudioNode(this.sourceNode),this.sourceNode=this._sourceNodeNext,this._playbackStartTime=this.sourceNode.startTime,this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0),this._soundCompleteTimeout=setTimeout(this._endedHandler,this._duration)},a._updateDuration=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._pause(),this._resume())},createjs.WebAudioSoundInstance=createjs.promote(WebAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioPlugin(){this.AbstractPlugin_constructor(),this._panningModel=b._panningModel,this.context=b.context,this.dynamicsCompressorNode=this.context.createDynamicsCompressor(),this.dynamicsCompressorNode.connect(this.context.destination),this.gainNode=this.context.createGain(),this.gainNode.connect(this.dynamicsCompressorNode),createjs.WebAudioSoundInstance.destinationNode=this.gainNode,this._capabilities=b._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var a=createjs.extend(WebAudioPlugin,createjs.AbstractPlugin),b=WebAudioPlugin;b._capabilities=null,b._panningModel="equalpower",b.context=null,b.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return"file:"!=location.protocol||a||this._isFileXHRSupported()?(b._generateCapabilities(),null==b.context?!1:!0):!1},b.playEmptySound=function(){if(null!=b.context){var a=b.context.createBufferSource();a.buffer=b.context.createBuffer(1,1,22050),a.connect(b.context.destination),a.start(0,0,0)}},b._isFileXHRSupported=function(){var a=!0,b=new XMLHttpRequest;try{b.open("GET","WebAudioPluginTest.fail",!1)}catch(c){return a=!1}b.onerror=function(){a=!1},b.onload=function(){a=404==this.status||200==this.status||0==this.status&&""!=this.response};try{b.send()}catch(c){a=!1}return a},b._generateCapabilities=function(){if(null==b._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;if(null==b.context)if(window.AudioContext)b.context=new AudioContext;else{if(!window.webkitAudioContext)return null;b.context=new webkitAudioContext}b._compatibilitySetUp(),b.playEmptySound(),b._capabilities={panning:!0,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}b.context.destination.numberOfChannels<2&&(b._capabilities.panning=!1)}},b._compatibilitySetUp=function(){if(b._panningModel="equalpower",!b.context.createGain){b.context.createGain=b.context.createGainNode;var a=b.context.createBufferSource();a.__proto__.start=a.__proto__.noteGrainOn,a.__proto__.stop=a.__proto__.noteOff,b._panningModel=0}},a.toString=function(){return"[WebAudioPlugin]"},a._addPropsToClasses=function(){var a=this._soundInstanceClass;a.context=this.context,a.destinationNode=this.gainNode,a._panningModel=this._panningModel,this._loaderClass.context=this.context},a._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},createjs.WebAudioPlugin=createjs.promote(WebAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){"use strict";function HTMLAudioTagPool(){throw"HTMLAudioTagPool cannot be instantiated"}function a(){this._tags=[]}var b=HTMLAudioTagPool;b._tags={},b._tagPool=new a,b._tagUsed={},b.get=function(a){var c=b._tags[a];return null==c?(c=b._tags[a]=b._tagPool.get(),c.src=a):b._tagUsed[a]?(c=b._tagPool.get(),c.src=a):b._tagUsed[a]=!0,c},b.set=function(a,c){c==b._tags[a]?b._tagUsed[a]=!1:b._tagPool.set(c)},b.remove=function(a){var c=b._tags[a];return null==c?!1:(b._tagPool.set(c),delete b._tags[a],delete b._tagUsed[a],!0)},b.getDuration=function(a){var c=b._tags[a];return null==c?0:1e3*c.duration},createjs.HTMLAudioTagPool=HTMLAudioTagPool;var c=a.prototype;c.constructor=a,c.get=function(){var a;return a=0==this._tags.length?this._createTag():this._tags.pop(),null==a.parentNode&&document.body.appendChild(a),a},c.set=function(a){var b=createjs.indexOf(this._tags,a);-1==b&&(this._tags.src=null,this._tags.push(a))},c.toString=function(){return"[TagPool]"},c._createTag=function(){var a=document.createElement("audio");return a.autoplay=!1,a.preload="none",a}}(),this.createjs=this.createjs||{},function(){"use strict";function HTMLAudioSoundInstance(a,b,c,d){this.AbstractSoundInstance_constructor(a,b,c,d),this._audioSpriteStopTime=null,this._delayTimeoutId=null,this._endedHandler=createjs.proxy(this._handleSoundComplete,this),this._readyHandler=createjs.proxy(this._handleTagReady,this),this._stalledHandler=createjs.proxy(this._playFailed,this),this._audioSpriteEndHandler=createjs.proxy(this._handleAudioSpriteLoop,this),this._loopHandler=createjs.proxy(this._handleSoundComplete,this),c?this._audioSpriteStopTime=.001*(b+c):this._duration=createjs.HTMLAudioTagPool.getDuration(this.src)}var a=createjs.extend(HTMLAudioSoundInstance,createjs.AbstractSoundInstance);a.setMasterVolume=function(){this._updateVolume()},a.setMasterMute=function(){this._updateVolume()},a.toString=function(){return"[HTMLAudioSoundInstance]"},a._removeLooping=function(){null!=this._playbackResource&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},a._addLooping=function(){null==this._playbackResource||this._audioSpriteStopTime||(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.loop=!0)},a._handleCleanUp=function(){var a=this._playbackResource;if(null!=a){a.pause(),a.loop=!1,a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),a.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1);try{a.currentTime=this._startTime}catch(b){}createjs.HTMLAudioTagPool.set(this.src,a),this._playbackResource=null}},a._beginPlaying=function(a){return this._playbackResource=createjs.HTMLAudioTagPool.get(this.src),this.AbstractSoundInstance__beginPlaying(a)},a._handleSoundReady=function(){if(4!==this._playbackResource.readyState){var a=this._playbackResource;return a.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),a.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),a.preload="auto",void a.load()}this._updateVolume(),this._playbackResource.currentTime=.001*(this._startTime+this._position),this._audioSpriteStopTime?this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1):(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),0!=this._loop&&(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.loop=!0)),this._playbackResource.play()},a._handleTagReady=function(){this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),this._handleSoundReady()},a._pause=function(){this._playbackResource.pause()},a._resume=function(){this._playbackResource.play()},a._updateVolume=function(){if(null!=this._playbackResource){var a=this._muted||createjs.Sound._masterMute?0:this._volume*createjs.Sound._masterVolume;a!=this._playbackResource.volume&&(this._playbackResource.volume=a)}},a._calculateCurrentPosition=function(){return 1e3*this._playbackResource.currentTime-this._startTime},a._updatePosition=function(){this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._handleSetPositionSeek,!1);try{this._playbackResource.currentTime=.001*(this._position+this._startTime)}catch(a){this._handleSetPositionSeek(null)}},a._handleSetPositionSeek=function(){null!=this._playbackResource&&(this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._handleSetPositionSeek,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},a._handleAudioSpriteLoop=function(){this._playbackResource.currentTime<=this._audioSpriteStopTime||(this._playbackResource.pause(),0==this._loop?this._handleSoundComplete(null):(this._position=0,this._loop--,this._playbackResource.currentTime=.001*this._startTime,this._paused||this._playbackResource.play(),this._sendEvent("loop")))},a._handleLoop=function(){0==this._loop&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},a._updateStartTime=function(){this._audioSpriteStopTime=.001*(this._startTime+this._duration),this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1))},a._updateDuration=function(){this._audioSpriteStopTime=.001*(this._startTime+this._duration),this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED,this._endedHandler,!1),this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE,this._audioSpriteEndHandler,!1))},createjs.HTMLAudioSoundInstance=createjs.promote(HTMLAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function HTMLAudioPlugin(){this.AbstractPlugin_constructor(),this.defaultNumChannels=2,this._capabilities=b._capabilities,this._loaderClass=createjs.SoundLoader,this._soundInstanceClass=createjs.HTMLAudioSoundInstance}var a=createjs.extend(HTMLAudioPlugin,createjs.AbstractPlugin),b=HTMLAudioPlugin;b.MAX_INSTANCES=30,b._AUDIO_READY="canplaythrough",b._AUDIO_ENDED="ended",b._AUDIO_SEEKED="seeked",b._AUDIO_STALLED="stalled",b._TIME_UPDATE="timeupdate",b._capabilities=null,b.isSupported=function(){return b._generateCapabilities(),null!=b._capabilities},b._generateCapabilities=function(){if(null==b._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;b._capabilities={panning:!1,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},a.register=function(a){var b=createjs.HTMLAudioTagPool.get(a.src),c=this.AbstractPlugin_register(a);return c.setTag(b),c},a.removeSound=function(a){this.AbstractPlugin_removeSound(a),createjs.HTMLAudioTagPool.remove(a)},a.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.setPlaybackResource(null),d},a.toString=function(){return"[HTMLAudioPlugin]"},a.setVolume=a.getVolume=a.setMute=null,createjs.HTMLAudioPlugin=createjs.promote(HTMLAudioPlugin,"AbstractPlugin")}();
\ No newline at end of file