diff --git a/.gitignore b/.gitignore
index 88e35a3c..14eedf8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@ docs/**/
**/.sass-cache/
main.css
*.codekit
+tests/.grunt
+_SpecRunner.html
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 00000000..41a9ccc6
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,14 @@
+_assets
+build
+bower.json
+dev
+docs
+examples
+extras
+icon.png
+lib/**-NEXT**.js
+spikes
+src
+tests
+tutorials
+VERSIONS.txt
\ No newline at end of file
diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..4ca50b6c
--- /dev/null
+++ b/ISSUE_TEMPLATE.md
@@ -0,0 +1,27 @@
+### TODO
+- [ ] Is this a question or bug? [Stack Overflow](https://stackoverflow.com/questions/tagged/createjs) is a much better place to ask any questions you may have.
+
+- [ ] Did you search the [issues](https://github.com/CreateJS/SoundJS/issues) to see if someone else has already reported your issue? If yes, please add more details if you have any!
+
+- [ ] If you're using an older [version](https://github.com/CreateJS/SoundJS/blob/master/VERSIONS.txt), have you tried the latest?
+
+- [ ] If you're requesting a new feature; provide as many details as you can. Why do you want this feature? Do you have ideas for how this feature should be implemented? Pseudocode is always welcome!
+
+
+### Issue Details
+* Version used (Ex; 1.0):
+
+
+* Describe whats happening (Include any relevant console errors, a [Gist](https://gist.github.com/) is preferred for longer errors):
+
+
+
+* OS & Browser version *(Please be specific)* (Ex; Windows 10 Home, Chrome 62.0.3202.94):
+
+
+
+* Do you know of any workarounds?
+
+
+
+* Provide any extra details that will help us fix your issue. Including a link to a [CodePen.io](https://codepen.io) or [JSFiddle.net](https://jsfiddle.net) example that shows the issue in isolation will greatly increase the chance of getting a quick response.
diff --git a/README.md b/README.md
index 9521eda3..61aa4eb5 100644
--- a/README.md
+++ b/README.md
@@ -2,17 +2,21 @@
SoundJS is a library to make working with audio on the web easier. It provides a consistent API for playing audio in
different browsers, including using a target plugin model to provide an easy way to provide additional audio plugins
-like Web Audio, and a Flash fallback. A mechanism has been provided for easily tying in audio preloading to
-[PreloadJS](http://preloadjs.com)
+like a Flash fallback (included, but must be used separately from the combined/minified version).
+
+A mechanism has been provided for easily tying in audio preloading to [PreloadJS](http://createjs.com/preloadjs/).
## Example
- createjs.Sound.addEventListener("fileload", handleLoadComplete);
- createjs.Sound.registerSound({src:"path/to/sound.mp3|path/to/sound.ogg", id:"sound"});
- function handleLoadComplete(event) {
- createjs.Sound.play("sound");
- }
+```javascript
+createjs.Sound.on("fileload", handleLoadComplete);
+createjs.Sound.alternateExtensions = ["mp3"];
+createjs.Sound.registerSound({src:"path/to/sound.ogg", id:"sound"});
+function handleLoadComplete(event) {
+ createjs.Sound.play("sound");
+}
+```
## License
Built by gskinner.com, and released for free under the MIT license, which means you can use it for almost any purpose
@@ -21,10 +25,12 @@ Built by gskinner.com, and released for free under the MIT license, which means
## Support and Resources
* Find examples and more information at the [SoundJS web site](http://soundjs.com/)
-* Read the [documentation](http://createjs.com/Docs/SoundJS/)
-* You can also ask questions and interact with other users at our [Community](http://community.createjs.com) site.
+* Read the [documentation](http://createjs.com/docs/soundjs/)
+* Discuss, share projects, and interact with other users on [reddit](http://www.reddit.com/r/createjs/).
+* Ask technical questions on [Stack Overflow](http://stackoverflow.com/questions/tagged/soundjs).
+* File verified bugs or formal feature requests using Issues on [GitHub](https://github.com/CreateJS/SoundJS/issues).
* Have a look at the included [examples](https://github.com/CreateJS/SoundJS/tree/master/examples) and
-[API documentation](http://createjs.com/Docs/SoundJS/) for more in-depth information.
+[API documentation](http://createjs.com/docs/soundjs/) for more in-depth information.
## Classes
@@ -33,7 +39,7 @@ Built by gskinner.com, and released for free under the MIT license, which means
The core API for playing sounds. Call createjs.Sound.play(sound, ...options), and a sound instance is created that can be
used to control the audio, and dispatches events when it is complete, loops, or is interrupted.
-### [SoundInstance](http://createjs.com/Docs/SoundJS/classes/SoundInstance.html)
+### [SoundInstance](http://createjs.com/Docs/SoundJS/classes/AbstractSoundInstance.html)
A controllable sound object that wraps the actual plugin implementation, providing a consistent API for audio playback,
no matter what happens in the background. Sound instances can be paused, muted, and stopped; and the volume, pan (where
available), and position changed using the simple API.
@@ -46,9 +52,13 @@ run locally, and the HTML audio plugin will be used instead.
The fallback built-in plugin, which manages audio playback via the HTML5
-
+
@@ -130,7 +130,7 @@
Sorry!
function playSound(target) {
//Play the sound: play (src, interrupt, delay, offset, loop, volume, pan)
- var instance = createjs.Sound.play(target.id, createjs.Sound.INTERRUPT_NONE, 0, 0, false, 1);
+ var instance = createjs.Sound.play(target.id);
if (instance == null || instance.playState == createjs.Sound.PLAY_FAILED) {
return;
}
diff --git a/examples/SoundExplosion.html b/examples/03_PlayWithEaselJS.html
similarity index 97%
rename from examples/SoundExplosion.html
rename to examples/03_PlayWithEaselJS.html
index 177ccea4..84673f21 100644
--- a/examples/SoundExplosion.html
+++ b/examples/03_PlayWithEaselJS.html
@@ -2,7 +2,7 @@
- EaselJS Example: Using Stage.autoClear
+ SoudnJS Example: Play With EaselJS
@@ -13,7 +13,7 @@
-
Fireworks Demo
+
Play with EaselJS
A simple demo demonstrating playing a lot of sounds at once while
animating.
@@ -35,7 +35,7 @@
Sorry!
-
+
@@ -234,7 +234,7 @@
Sorry!
s = new createjs.Bitmap(sparkleShape.cacheCanvas);
// add a bounce sound, which never changes after it's added
s.bounceSound = createjs.Sound.createInstance("bounce"); // create it by id
- s.bounceSound.setVolume(Math.random() * 0.01 + 0.01); // because there will be a lot
+ s.bounceSound.volume = (Math.random() * 0.01 + 0.01); // because there will be a lot
}
return s;
}
diff --git a/examples/SoundTween.html b/examples/04_SoundTween.html
old mode 100644
new mode 100755
similarity index 87%
rename from examples/SoundTween.html
rename to examples/04_SoundTween.html
index b7b7934d..eb8fdcc8
--- a/examples/SoundTween.html
+++ b/examples/04_SoundTween.html
@@ -14,7 +14,7 @@
Sound Tween
-
This example shows how to tween volume and pan.
+
This example shows how to tween volume and pan. Note that HTML audio does not support panning.
function drawDial() {
var s = new createjs.Shape();
s.graphics.setStrokeStyle(5, 'round', 'round');
- s.graphics.beginStroke(('#000000'));
+ s.graphics.beginStroke('#000000');
s.graphics.beginFill("#777").drawCircle(0, 0, 50);
s.graphics.endStroke();
s.graphics.endFill();
s.graphics.setStrokeStyle(1, 'round', 'round');
- s.graphics.beginStroke(('#000000'));
+ s.graphics.beginStroke('#000000');
s.graphics.moveTo(0, 0);
s.graphics.lineTo(0, -50);
diff --git a/examples/AudioSprite.html b/examples/05_AudioSprite.html
old mode 100644
new mode 100755
similarity index 85%
rename from examples/AudioSprite.html
rename to examples/05_AudioSprite.html
index dcd9bfa3..e647c931
--- a/examples/AudioSprite.html
+++ b/examples/05_AudioSprite.html
@@ -29,10 +29,25 @@
Sorry!
with the device and browser you are using. Thank you.
-
+
+
-
-
-
-
-
-
Web Audio Music Visualizer
-
-
This example shows a web audio exclusive demo that examines music data as
- it is played.
-
-
-
-
Sorry!
-
-
SoundJS or Web Audio is not currently supported in your browser.
- Either you are using a browser that does not support web audio, or you
- are trying to open this locally.
- Because web audio uses XHR, it cannot be opened as a local file and must
- be hosted on a server.
-
-
Please log a bug
- if you are not opening this locally and your browser supports web audio.
- Thank you.
This example shows a web audio exclusive demo that examines music data as
+ it is played.
+
+
+
+
Sorry!
+
+
SoundJS or Web Audio is not currently supported in your browser.
+ Either you are using a browser that does not support web audio, or you
+ are trying to open this locally.
+ Because web audio uses XHR, it cannot be opened as a local file and must
+ be hosted on a server.
+
+
Please log a bug
+ if you are not opening this locally and your browser supports web audio.
+ Thank you.
Audio position is calculated asynchronusly by Media. The SoundJS solution to this problem is two-fold:
+ *
Provide {{#crossLink "CordovaAudioSoundInstance/getCurrentPosition"}}{{/crossLink}} that maps directly to media.getCurrentPosition.
+ *
Provide a best guess position based on elapsed time since playback started, which is synchronized with actual position when the audio is paused or stopped.
+ * Testing showed this to be fairly reliable within 200ms.
+ *
+ *
+ * @class CordovaAudioPlugin
+ * @extends AbstractPlugin
+ * @constructor
+ */
+ function CordovaAudioPlugin() {
+ this.AbstractPlugin_constructor();
+
+ this._capabilities = s._capabilities;
+
+ this._loaderClass = createjs.CordovaAudioLoader;
+ this._soundInstanceClass = createjs.CordovaAudioSoundInstance;
+
+ this._srcDurationHash = {};
+ }
+
+ var p = createjs.extend(CordovaAudioPlugin, createjs.AbstractPlugin);
+ var s = CordovaAudioPlugin;
+
+
+// Static Properties
+ /**
+ * Sets a default playAudioWhenScreenIsLocked property for play calls on iOS devices.
+ * Individual SoundInstances can alter the default with {{#crossLink "CordovaAudioSoundInstance/playWhenScreenLocked"}}{{/crossLink}}.
+ * @property playWhenScreenLocked
+ * @type {boolean}
+ * @static
+ */
+ s.playWhenScreenLocked = false;
+
+ /**
+ * The capabilities of the plugin. This is generated via the {{#crossLink "CordovaAudioPlugin/_generateCapabilities"}}{{/crossLink}}
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for an overview of all
+ * of the available properties.
+ * @property _capabilities
+ * @type {Object}
+ * @private
+ * @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/capabilities:property"}}{{/crossLink}}
+ * method for an overview of plugin capabilities.
+ * @method _generateCapabilities
+ * @static
+ * @private
+ */
+ s._generateCapabilities = function () {
+ if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) {return;}
+
+ // OJR my best guess is that Cordova will have the same limits on playback that the audio tag has, but this could be wrong
+ 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.create = function (src, startTime, duration) {
+ var si = this.AbstractPlugin_create(src, startTime, duration);
+ si.playWhenScreenLocked = this.playWhenScreenLocked;
+ return si;
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioPlugin]";
+ };
+
+ // plugin does not support these
+ p.setVolume = p.getVolume = p.setMute = null;
+
+ /**
+ * Get the duration for a src. Intended for internal use by CordovaAudioSoundInstance.
+ * @method getSrcDuration
+ * @param src
+ * @returns {Number} The duration of the src or null if it does not exist
+ */
+ p.getSrcDuration = function(src) {
+ return this._srcDurationHash[src];
+ };
+
+// Private Methods
+ p._handlePreloadComplete = function (event) {
+ var src = event.target.getItem().src;
+ this._srcDurationHash[src] = event.result;
+ this._audioSources[src] = event.result;
+ //this.AbstractPlugin__handlePreloadComplete(event); // we don't want to do the rest of this
+ };
+
+ p.removeSound = function (src) {
+ delete(this._srcDurationHash[src]);
+ this.AbstractPlugin_removeSound(src);
+ };
+
+ createjs.CordovaAudioPlugin = createjs.promote(CordovaAudioPlugin, "AbstractPlugin");
+}());
+
+//##############################################################################
+// version_cordovaplugin.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+
+ var s = createjs.CordovaAudioPlugin = createjs.CordovaAudioPlugin || {};
+
+ /**
+ * The version string for this release.
+ * @for CordovaAudioPlugin
+ * @property version
+ * @type String
+ * @static
+ **/
+ s.version = /*=version*/"NEXT"; // injected by build process
+
+ /**
+ * The build date for this release in UTC format.
+ * @for CordovaAudioPlugin
+ * @property buildDate
+ * @type String
+ * @static
+ **/
+ s.buildDate = /*=date*/"Wed, 21 Jun 2017 16:53:32 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
new file mode 100644
index 00000000..d0a91ffc
--- /dev/null
+++ b/lib/cordovaaudioplugin-NEXT.min.js
@@ -0,0 +1,17 @@
+/*!
+* @license SoundJS
+* Visit http://createjs.com/ for documentation, updates and examples.
+*
+* Copyright (c) 2011-2015 gskinner.com, inc.
+*
+* Distributed under the terms of the MIT license.
+* http://www.opensource.org/licenses/mit-license.html
+*
+* This notice shall be included in all copies or substantial portions of the Software.
+*/
+
+/**!
+ * 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.Types.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._playbackResource.pause(),this._playbackResource.release(),this.AbstractSoundInstance_destroy()},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)},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, 21 Jun 2017 16:53:32 GMT"}();
\ No newline at end of file
diff --git a/lib/cordovaaudioplugin.js b/lib/cordovaaudioplugin.js
new file mode 100644
index 00000000..2688126c
--- /dev/null
+++ b/lib/cordovaaudioplugin.js
@@ -0,0 +1,571 @@
+/*!
+* 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.
+*/
+
+
+//##############################################################################
+// CordovaAudioLoader.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * Loader provides a mechanism to preload Cordova 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.
+ * Currently files are assumed to be local and no loading actually takes place. This class exists to more easily support
+ * the existing architecture.
+ *
+ * @class CordovaAudioLoader
+ * @param {String} loadItem The item to be loaded
+ * @extends XHRRequest
+ * @protected
+ */
+ function Loader(loadItem) {
+ this.AbstractLoader_constructor(loadItem, true, createjs.Types.SOUND);
+
+ /**
+ * A Media object used to determine if src exists and to get duration
+ * @property _media
+ * @type {Media}
+ * @protected
+ */
+ this._media = null;
+
+ /**
+ * A time counter that triggers timeout if loading takes too long
+ * @property _loadTime
+ * @type {number}
+ * @protected
+ */
+ this._loadTime = 0;
+
+ /**
+ * The frequency to fire the loading timer until duration can be retrieved
+ * @property _TIMER_FREQUENCY
+ * @type {number}
+ * @protected
+ */
+ this._TIMER_FREQUENCY = 100;
+ };
+ var p = createjs.extend(Loader, createjs.AbstractLoader);
+
+
+// public methods
+ p.load = function() {
+ this._media = new Media(this._item.src, null, createjs.proxy(this._mediaErrorHandler,this));
+ this._media.seekTo(0); // needed to get duration
+
+ this._getMediaDuration();
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioLoader]";
+ };
+
+
+// private methods
+ /**
+ * Fires if audio cannot seek, indicating that src does not exist.
+ * @method _mediaErrorHandler
+ * @param error
+ * @protected
+ */
+ p._mediaErrorHandler = function(error) {
+ this._media.release();
+ this._sendError();
+ };
+
+ /**
+ * will attempt to get duration of audio until successful or time passes this._item.loadTimeout
+ * @method _getMediaDuration
+ * @protected
+ */
+ p._getMediaDuration = function() {
+ this._result = this._media.getDuration() * 1000;
+ if (this._result < 0) {
+ this._loadTime += this._TIMER_FREQUENCY;
+ if (this._loadTime > this._item.loadTimeout) {
+ this.handleEvent({type:"timeout"});
+ } else {
+ setTimeout(createjs.proxy(this._getMediaDuration, this), this._TIMER_FREQUENCY);
+ }
+ } else {
+ this._media.release();
+ this._sendComplete();
+ }
+ };
+
+ createjs.CordovaAudioLoader = createjs.promote(Loader, "AbstractLoader");
+}());
+
+//##############################################################################
+// CordovaAudioSoundInstance.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * CordovaAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
+ * {{#crossLink "CordovaAudioPlugin"}}{{/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 CordovaAudioSoundInstance
+ * @extends AbstractSoundInstance
+ * @constructor
+ */
+ function CordovaAudioSoundInstance(src, startTime, duration, playbackResource) {
+ this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
+
+// Public Properties
+ /**
+ * Sets the playAudioWhenScreenIsLocked property for play calls on iOS devices.
+ * @property playWhenScreenLocked
+ * @type {boolean}
+ */
+ this.playWhenScreenLocked = null;
+
+// Private Properties
+ /**
+ * Used to approximate the playback position by storing the number of milliseconds elapsed since
+ * 1 January 1970 00:00:00 UTC when playing
+ * Note that if js clock is out of sync with Media playback, this will become increasingly inaccurate.
+ * @property _playStartTime
+ * @type {Number}
+ * @protected
+ */
+ this._playStartTime = null;
+
+ /**
+ * A TimeOut used to trigger the end and possible loop of audio sprites.
+ * @property _audioSpriteTimeout
+ * @type {null}
+ * @protected
+ */
+ this._audioSpriteTimeout = null;
+
+ /**
+ * Boolean value that indicates if we are using an audioSprite
+ * @property _audioSprite
+ * @type {boolean}
+ * @protected
+ */
+ this._audioSprite = false;
+
+ // Proxies, make removing listeners easier.
+ 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(src, this._mediaPlayFinishedHandler, this._mediaErrorHandler, this._mediaProgressHandler);
+
+ if (duration) {
+ this._audioSprite = true;
+ } else {
+ this._setDurationFromSource();
+ }
+ }
+ var p = createjs.extend(CordovaAudioSoundInstance, createjs.AbstractSoundInstance);
+
+
+// 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.destroy = function() {
+ // pause and release the playback resource, then call parent function
+ this._playbackResource.pause();
+ this._playbackResource.release();
+ this.AbstractSoundInstance_destroy();
+ };
+
+ /**
+ * Maps to Media.getCurrentPosition,
+ * which is curiously asynchronus and requires a callback.
+ * @method getCurrentPosition
+ * @param {Method} mediaSuccess The callback that is passed the current position in seconds.
+ * @param {Method} [mediaError=null] (Optional) The callback to execute if an error occurs.
+ */
+ p.getCurrentPosition = function (mediaSuccess, mediaError) {
+ this._playbackResource.getCurrentPosition(mediaSuccess, mediaError);
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioSoundInstance]";
+ };
+
+//Private Methods
+ /**
+ * media object has failed and likely will never work
+ * @method _handleMediaError
+ * @param error
+ * @private
+ */
+ p._handleMediaError = function(error) {
+ clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
+
+ this.playState = createjs.Sound.PLAY_FAILED;
+ this._sendEvent("failed");
+ };
+
+ p._handleMediaProgress = function(state) {
+ // do nothing
+ };
+
+ p._handleAudioSpriteComplete = function() {
+ this._playbackResource.pause();
+ this._handleSoundComplete();
+ };
+ /* don't need these for current looping approach
+ p._removeLooping = function() {
+ };
+
+ p._addLooping = function() {
+ };
+ */
+
+ p._handleCleanUp = function () {
+ clearTimeout(this._audioSpriteTimeout);
+ // OJR cannot use .stop as it prevents .seekTo from working
+ // todo consider media.release
+ };
+
+ p._handleSoundReady = function (event) {
+ this._playbackResource.seekTo(this._startTime + this._position);
+
+ if (this._audioSprite) {
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this._position)
+ }
+
+ this._playbackResource.play({playAudioWhenScreenIsLocked: this.playWhenScreenLocked});
+ this._playStartTime = Date.now();
+ };
+
+ p._pause = function () {
+ clearTimeout(this._audioSpriteTimeout);
+ this._playbackResource.pause();
+ if (this._playStartTime) {
+ this._position = Date.now() - this._playStartTime;
+ this._playStartTime = null;
+ }
+ this._playbackResource.getCurrentPosition(createjs.proxy(this._updatePausePos, this));
+ };
+
+ /**
+ * Synchronizes the best guess position with the actual current position.
+ * @method _updatePausePos
+ * @param {Number} pos The current position in seconds
+ * @private
+ */
+ p._updatePausePos = function (pos) {
+ this._position = pos * 1000 - this._startTime;
+ if(this._playStartTime) {
+ this._playStartTime = Date.now();
+ }
+ };
+
+ p._resume = function () {
+ if (this._audioSprite) {
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this._position)
+ }
+
+ this._playbackResource.play({playAudioWhenScreenIsLocked: this.playWhenScreenLocked});
+ this._playStartTime = Date.now();
+ };
+
+ p._handleStop = function() {
+ clearTimeout(this._audioSpriteTimeout);
+ this._playbackResource.pause(); // cannot use .stop because it prevents .seekTo from working
+ this._playbackResource.seekTo(this._startTime);
+ if (this._playStartTime) {
+ this._position = 0;
+ this._playStartTime = null;
+ }
+ };
+
+ p._updateVolume = function () {
+ var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
+ this._playbackResource.setVolume(newVolume);
+ };
+
+ p._calculateCurrentPosition = function() {
+ // return best guess position.
+ // Note if Media and js clock are out of sync, this value will become increasingly inaccurate over time
+ if (this._playStartTime) {
+ this._position = Date.now() - this._playStartTime + this._position;
+ this._playStartTime = Date.now();
+ }
+ return this._position;
+ };
+
+ p._updatePosition = function() {
+ this._playbackResource.seekTo(this._startTime + this._position);
+ this._playStartTime = Date.now();
+ if (this._audioSprite) {
+ clearTimeout(this._audioSpriteTimeout);
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this._position)
+ }
+ };
+
+ p._handleLoop = function (event) {
+ this._handleSoundReady();
+ };
+
+ p._updateStartTime = function () {
+ this._audioSprite = true;
+
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ // do nothing
+ }
+ };
+
+ p._updateDuration = function () {
+ this._audioSprite
+
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ clearTimeout(this._audioSpriteTimeout);
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this.position)
+ }
+ };
+
+ p._setDurationFromSource = function () {
+ this._duration = createjs.Sound.activePlugin.getSrcDuration(this.src); // TODO find a better way to do this that does not break flow
+ };
+
+ createjs.CordovaAudioSoundInstance = createjs.promote(CordovaAudioSoundInstance, "AbstractSoundInstance");
+}());
+
+//##############################################################################
+// CordovaAudioPlugin.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+
+ "use strict";
+
+ /**
+ * Play sounds using Cordova Media plugin, which will work with a Cordova app and tools that utilize Cordova such as PhoneGap or Ionic.
+ * This plugin is not used by default, and must be registered manually in {{#crossLink "Sound"}}{{/crossLink}}
+ * using the {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
+ * This plugin is recommended when building a Cordova based app, but is not required.
+ *
+ * NOTE the Cordova Media plugin is required
+ *
+ * cordova plugin add org.apache.cordova.media
+ *
+ *
Known Issues
+ * Audio Position
+ *
Audio position is calculated asynchronusly by Media. The SoundJS solution to this problem is two-fold:
+ *
Provide {{#crossLink "CordovaAudioSoundInstance/getCurrentPosition"}}{{/crossLink}} that maps directly to media.getCurrentPosition.
+ *
Provide a best guess position based on elapsed time since playback started, which is synchronized with actual position when the audio is paused or stopped.
+ * Testing showed this to be fairly reliable within 200ms.
+ *
+ *
+ * @class CordovaAudioPlugin
+ * @extends AbstractPlugin
+ * @constructor
+ */
+ function CordovaAudioPlugin() {
+ this.AbstractPlugin_constructor();
+
+ this._capabilities = s._capabilities;
+
+ this._loaderClass = createjs.CordovaAudioLoader;
+ this._soundInstanceClass = createjs.CordovaAudioSoundInstance;
+
+ this._srcDurationHash = {};
+ }
+
+ var p = createjs.extend(CordovaAudioPlugin, createjs.AbstractPlugin);
+ var s = CordovaAudioPlugin;
+
+
+// Static Properties
+ /**
+ * Sets a default playAudioWhenScreenIsLocked property for play calls on iOS devices.
+ * Individual SoundInstances can alter the default with {{#crossLink "CordovaAudioSoundInstance/playWhenScreenLocked"}}{{/crossLink}}.
+ * @property playWhenScreenLocked
+ * @type {boolean}
+ * @static
+ */
+ s.playWhenScreenLocked = false;
+
+ /**
+ * The capabilities of the plugin. This is generated via the {{#crossLink "CordovaAudioPlugin/_generateCapabilities"}}{{/crossLink}}
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for an overview of all
+ * of the available properties.
+ * @property _capabilities
+ * @type {Object}
+ * @private
+ * @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/capabilities:property"}}{{/crossLink}}
+ * method for an overview of plugin capabilities.
+ * @method _generateCapabilities
+ * @static
+ * @private
+ */
+ s._generateCapabilities = function () {
+ if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) {return;}
+
+ // OJR my best guess is that Cordova will have the same limits on playback that the audio tag has, but this could be wrong
+ 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.create = function (src, startTime, duration) {
+ var si = this.AbstractPlugin_create(src, startTime, duration);
+ si.playWhenScreenLocked = this.playWhenScreenLocked;
+ return si;
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioPlugin]";
+ };
+
+ // plugin does not support these
+ p.setVolume = p.getVolume = p.setMute = null;
+
+ /**
+ * Get the duration for a src. Intended for internal use by CordovaAudioSoundInstance.
+ * @method getSrcDuration
+ * @param src
+ * @returns {Number} The duration of the src or null if it does not exist
+ */
+ p.getSrcDuration = function(src) {
+ return this._srcDurationHash[src];
+ };
+
+// Private Methods
+ p._handlePreloadComplete = function (event) {
+ var src = event.target.getItem().src;
+ this._srcDurationHash[src] = event.result;
+ this._audioSources[src] = event.result;
+ //this.AbstractPlugin__handlePreloadComplete(event); // we don't want to do the rest of this
+ };
+
+ p.removeSound = function (src) {
+ delete(this._srcDurationHash[src]);
+ this.AbstractPlugin_removeSound(src);
+ };
+
+ createjs.CordovaAudioPlugin = createjs.promote(CordovaAudioPlugin, "AbstractPlugin");
+}());
+
+//##############################################################################
+// version_cordovaplugin.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function () {
+
+ var s = createjs.CordovaAudioPlugin = createjs.CordovaAudioPlugin || {};
+
+ /**
+ * The version string for this release.
+ * @for CordovaAudioPlugin
+ * @property version
+ * @type String
+ * @static
+ **/
+ s.version = /*=version*/"1.0.0"; // injected by build process
+
+ /**
+ * The build date for this release in UTC format.
+ * @for CordovaAudioPlugin
+ * @property buildDate
+ * @type String
+ * @static
+ **/
+ s.buildDate = /*=date*/"Thu, 14 Sep 2017 19:47:47 GMT"; // injected by build process
+
+})();
\ No newline at end of file
diff --git a/lib/cordovaaudioplugin.min.js b/lib/cordovaaudioplugin.min.js
new file mode 100644
index 00000000..e400d506
--- /dev/null
+++ b/lib/cordovaaudioplugin.min.js
@@ -0,0 +1,17 @@
+/*!
+* @license SoundJS
+* Visit http://createjs.com/ for documentation, updates and examples.
+*
+* Copyright (c) 2011-2015 gskinner.com, inc.
+*
+* Distributed under the terms of the MIT license.
+* http://www.opensource.org/licenses/mit-license.html
+*
+* This notice shall be included in all copies or substantial portions of the Software.
+*/
+
+/**!
+ * 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.Types.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._playbackResource.pause(),this._playbackResource.release(),this.AbstractSoundInstance_destroy()},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)},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="1.0.0",a.buildDate="Thu, 14 Sep 2017 19:47:47 GMT"}();
\ No newline at end of file
diff --git a/lib/flashaudioplugin-NEXT.combined.js b/lib/flashaudioplugin-NEXT.js
similarity index 96%
rename from lib/flashaudioplugin-NEXT.combined.js
rename to lib/flashaudioplugin-NEXT.js
index 8aeff12c..827ae762 100644
--- a/lib/flashaudioplugin-NEXT.combined.js
+++ b/lib/flashaudioplugin-NEXT.js
@@ -829,7 +829,7 @@ this.createjs = this.createjs || {};
* @protected
*/
function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, false, createjs.AbstractLoader.SOUND);
+ this.AbstractLoader_constructor(loadItem, false, createjs.Types.SOUND);
// Public properties
@@ -844,14 +844,13 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(Loader, createjs.AbstractLoader);
-
// Static Properties
var s = Loader;
/**
* A reference to the Flash instance that gets created.
* @property flash
* @type {Object | Embed}
- * @protected
+ * @private
*/
s._flash = null;
@@ -859,7 +858,7 @@ this.createjs = this.createjs || {};
* A list of loader instances that tried to load before _flash was set
* @property _preloadInstances
* @type {Array}
- * @protected
+ * @private
*/
s._preloadInstances = [];
@@ -1009,23 +1008,20 @@ this.createjs = this.createjs || {};
// Public Methods
+ // TODO change flash.setLoop to mimic remove and add??
+ p.setLoop = function (value) {
+ if(this.flashId!= null) {
+ s._flash.setLoop(this.flashId, value);
+ }
+ this._loop = value;
+ };
+
p.toString = function () {
return "[FlashAudioSoundInstance]"
};
// Private Methods
- // TODO change flash.setLoop to mimic remove and add??
- p._removeLooping = function () {
- if (this.flashId == null) { return; }
- s._flash.setLoop(this.flashId, this._loop);
- };
-
- p._addLooping = function () {
- if (this.flashId == null) { return; }
- s._flash.setLoop(this.flashId, this._loop);
- };
-
p._updateVolume = function() {
if (this.flashId == null) { return; }
s._flash.setVolume(this.flashId, this._volume)
@@ -1053,13 +1049,17 @@ this.createjs = this.createjs || {};
this.flashId = null;
};
- p._beginPlaying = function (offset, loop, volume, pan) {
+ p._beginPlaying = function (playProps) {
if (s._flash == null) { return false; }
- this.setPosition(offset);
- this.setLoop(loop);
- this.setVolume(volume);
- this.setPan(pan);
+ this.position = playProps.offset;
+ this.loop = playProps.loop;
+ this.volume = playProps.volume;
+ this.pan = playProps.pan;
+ if (playProps.startTime != null) {
+ this.startTime = playProps.startTime;
+ this.duration = playProps.duration;
+ }
this._paused = false;
this.flashId = s._flash.playSound(this.src, this._position, this._loop, this._volume, this._pan, this._startTime, this._duration);
@@ -1141,12 +1141,6 @@ this.createjs = this.createjs || {};
"use strict";
- /**
- * FlashPlugin has been renamed to {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
- * @class FlashPlugin
- * @deprecated
- */
-
/**
* Play sounds using a Flash instance. This plugin is not used by default, and must be registered manually in
* {{#crossLink "Sound"}}{{/crossLink}} using the {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method. This
@@ -1256,17 +1250,6 @@ this.createjs = this.createjs || {};
this._flashPreloadInstances = {};
//TODO consider combining _flashInstances and _flashPreloadInstances into a single hash
- // TODO remove _queuedInstances
- /**
- * An array of Sound Preload instances that are waiting to preload. Once Flash is initialized, the queued
- * instances are preloaded.
- * @property _queuedInstances
- * @type {Object}
- * @protected
- */
- this._queuedInstances = [];
-
-
this._capabilities = s._capabilities;
this._loaderClass = createjs.FlashAudioLoader;
@@ -1296,7 +1279,6 @@ this.createjs = this.createjs || {};
var p = createjs.extend(FlashAudioPlugin, createjs.AbstractPlugin);
var s = FlashAudioPlugin;
-
// Static properties
/**
* Event constant for the "registerFlashID" event for cleaner code.
@@ -1304,7 +1286,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default registerflashid
* @static
- * @protected
+ * @private
*/
s._REG_FLASHID = "registerflashid";
@@ -1314,17 +1296,17 @@ this.createjs = this.createjs || {};
* @type {String}
* @default unregisterflashid
* @static
- * @protected
+ * @private
*/
s._UNREG_FLASHID = "unregisterflashid";
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities"}}{{/crossLink}}
- * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for a list of available
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for a list of available
* capabilities.
* @property _capabilities
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
@@ -1357,11 +1339,11 @@ this.createjs = this.createjs || {};
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -1385,25 +1367,13 @@ this.createjs = this.createjs || {};
//public methods
p.register = function (src, instances) {
- if (!this.flashReady) {
- this._queuedInstances.push(src);
- }
var loader = this.AbstractPlugin_register(src, instances);
loader.addEventListener(s._REG_FLASHID, createjs.proxy(this.registerPreloadInstance, this));
loader.addEventListener(s._UNREG_FLASHID, createjs.proxy(this.unregisterPreloadInstance, this));
return loader;
};
- p.removeSound = function (src) {
- var i = createjs.indexOf(this._queuedInstances, src);
- if(i != -1) {this._queuedInstances.splice(i,1);}
- // NOTE sound cannot be removed from a swf
-
- this.AbstractPlugin_removeSound(src);
- };
-
p.removeAllSounds = function () {
- this._queuedInstances.length = 0;
this._flashInstances = {};
this._flashPreloadInstances = {};
// NOTE sound cannot be removed from a swf
@@ -1445,13 +1415,6 @@ this.createjs = this.createjs || {};
this._loaderClass.setFlash(this._flash);
this._soundInstanceClass.setFlash(this._flash);
-
- // Anything that needed to be preloaded, can now do so.
- for (var i = 0, l = this._queuedInstances.length; i < l; i++) {
- this._flash.register(this._queuedInstances[i]); // NOTE this flash function currently does nothing
- }
- this._queuedInstances.length = 0;
-
};
/**
@@ -1596,7 +1559,6 @@ this.createjs = this.createjs || {};
};
createjs.FlashAudioPlugin = createjs.promote(FlashAudioPlugin, "AbstractPlugin");
- createjs.FlashPlugin = createjs.FlashAudioPlugin; // TODO remove deprecated
}());
//##############################################################################
@@ -1611,7 +1573,7 @@ this.createjs = this.createjs || {};
/**
* The version string for this release.
- * @for FlashPlugin
+ * @for FlashAudioPlugin
* @property version
* @type String
* @static
@@ -1620,11 +1582,11 @@ this.createjs = this.createjs || {};
/**
* The build date for this release in UTC format.
- * @for FlashPlugin
+ * @for FlashAudioPlugin
* @property buildDate
* @type String
* @static
**/
- s.buildDate = /*=date*/"Thu, 11 Dec 2014 23:16:15 GMT"; // injected by build process
+ s.buildDate = /*=date*/"Wed, 21 Jun 2017 16:53:32 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 af26b69b..ab445972 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;da;a++)this._flash.register(this._queuedInstances[a]);this._queuedInstances.length=0},b._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;return this._flash.setMasterVolume(a)},b.registerPreloadInstance=function(a){this._flashPreloadInstances[a.target.flashId]=a.target},b.unregisterPreloadInstance=function(a){delete this._flashPreloadInstances[a.target.flashId]},b.registerSoundInstance=function(a){this._flashInstances[a.target.flashId]=a.target},b.unregisterSoundInstance=function(a){delete this._flashInstances[a.target.flashId]},b.flashLog=function(a){try{this.showOutput&&console.log(a)}catch(b){}},b.handleSoundEvent=function(a,b){var c=this._flashInstances[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){}}},b.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){}}},b.handleEvent=function(a){switch(a){case"ready":this._handleFlashReady()}},b.handleErrorEvent=function(){},createjs.FlashAudioPlugin=createjs.promote(a,"AbstractPlugin"),createjs.FlashPlugin=createjs.FlashAudioPlugin}(),this.createjs=this.createjs||{},function(){var a=createjs.FlashAudioPlugin=createjs.FlashAudioPlugin||{};a.version="NEXT",a.buildDate="Thu, 11 Dec 2014 23:16:15 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, 21 Jun 2017 16:53:32 GMT"}();
\ No newline at end of file
diff --git a/lib/flashaudioplugin-0.6.0.combined.js b/lib/flashaudioplugin.js
similarity index 96%
rename from lib/flashaudioplugin-0.6.0.combined.js
rename to lib/flashaudioplugin.js
index 5001250e..e1f542fa 100644
--- a/lib/flashaudioplugin-0.6.0.combined.js
+++ b/lib/flashaudioplugin.js
@@ -829,7 +829,7 @@ this.createjs = this.createjs || {};
* @protected
*/
function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, false, createjs.AbstractLoader.SOUND);
+ this.AbstractLoader_constructor(loadItem, false, createjs.Types.SOUND);
// Public properties
@@ -844,14 +844,13 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(Loader, createjs.AbstractLoader);
-
// Static Properties
var s = Loader;
/**
* A reference to the Flash instance that gets created.
* @property flash
* @type {Object | Embed}
- * @protected
+ * @private
*/
s._flash = null;
@@ -859,7 +858,7 @@ this.createjs = this.createjs || {};
* A list of loader instances that tried to load before _flash was set
* @property _preloadInstances
* @type {Array}
- * @protected
+ * @private
*/
s._preloadInstances = [];
@@ -1009,23 +1008,20 @@ this.createjs = this.createjs || {};
// Public Methods
+ // TODO change flash.setLoop to mimic remove and add??
+ p.setLoop = function (value) {
+ if(this.flashId!= null) {
+ s._flash.setLoop(this.flashId, value);
+ }
+ this._loop = value;
+ };
+
p.toString = function () {
return "[FlashAudioSoundInstance]"
};
// Private Methods
- // TODO change flash.setLoop to mimic remove and add??
- p._removeLooping = function () {
- if (this.flashId == null) { return; }
- s._flash.setLoop(this.flashId, this._loop);
- };
-
- p._addLooping = function () {
- if (this.flashId == null) { return; }
- s._flash.setLoop(this.flashId, this._loop);
- };
-
p._updateVolume = function() {
if (this.flashId == null) { return; }
s._flash.setVolume(this.flashId, this._volume)
@@ -1053,13 +1049,17 @@ this.createjs = this.createjs || {};
this.flashId = null;
};
- p._beginPlaying = function (offset, loop, volume, pan) {
+ p._beginPlaying = function (playProps) {
if (s._flash == null) { return false; }
- this.setPosition(offset);
- this.setLoop(loop);
- this.setVolume(volume);
- this.setPan(pan);
+ this.position = playProps.offset;
+ this.loop = playProps.loop;
+ this.volume = playProps.volume;
+ this.pan = playProps.pan;
+ if (playProps.startTime != null) {
+ this.startTime = playProps.startTime;
+ this.duration = playProps.duration;
+ }
this._paused = false;
this.flashId = s._flash.playSound(this.src, this._position, this._loop, this._volume, this._pan, this._startTime, this._duration);
@@ -1141,12 +1141,6 @@ this.createjs = this.createjs || {};
"use strict";
- /**
- * FlashPlugin has been renamed to {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
- * @class FlashPlugin
- * @deprecated
- */
-
/**
* Play sounds using a Flash instance. This plugin is not used by default, and must be registered manually in
* {{#crossLink "Sound"}}{{/crossLink}} using the {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method. This
@@ -1256,17 +1250,6 @@ this.createjs = this.createjs || {};
this._flashPreloadInstances = {};
//TODO consider combining _flashInstances and _flashPreloadInstances into a single hash
- // TODO remove _queuedInstances
- /**
- * An array of Sound Preload instances that are waiting to preload. Once Flash is initialized, the queued
- * instances are preloaded.
- * @property _queuedInstances
- * @type {Object}
- * @protected
- */
- this._queuedInstances = [];
-
-
this._capabilities = s._capabilities;
this._loaderClass = createjs.FlashAudioLoader;
@@ -1296,7 +1279,6 @@ this.createjs = this.createjs || {};
var p = createjs.extend(FlashAudioPlugin, createjs.AbstractPlugin);
var s = FlashAudioPlugin;
-
// Static properties
/**
* Event constant for the "registerFlashID" event for cleaner code.
@@ -1304,7 +1286,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default registerflashid
* @static
- * @protected
+ * @private
*/
s._REG_FLASHID = "registerflashid";
@@ -1314,17 +1296,17 @@ this.createjs = this.createjs || {};
* @type {String}
* @default unregisterflashid
* @static
- * @protected
+ * @private
*/
s._UNREG_FLASHID = "unregisterflashid";
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities"}}{{/crossLink}}
- * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for a list of available
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for a list of available
* capabilities.
* @property _capabilities
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
@@ -1357,11 +1339,11 @@ this.createjs = this.createjs || {};
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -1385,25 +1367,13 @@ this.createjs = this.createjs || {};
//public methods
p.register = function (src, instances) {
- if (!this.flashReady) {
- this._queuedInstances.push(src);
- }
var loader = this.AbstractPlugin_register(src, instances);
loader.addEventListener(s._REG_FLASHID, createjs.proxy(this.registerPreloadInstance, this));
loader.addEventListener(s._UNREG_FLASHID, createjs.proxy(this.unregisterPreloadInstance, this));
return loader;
};
- p.removeSound = function (src) {
- var i = createjs.indexOf(this._queuedInstances, src);
- if(i != -1) {this._queuedInstances.splice(i,1);}
- // NOTE sound cannot be removed from a swf
-
- this.AbstractPlugin_removeSound(src);
- };
-
p.removeAllSounds = function () {
- this._queuedInstances.length = 0;
this._flashInstances = {};
this._flashPreloadInstances = {};
// NOTE sound cannot be removed from a swf
@@ -1445,13 +1415,6 @@ this.createjs = this.createjs || {};
this._loaderClass.setFlash(this._flash);
this._soundInstanceClass.setFlash(this._flash);
-
- // Anything that needed to be preloaded, can now do so.
- for (var i = 0, l = this._queuedInstances.length; i < l; i++) {
- this._flash.register(this._queuedInstances[i]); // NOTE this flash function currently does nothing
- }
- this._queuedInstances.length = 0;
-
};
/**
@@ -1596,7 +1559,6 @@ this.createjs = this.createjs || {};
};
createjs.FlashAudioPlugin = createjs.promote(FlashAudioPlugin, "AbstractPlugin");
- createjs.FlashPlugin = createjs.FlashAudioPlugin; // TODO remove deprecated
}());
//##############################################################################
@@ -1611,20 +1573,20 @@ this.createjs = this.createjs || {};
/**
* The version string for this release.
- * @for FlashPlugin
+ * @for FlashAudioPlugin
* @property version
* @type String
* @static
**/
- s.version = /*=version*/"0.6.0"; // injected by build process
+ s.version = /*=version*/"1.0.0"; // injected by build process
/**
* The build date for this release in UTC format.
- * @for FlashPlugin
+ * @for FlashAudioPlugin
* @property buildDate
* @type String
* @static
**/
- s.buildDate = /*=date*/"Thu, 11 Dec 2014 23:32:09 GMT"; // injected by build process
+ s.buildDate = /*=date*/"Thu, 14 Sep 2017 19:47:47 GMT"; // injected by build process
})();
\ No newline at end of file
diff --git a/lib/flashaudioplugin-0.6.0.min.js b/lib/flashaudioplugin.min.js
similarity index 56%
rename from lib/flashaudioplugin-0.6.0.min.js
rename to lib/flashaudioplugin.min.js
index ae74eb55..79a55a7e 100644
--- a/lib/flashaudioplugin-0.6.0.min.js
+++ b/lib/flashaudioplugin.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;da;a++)this._flash.register(this._queuedInstances[a]);this._queuedInstances.length=0},b._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;return this._flash.setMasterVolume(a)},b.registerPreloadInstance=function(a){this._flashPreloadInstances[a.target.flashId]=a.target},b.unregisterPreloadInstance=function(a){delete this._flashPreloadInstances[a.target.flashId]},b.registerSoundInstance=function(a){this._flashInstances[a.target.flashId]=a.target},b.unregisterSoundInstance=function(a){delete this._flashInstances[a.target.flashId]},b.flashLog=function(a){try{this.showOutput&&console.log(a)}catch(b){}},b.handleSoundEvent=function(a,b){var c=this._flashInstances[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){}}},b.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){}}},b.handleEvent=function(a){switch(a){case"ready":this._handleFlashReady()}},b.handleErrorEvent=function(){},createjs.FlashAudioPlugin=createjs.promote(a,"AbstractPlugin"),createjs.FlashPlugin=createjs.FlashAudioPlugin}(),this.createjs=this.createjs||{},function(){var a=createjs.FlashAudioPlugin=createjs.FlashAudioPlugin||{};a.version="0.6.0",a.buildDate="Thu, 11 Dec 2014 23:32:09 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="1.0.0",a.buildDate="Thu, 14 Sep 2017 19:47:47 GMT"}();
\ No newline at end of file
diff --git a/lib/soundjs-0.6.0.min.js b/lib/soundjs-0.6.0.min.js
deleted file mode 100644
index 9ad66c69..00000000
--- a/lib/soundjs-0.6.0.min.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/*!
-* @license SoundJS
-* Visit http://createjs.com/ for documentation, updates and examples.
-*
-* Copyright (c) 2011-2013 gskinner.com, inc.
-*
-* Distributed under the terms of the MIT license.
-* http://www.opensource.org/licenses/mit-license.html
-*
-* This notice shall be included in all copies or substantial portions of the Software.
-*/
-
-/**!
- * SoundJS FlashAudioPlugin also includes swfobject (http://code.google.com/p/swfobject/)
- */
-
-this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="0.6.0",a.buildDate="Thu, 11 Dec 2014 23:32:09 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";var a=Object.defineProperty?!0:!1,b={};try{Object.defineProperty(b,"bar",{get:function(){return this._bar},set:function(a){this._bar=a}})}catch(c){a=!1}createjs.definePropertySupported=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"BrowserDetect cannot be instantiated"}var b=a.agent=window.navigator.userAgent;a.isWindowPhone=b.indexOf("IEMobile")>-1||b.indexOf("Windows Phone")>-1,a.isFirefox=b.indexOf("Firefox")>-1,a.isOpera=null!=window.opera,a.isChrome=b.indexOf("Chrome")>-1,a.isIOS=(b.indexOf("iPod")>-1||b.indexOf("iPhone")>-1||b.indexOf("iPad")>-1)&&!a.isWindowPhone,a.isAndroid=b.indexOf("Android")>-1&&!a.isWindowPhone,a.isBlackberry=b.indexOf("Blackberry")>-1,createjs.BrowserDetect=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this._listeners=null,this._captureListeners=null}var b=a.prototype;a.initialize=function(a){a.addEventListener=b.addEventListener,a.on=b.on,a.removeEventListener=a.off=b.removeEventListener,a.removeAllEventListeners=b.removeAllEventListeners,a.hasEventListener=b.hasEventListener,a.dispatchEvent=b.dispatchEvent,a._dispatchEvent=b._dispatchEvent,a.willTrigger=b.willTrigger},b.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},b.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)},b.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}}},b.off=b.removeEventListener,b.removeAllEventListeners=function(a){a?(this._listeners&&delete this._listeners[a],this._captureListeners&&delete this._captureListeners[a]):this._listeners=this._captureListeners=null},b.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},b.hasEventListener=function(a){var b=this._listeners,c=this._captureListeners;return!!(b&&b[a]||c&&c[a])},b.willTrigger=function(a){for(var b=this;b;){if(b.hasEventListener(a))return!0;b=b.parent}return!1},b.toString=function(){return"[EventDispatcher]"},b._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=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=a.prototype;b.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},b.stopPropagation=function(){this.propagationStopped=!0},b.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},b.remove=function(){this.removed=!0},b.clone=function(){return new a(this.type,this.bubbles,this.cancelable)},b.set=function(a){for(var b in a)this[b]=a[b];return this},b.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var b=createjs.extend(a,createjs.Event);b.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(a,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.Event);b.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(a,"Event")}(window),this.createjs=this.createjs||{},function(){"use strict";function a(){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="Anonymous",this.loadTimeout=8e3}var b=a.prototype={},c=a;c.create=function(b){if("string"==typeof b){var d=new a;return d.src=b,d}if(b instanceof c)return b;if(b instanceof Object)return b;throw new Error("Type not recognized.")},b.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=c}(),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:void 0},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: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 a(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 b=createjs.extend(a,createjs.EventDispatcher),c=a;c.POST="POST",c.GET="GET",c.BINARY="binary",c.CSS="css",c.IMAGE="image",c.JAVASCRIPT="javascript",c.JSON="json",c.JSONP="jsonp",c.MANIFEST="manifest",c.SOUND="sound",c.VIDEO="video",c.SPRITESHEET="spritesheet",c.SVG="svg",c.TEXT="text",c.XML="xml",b.getItem=function(){return this._item},b.getResult=function(a){return a?this._rawResult:this._result},b.getTag=function(){return this._tag},b.setTag=function(a){this._tag=a},b.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()},b.cancel=function(){this.canceled=!0,this.destroy()},b.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()},b.getLoadedItems=function(){return this._loadedItems},b._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},b._createTag=function(){return null},b._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},b._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)}},b._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)}},b._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},b._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},b.resultFormatter=null,b.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)}},b.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},b.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src"}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},b._createTag=function(){},b._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},b._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(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(a){this._item=a},b=createjs.extend(a,createjs.EventDispatcher);b.load=function(){},b.destroy=function(){},b.cancel=function(){},createjs.AbstractRequest=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractRequest);b.load=function(){null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0),this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._tag[this._tagSrcAttribute]=this._item.src},b.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},b._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},b._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},b._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag)},b._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},b._showTag=function(){this._tag.style.visibility=this._startTagVisibility},b._handleStalled=function(){},createjs.TagRequest=createjs.promote(a,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var b=createjs.extend(a,createjs.TagRequest);b.load=function(){this._tag.onstalled=createjs.proxy(this._handleStalled,this),this._tag.onprogress=createjs.proxy(this._handleProgress,this),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},b._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},b._handleStalled=function(){},b._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},b._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._tag.onprogress=null,this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(a,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractRequest);a.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],b.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},b.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},b.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))}},b.setResponseType=function(a){this._request.responseType=a},b.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},b.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},b._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},b._handleLoadStart=function(){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},b._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},b._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},b._handleReadyStateChange=function(){4==this._request.readyState&&this._handleLoad()},b._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"))}},b._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},b._checkError=function(){var a=parseInt(this._request.status);switch(a){case 404:case 0:return new Error(a)}return null},b._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},b._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}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},b._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)},b.toString=function(){return"[PreloadJS XHRRequest]"},createjs.XHRRequest=createjs.promote(a,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractMediaLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},b._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(a,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"Sound cannot be instantiated"}function b(a,b){this.init(a,b)}var c=a;c.INTERRUPT_ANY="any",c.INTERRUPT_EARLY="early",c.INTERRUPT_LATE="late",c.INTERRUPT_NONE="none",c.PLAY_INITED="playInited",c.PLAY_SUCCEEDED="playSucceeded",c.PLAY_INTERRUPTED="playInterrupted",c.PLAY_FINISHED="playFinished",c.PLAY_FAILED="playFailed",c.SUPPORTED_EXTENSIONS=["mp3","ogg","mpeg","wav","m4a","mp4","aiff","wma","mid"],c.EXTENSION_MAP={m4a:"mp4"},c.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,c.defaultInterruptBehavior=c.INTERRUPT_NONE,c.alternateExtensions=[],c.activePlugin=null,c._pluginsRegistered=!1,c._lastID=0,c._masterVolume=1,c._masterMute=!1,c._instances=[],c._idHash={},c._preloadHash={},c.addEventListener=null,c.removeEventListener=null,c.removeAllEventListeners=null,c.dispatchEvent=null,c.hasEventListener=null,c._listeners=null,createjs.EventDispatcher.initialize(c),c.getPreloadHandlers=function(){return{callback:createjs.proxy(c.initLoad,c),types:["sound"],extensions:c.SUPPORTED_EXTENSIONS}},c._handleLoadComplete=function(a){var b=a.target.getItem().src;if(c._preloadHash[b])for(var d=0,e=c._preloadHash[b].length;e>d;d++){var f=c._preloadHash[b][d];if(c._preloadHash[b][d]=!0,c.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,c.dispatchEvent(a)}}},c._handleLoadError=function(a){var b=a.target.getItem().src;if(c._preloadHash[b])for(var d=0,e=c._preloadHash[b].length;e>d;d++){var f=c._preloadHash[b][d];if(c._preloadHash[b][d]=!1,c.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,c.dispatchEvent(a)}}},c._registerPlugin=function(a){return a.isSupported()?(c.activePlugin=new a,!0):!1},c.registerPlugins=function(a){c._pluginsRegistered=!0;for(var b=0,d=a.length;d>b;b++)if(c._registerPlugin(a[b]))return!0;return!1},c.initializeDefaultPlugins=function(){return null!=c.activePlugin?!0:c._pluginsRegistered?!1:c.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},c.isReady=function(){return null!=c.activePlugin},c.getCapabilities=function(){return null==c.activePlugin?null:c.activePlugin._capabilities},c.getCapability=function(a){return null==c.activePlugin?null:c.activePlugin._capabilities[a]},c.initLoad=function(a){return c._registerSound(a)},c._registerSound=function(a){if(!c.initializeDefaultPlugins())return!1;var d=c._parsePath(a.src);if(null==d)return!1;a.src=d.src,a.type="sound";var e=a.data,f=c.activePlugin.defaultNumChannels||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],c._idHash[g.id]={src:a.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)};null!=a.id&&(c._idHash[a.id]={src:a.src});var i=c.activePlugin.register(a,f);return b.create(a.src,f),null!=e&&isNaN(e)?a.data.channels=f||b.maxPerChannel():a.data=f||b.maxPerChannel(),i.type&&(a.type=i.type),i},c.registerSound=function(a,b,d,e){var f={src:a,id:b,data:d};a instanceof Object&&(e=b,f=a),f=createjs.LoadItem.create(f),null!=e&&(f.src=e+a);var g=c._registerSound(f);if(!g)return!1;if(c._preloadHash[f.src]||(c._preloadHash[f.src]=[]),c._preloadHash[f.src].push(f),1==c._preloadHash[f.src].length)g.on("complete",createjs.proxy(this._handleLoadComplete,this)),g.on("error",createjs.proxy(this._handleLoadError,this)),c.activePlugin.preload(g);else if(1==c._preloadHash[f.src][0])return!0;return f},c.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path);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);return c},c.registerManifest=function(a,b){try{console.log("createjs.Sound.registerManifest is deprecated, please use createjs.Sound.registerSounds.")}catch(c){}return this.registerSounds(a,b)},c.removeSound=function(a,d){if(null==c.activePlugin)return!1;a instanceof Object&&(a=a.src),a=c._getSrcById(a).src,null!=d&&(a=d+a);var e=c._parsePath(a);if(null==e)return!1;a=e.src;for(var f in c._idHash)c._idHash[f].src==a&&delete c._idHash[f];return b.removeSrc(a),delete c._preloadHash[a],c.activePlugin.removeSound(a),!0},c.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},c.removeManifest=function(a,b){try{console.log("createjs.Sound.removeManifest is deprecated, please use createjs.Sound.removeSounds.")}catch(d){}return c.removeSounds(a,b)},c.removeAllSounds=function(){c._idHash={},c._preloadHash={},b.removeAll(),c.activePlugin&&c.activePlugin.removeAllSounds()},c.loadComplete=function(a){if(!c.isReady())return!1;var b=c._parsePath(a);return a=b?c._getSrcById(b.src).src:c._getSrcById(a).src,1==c._preloadHash[a][0]},c._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var b=a.match(c.FILE_PATTERN);if(null==b)return!1;for(var d=b[4],e=b[5],f=c.getCapabilities(),g=0;!f[e];)if(e=c.alternateExtensions[g++],g>c.alternateExtensions.length)return null;a=a.replace("."+b[5],"."+e);var h={name:d,src:a,extension:e};return h},c.play=function(a,b,d,e,f,g,h,i,j){b instanceof Object&&(d=b.delay,e=b.offset,f=b.loop,g=b.volume,h=b.pan,i=b.startTime,j=b.duration,b=b.interrupt);var k=c.createInstance(a,i,j),l=c._playInstance(k,b,d,e,f,g,h);return l||k._playFailed(),k},c.createInstance=function(a,d,e){if(!c.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(a,d,e);a=c._getSrcById(a);var f=c._parsePath(a.src),g=null;return null!=f&&null!=f.src?(b.create(f.src),null==d&&(d=a.startTime),g=c.activePlugin.create(f.src,d,e||a.duration)):g=new createjs.DefaultSoundInstance(a,d,e),g.uniqueId=c._lastID++,g},c.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),c._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var b=this._instances,d=0,e=b.length;e>d;d++)b[d].setMasterVolume(a)},c.getVolume=function(){return c._masterVolume},c.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},c.getMute=function(){return this._masterMute},c.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},c._playInstance=function(a,b,d,e,f,g,h){if(b instanceof Object&&(d=b.delay,e=b.offset,f=b.loop,g=b.volume,h=b.pan,b=b.interrupt),b=b||c.defaultInterruptBehavior,null==d&&(d=0),null==e&&(e=a.getPosition()),null==f&&(f=a.loop),null==g&&(g=a.volume),null==h&&(h=a.pan),0==d){var i=c._beginPlaying(a,b,e,f,g,h);if(!i)return!1}else{var j=setTimeout(function(){c._beginPlaying(a,b,e,f,g,h)},d);a.delayTimeoutId=j}return this._instances.push(a),!0},c._beginPlaying=function(a,c,d,e,f,g){if(!b.add(a,c))return!1;var h=a._beginPlaying(d,e,f,g);if(!h){var i=createjs.indexOf(this._instances,a);return i>-1&&this._instances.splice(i,1),!1}return!0},c._getSrcById=function(a){return c._idHash[a]||{src:a}},c._playFinished=function(a){b.remove(a);var c=createjs.indexOf(this._instances,a);c>-1&&this._instances.splice(c,1)},createjs.Sound=a,b.channels={},b.create=function(a,c){var d=b.get(a);return null==d?(b.channels[a]=new b(a,c),!0):!1},b.removeSrc=function(a){var c=b.get(a);return null==c?!1:(c._removeAll(),delete b.channels[a],!0)},b.removeAll=function(){for(var a in b.channels)b.channels[a]._removeAll();b.channels={}},b.add=function(a,c){var d=b.get(a.src);return null==d?!1:d._add(a,c)},b.remove=function(a){var c=b.get(a.src);return null==c?!1:(c._remove(a),!0)},b.maxPerChannel=function(){return d.maxDefault},b.get=function(a){return b.channels[a]};var d=b.prototype;d.constructor=b,d.src=null,d.max=null,d.maxDefault=100,d.length=0,d.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},d._get=function(a){return this._instances[a]},d._add=function(a,b){return this._getSlot(b,a)?(this._instances.push(a),this.length++,!0):!1},d._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1==b?!1:(this._instances.splice(b,1),this.length--,!0)},d._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},d._getSlot=function(b){var c,d;if(b!=a.INTERRUPT_NONE&&(d=this._get(0),null==d))return!0;for(var e=0,f=this.max;f>e;e++){if(c=this._get(e),null==c)return!0;if(c.playState==a.PLAY_FINISHED||c.playState==a.PLAY_INTERRUPTED||c.playState==a.PLAY_FAILED){d=c;break}b!=a.INTERRUPT_NONE&&(b==a.INTERRUPT_EARLY&&c.getPosition()d.getPosition())&&(d=c)}return null!=d?(d._interrupt(),this._remove(d),!0):!1},d.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._startTime=Math.max(0,b||0),this._volume=1,createjs.definePropertySupported&&Object.defineProperty(this,"volume",{get:this.getVolume,set:this.setVolume}),this._pan=0,createjs.definePropertySupported&&Object.defineProperty(this,"pan",{get:this.getPan,set:this.setPan}),this._duration=Math.max(0,c||0),createjs.definePropertySupported&&Object.defineProperty(this,"duration",{get:this.getDuration,set:this.setDuration}),this._playbackResource=null,createjs.definePropertySupported&&Object.defineProperty(this,"playbackResource",{get:this.getPlaybackResource,set:this.setPlaybackResource}),d!==!1&&d!==!0&&this.setPlaybackResource(d),this._position=0,createjs.definePropertySupported&&Object.defineProperty(this,"position",{get:this.getPosition,set:this.setPosition}),this._loop=0,createjs.definePropertySupported&&Object.defineProperty(this,"loop",{get:this.getLoop,set:this.setLoop}),this._muted=!1,createjs.definePropertySupported&&Object.defineProperty(this,"muted",{get:this.getMuted,set:this.setMuted}),this._paused=!1,createjs.definePropertySupported&&Object.defineProperty(this,"paused",{get:this.getPaused,set:this.setPaused})
-},b=createjs.extend(a,createjs.EventDispatcher);b.play=function(a,b,c,d,e,f){return this.playState==createjs.Sound.PLAY_SUCCEEDED?(a instanceof Object&&(c=a.offset,d=a.loop,e=a.volume,f=a.pan),null!=c&&this.setPosition(c),null!=d&&this.setLoop(d),null!=e&&this.setVolume(e),null!=f&&this.setPan(f),void(this._paused&&this.setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,a,b,c,d,e,f),this)},b.pause=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED?!1:(this.setPaused(!0),!0)},b.resume=function(){return this._paused?(this.setPaused(!1),!0):!1},b.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},b.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},b.toString=function(){return"[AbstractSoundInstance]"},b.getPaused=function(){return this._paused},b.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)},b.setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},b.getVolume=function(){return this._volume},b.setMute=function(a){this.setMuted(a)},b.getMute=function(){return this._muted},b.setMuted=function(a){return a===!0||a===!1?(this._muted=a,this._updateVolume(),this):void 0},b.getMuted=function(){return this._muted},b.setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},b.getPan=function(){return this._pan},b.getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED?this._position:this._calculateCurrentPosition()},b.setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},b.getDuration=function(){return this._duration},b.setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},b.setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._setDurationFromSource(),this},b.getPlaybackResource=function(){return this._playbackResource},b.getLoop=function(){return this._loop},b.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},b._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},b._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},b._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},b._beginPlaying=function(a,b,c,d){return this.setPosition(a),this.setLoop(b),this.setVolume(c),this.setPan(d),null!=this._playbackResource&&this._positionc;c++){var e=this._soundInstances[b][c];e.setPlaybackResource(this._audioSources[b])}},b._handlePreloadError=function(){},b._updateVolume=function(){},createjs.AbstractPlugin=a}(),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._handleError,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 a(a,b,d,e){this.AbstractSoundInstance_constructor(a,b,d,e),this.gainNode=c.context.createGain(),this.panNode=c.context.createPanner(),this.panNode.panningModel=c._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 b=createjs.extend(a,createjs.AbstractSoundInstance),c=a;c.context=null,c.destinationNode=null,c._panningModel="equalpower",b.destroy=function(){this.AbstractSoundInstance_destroy(),this.panNode.disconnect(0),this.panNode=null,this.gainNode.disconnect(0),this.gainNode=null},b.toString=function(){return"[WebAudioSoundInstance]"},b._updatePan=function(){this.panNode.setPosition(this._pan,0,-.5)},b._removeLooping=function(){this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext)},b._addLooping=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},b._setDurationFromSource=function(){this._duration=1e3*this.playbackResource.duration},b._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},b._cleanUpAudioNode=function(a){return a&&(a.stop(0),a.disconnect(0),a=null),a},b._handleSoundReady=function(){this.gainNode.connect(c.destinationNode);var a=.001*this._duration,b=.001*this._position;this.sourceNode=this._createAndPlayAudioNode(c.context.currentTime-a,b),this._playbackStartTime=this.sourceNode.startTime-b,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(a-b)),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},b._createAndPlayAudioNode=function(a,b){var d=c.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,b+.001*this._startTime,e-b),d},b._pause=function(){this._position=1e3*(c.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)},b._resume=function(){this._handleSoundReady()},b._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},b._calculateCurrentPosition=function(){return 1e3*(c.context.currentTime-this._playbackStartTime)},b._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},b._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)},b._updateDuration=function(){this._pause(),this._resume()},createjs.WebAudioSoundInstance=createjs.promote(a,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.AbstractPlugin_constructor(),this._panningModel=c._panningModel,this._volume=1,this.context=c.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=c._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var b=createjs.extend(a,createjs.AbstractPlugin),c=a;c._capabilities=null,c._panningModel="equalpower",c.context=null,c.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return"file:"!=location.protocol||a||this._isFileXHRSupported()?(c._generateCapabilities(),null==c.context?!1:!0):!1},c.playEmptySound=function(){var a=c.context.createBufferSource();a.buffer=c.context.createBuffer(1,1,22050),a.connect(c.context.destination),a.start(0,0,0)},c._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},c._generateCapabilities=function(){if(null==c._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;if(null==c.context)if(window.AudioContext)c.context=new AudioContext;else{if(!window.webkitAudioContext)return null;c.context=new webkitAudioContext}c._compatibilitySetUp(),c.playEmptySound(),c._capabilities={panning:!0,volume:!0,tracks:-1};for(var b=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=b.length;f>e;e++){var g=b[e],h=d[g]||g;c._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}c.context.destination.numberOfChannels<2&&(c._capabilities.panning=!1)}},c._compatibilitySetUp=function(){if(c._panningModel="equalpower",!c.context.createGain){c.context.createGain=c.context.createGainNode;var a=c.context.createBufferSource();a.__proto__.start=a.__proto__.noteGrainOn,a.__proto__.stop=a.__proto__.noteOff,c._panningModel=0}},b.toString=function(){return"[WebAudioPlugin]"},b._addPropsToClasses=function(){var a=this._soundInstanceClass;a.context=this.context,a.destinationNode=this.gainNode,a._panningModel=this._panningModel,this._loaderClass.context=this.context},b._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},createjs.WebAudioPlugin=createjs.promote(a,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.src=a,this.length=0,this.available=0,this.tags=[],this.duration=0}var b=a.prototype;b.constructor=a;var c=a;c.tags={},c.get=function(b){var d=c.tags[b];return null==d&&(d=c.tags[b]=new a(b)),d},c.remove=function(a){var b=c.tags[a];return null==b?!1:(b.removeAll(),delete c.tags[a],!0)},c.getInstance=function(a){var b=c.tags[a];return null==b?null:b.get()},c.setInstance=function(a,b){var d=c.tags[a];return null==d?null:d.set(b)},c.getDuration=function(a){var b=c.tags[a];return null==b?0:b.getDuration()},b.add=function(a){this.tags.push(a),this.length++,this.available++},b.removeAll=function(){for(var a;this.length--;)a=this.tags[this.length],a.parentNode&&a.parentNode.removeChild(a),delete this.tags[this.length];this.src=null,this.tags.length=0},b.get=function(){if(0==this.tags.length)return null;this.available=this.tags.length;var a=this.tags.pop();return null==a.parentNode&&document.body.appendChild(a),a},b.set=function(a){var b=createjs.indexOf(this.tags,a);-1==b&&this.tags.push(a),this.available=this.tags.length},b.getDuration=function(){return this.duration||(this.duration=1e3*this.tags[this.tags.length-1].duration),this.duration},b.toString=function(){return"[HTMLAudioTagPool]"},createjs.HTMLAudioTagPool=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractSoundInstance);b.setMasterVolume=function(){this._updateVolume()},b.setMasterMute=function(){this._updateVolume()},b.toString=function(){return"[HTMLAudioSoundInstance]"},b._removeLooping=function(){null!=this._playbackResource&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._addLooping=function(){null==this._playbackResource||this._audioSpriteStopTime||(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.loop=!0)},b._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.setInstance(this.src,a),this._playbackResource=null}},b._beginPlaying=function(a,b,c,d){return this._playbackResource=createjs.HTMLAudioTagPool.getInstance(this.src),this.AbstractSoundInstance__beginPlaying(a,b,c,d)},b._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()},b._handleTagReady=function(){this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),this._handleSoundReady()},b._pause=function(){this._playbackResource.pause()},b._resume=function(){this._playbackResource.play()},b._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)}},b._calculateCurrentPosition=function(){return 1e3*this._playbackResource.currentTime-this._startTime},b._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)}},b._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))},b._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")))},b._handleLoop=function(){0==this._loop&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._updateDuration=function(){this._audioSpriteStopTime=.001*(startTime+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(a,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.AbstractPlugin_constructor(),this.defaultNumChannels=2,this._capabilities=c._capabilities,this._loaderClass=createjs.SoundLoader,this._soundInstanceClass=createjs.HTMLAudioSoundInstance}var b=createjs.extend(a,createjs.AbstractPlugin),c=a;c.MAX_INSTANCES=30,c._AUDIO_READY="canplaythrough",c._AUDIO_ENDED="ended",c._AUDIO_SEEKED="seeked",c._AUDIO_STALLED="stalled",c._TIME_UPDATE="timeupdate",c._capabilities=null,c.enableIOS=!1,c.isSupported=function(){return c._generateCapabilities(),null==c._capabilities?!1:!0},c._generateCapabilities=function(){if(null==c._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;c._capabilities={panning:!0,volume:!0,tracks:-1};for(var b=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=b.length;f>e;e++){var g=b[e],h=d[g]||g;c._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},b.register=function(a,b){for(var c=createjs.HTMLAudioTagPool.get(a.src),d=null,e=0;b>e;e++)d=this._createTag(a.src),c.add(d);var f=this.AbstractPlugin_register(a,b);return f.setTag(d),f},b.removeSound=function(a){this.AbstractPlugin_removeSound(a),createjs.HTMLAudioTagPool.remove(a)},b.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.setPlaybackResource(null),d},b.toString=function(){return"[HTMLAudioPlugin]"},b.setVolume=b.getVolume=b.setMute=null,b._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.HTMLAudioPlugin=createjs.promote(a,"AbstractPlugin")}();
\ No newline at end of file
diff --git a/lib/soundjs-NEXT.combined.js b/lib/soundjs-NEXT.js
similarity index 75%
rename from lib/soundjs-NEXT.combined.js
rename to lib/soundjs-NEXT.js
index dd01b64f..44fc637c 100644
--- a/lib/soundjs-NEXT.combined.js
+++ b/lib/soundjs-NEXT.js
@@ -57,7 +57,7 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.buildDate = /*=date*/"Thu, 11 Dec 2014 23:16:15 GMT"; // injected by build process
+ s.buildDate = /*=date*/"Thu, 12 Oct 2017 16:33:45 GMT"; // injected by build process
})();
@@ -78,13 +78,13 @@ this.createjs = this.createjs||{};
*
* function MySubClass() {}
* createjs.extend(MySubClass, MySuperClass);
- * ClassB.prototype.doSomething = function() { }
+ * MySubClass.prototype.doSomething = function() { }
*
* var foo = new MySubClass();
* console.log(foo instanceof MySuperClass); // true
* console.log(foo.prototype.constructor === MySubClass); // true
*
- * @method extends
+ * @method extend
* @param {Function} subclass The subclass.
* @param {Function} superclass The superclass to extend.
* @return {Function} Returns the subclass's new prototype.
@@ -158,7 +158,49 @@ createjs.promote = function(subclass, prefix) {
};
//##############################################################################
-// IndexOf.js
+// deprecate.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Wraps deprecated methods so they still be used, but throw warnings to developers.
+ *
+ * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
+ *
+ * The recommended approach for deprecated properties is:
+ *
+ * try {
+ * Obj ect.defineProperties(object, {
+ * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
+ * readWriteProp: {
+ * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
+ * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
+ * });
+ * } catch (e) {}
+ *
+ * @method deprecate
+ * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
+ * @param {String} [name=null] The name of the method or property to display in the console warning.
+ * to deprecate properties.
+ * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
+ * logging the warning in the console.
+ */
+createjs.deprecate = function(fallbackMethod, name) {
+ "use strict";
+ return function() {
+ var msg = "Deprecated property or method '"+name+"'. See docs for info.";
+ console && (console.warn ? console.warn(msg) : console.log(msg));
+ return fallbackMethod && fallbackMethod.apply(this, arguments);
+ }
+};
+
+//##############################################################################
+// indexOf.js
//##############################################################################
this.createjs = this.createjs||{};
@@ -190,7 +232,7 @@ createjs.indexOf = function (array, searchElement){
};
//##############################################################################
-// Proxy.js
+// proxy.js
//##############################################################################
this.createjs = this.createjs||{};
@@ -241,49 +283,6 @@ this.createjs = this.createjs||{};
}());
-//##############################################################################
-// definePropertySupported.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-(function() {
- "use strict";
-
- /**
- * Boolean value indicating if Object.defineProperty is supported.
- *
- *
Example
- *
- * if (createjs.definePropertySupported) { // add getter / setter}
- *
- * @property definePropertySupported
- * @type {Boolean}
- * @default true
- */
- var t = Object.defineProperty ? true : false;
-
- // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
- var foo = {};
- try {
- Object.defineProperty(foo, "bar", {
- get: function () {
- return this._bar;
- },
- set: function (value) {
- this._bar = value;
- }
- });
- } catch (e) {
- t = false;
- }
-
- createjs.definePropertySupported = t;
-}());
-
//##############################################################################
// BrowserDetect.js
//##############################################################################
@@ -339,366 +338,381 @@ this.createjs = this.createjs||{};
// EventDispatcher.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * EventDispatcher provides methods for managing queues of event listeners and dispatching events.
- *
- * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the
- * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method.
- *
- * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the
- * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports
- * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent.
- *
- * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier
- * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The
- * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to
- * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}.
- *
- * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}}
- * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also
- * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener.
- *
- *
Example
- * Add EventDispatcher capabilities to the "MyClass" class.
- *
- * EventDispatcher.initialize(MyClass.prototype);
- *
- * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
- *
- * instance.addEventListener("eventName", handlerMethod);
- * function handlerMethod(event) {
- * console.log(event.target + " Was Clicked");
- * }
- *
- * Maintaining proper scope
- * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
- * method to subscribe to events simplifies this.
- *
- * instance.addEventListener("click", function(event) {
- * console.log(instance == this); // false, scope is ambiguous.
- * });
- *
- * instance.on("click", function(event) {
- * console.log(instance == this); // true, "on" uses dispatcher scope by default.
- * });
- *
- * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage scope.
- *
- *
- * @class EventDispatcher
- * @constructor
- **/
- function EventDispatcher() {
-
-
- // private properties:
- /**
- * @protected
- * @property _listeners
- * @type Object
- **/
- this._listeners = null;
-
- /**
- * @protected
- * @property _captureListeners
- * @type Object
- **/
- this._captureListeners = null;
- }
- var p = EventDispatcher.prototype;
-
-
-// static public methods:
- /**
- * Static initializer to mix EventDispatcher methods into a target object or prototype.
- *
- * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
- * EventDispatcher.initialize(myObject); // add to a specific instance
- *
- * @method initialize
- * @static
- * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
- * prototype.
- **/
- EventDispatcher.initialize = function(target) {
- target.addEventListener = p.addEventListener;
- target.on = p.on;
- target.removeEventListener = target.off = p.removeEventListener;
- target.removeAllEventListeners = p.removeAllEventListeners;
- target.hasEventListener = p.hasEventListener;
- target.dispatchEvent = p.dispatchEvent;
- target._dispatchEvent = p._dispatchEvent;
- target.willTrigger = p.willTrigger;
- };
-
-
-// public methods:
- /**
- * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
- * multiple callbacks getting fired.
- *
- *
Example
- *
- * displayObject.addEventListener("click", handleClick);
- * function handleClick(event) {
- * // Click happened.
- * }
- *
- * @method addEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function | Object} Returns the listener for chaining or assignment.
- **/
- p.addEventListener = function(type, listener, useCapture) {
- var listeners;
- if (useCapture) {
- listeners = this._captureListeners = this._captureListeners||{};
- } else {
- listeners = this._listeners = this._listeners||{};
- }
- var arr = listeners[type];
- if (arr) { this.removeEventListener(type, listener, useCapture); }
- arr = listeners[type]; // remove may have deleted the array
- if (!arr) { listeners[type] = [listener]; }
- else { arr.push(listener); }
- return listener;
- };
-
- /**
- * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
- * only run once, associate arbitrary data with the listener, and remove the listener.
- *
- * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
- * The created anonymous function is returned for use with .removeEventListener (or .off).
- *
- *
Example
- *
- * var listener = myBtn.on("click", handleClick, null, false, {count:3});
- * function handleClick(evt, data) {
- * data.count -= 1;
- * console.log(this == myBtn); // true - scope defaults to the dispatcher
- * if (data.count == 0) {
- * alert("clicked 3 times!");
- * myBtn.off("click", listener);
- * // alternately: evt.remove();
- * }
- * }
- *
- * @method on
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
- * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
- * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
- * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
- **/
- p.on = function(type, listener, scope, once, data, useCapture) {
- if (listener.handleEvent) {
- scope = scope||listener;
- listener = listener.handleEvent;
- }
- scope = scope||this;
- return this.addEventListener(type, function(evt) {
- listener.call(scope, evt, data);
- once&&evt.remove();
- }, useCapture);
- };
-
- /**
- * Removes the specified event listener.
- *
- * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
- * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
- * closure will not work.
- *
- *
Example
- *
- * displayObject.removeEventListener("click", handleClick);
- *
- * @method removeEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener The listener function or object.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- **/
- p.removeEventListener = function(type, listener, useCapture) {
- var listeners = useCapture ? this._captureListeners : this._listeners;
- if (!listeners) { return; }
- var arr = listeners[type];
- if (!arr) { return; }
- for (var i=0,l=arr.length; iExample
- *
- * // Remove all listeners
- * displayObject.removeAllEventListeners();
- *
- * // Remove all click listeners
- * displayObject.removeAllEventListeners("click");
- *
- * @method removeAllEventListeners
- * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
- **/
- p.removeAllEventListeners = function(type) {
- if (!type) { this._listeners = this._captureListeners = null; }
- else {
- if (this._listeners) { delete(this._listeners[type]); }
- if (this._captureListeners) { delete(this._captureListeners[type]); }
- }
- };
-
- /**
- * Dispatches the specified event to all listeners.
- *
- *
Example
- *
- * // Use a string event
- * this.dispatchEvent("complete");
- *
- * // Use an Event instance
- * var event = new createjs.Event("progress");
- * this.dispatchEvent(event);
- *
- * @method dispatchEvent
- * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
- * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
- * dispatchEvent will construct an Event instance with the specified type.
- * @return {Boolean} Returns the value of eventObj.defaultPrevented.
- **/
- p.dispatchEvent = function(eventObj) {
- if (typeof eventObj == "string") {
- // won't bubble, so skip everything if there's no listeners:
- var listeners = this._listeners;
- if (!listeners || !listeners[eventObj]) { return false; }
- eventObj = new createjs.Event(eventObj);
- } else if (eventObj.target && eventObj.clone) {
- // redispatching an active event object, so clone it:
- eventObj = eventObj.clone();
- }
- try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
-
- if (!eventObj.bubbles || !this.parent) {
- this._dispatchEvent(eventObj, 2);
- } else {
- var top=this, list=[top];
- while (top.parent) { list.push(top = top.parent); }
- var i, l=list.length;
-
- // capture & atTarget
- for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
- list[i]._dispatchEvent(eventObj, 1+(i==0));
- }
- // bubbling
- for (i=1; iExample
+ * Add EventDispatcher capabilities to the "MyClass" class.
+ *
+ * EventDispatcher.initialize(MyClass.prototype);
+ *
+ * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
+ *
+ * instance.addEventListener("eventName", handlerMethod);
+ * function handlerMethod(event) {
+ * console.log(event.target + " Was Clicked");
+ * }
+ *
+ * Maintaining proper scope
+ * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
+ * method to subscribe to events simplifies this.
+ *
+ * instance.addEventListener("click", function(event) {
+ * console.log(instance == this); // false, scope is ambiguous.
+ * });
+ *
+ * instance.on("click", function(event) {
+ * console.log(instance == this); // true, "on" uses dispatcher scope by default.
+ * });
+ *
+ * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
+ * scope.
+ *
+ * Browser support
+ * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
+ * requires modern browsers (IE9+).
+ *
+ *
+ * @class EventDispatcher
+ * @constructor
+ **/
+ function EventDispatcher() {
+
+
+ // private properties:
+ /**
+ * @protected
+ * @property _listeners
+ * @type Object
+ **/
+ this._listeners = null;
+
+ /**
+ * @protected
+ * @property _captureListeners
+ * @type Object
+ **/
+ this._captureListeners = null;
+ }
+ var p = EventDispatcher.prototype;
+
+// static public methods:
+ /**
+ * Static initializer to mix EventDispatcher methods into a target object or prototype.
+ *
+ * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
+ * EventDispatcher.initialize(myObject); // add to a specific instance
+ *
+ * @method initialize
+ * @static
+ * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
+ * prototype.
+ **/
+ EventDispatcher.initialize = function(target) {
+ target.addEventListener = p.addEventListener;
+ target.on = p.on;
+ target.removeEventListener = target.off = p.removeEventListener;
+ target.removeAllEventListeners = p.removeAllEventListeners;
+ target.hasEventListener = p.hasEventListener;
+ target.dispatchEvent = p.dispatchEvent;
+ target._dispatchEvent = p._dispatchEvent;
+ target.willTrigger = p.willTrigger;
+ };
+
+
+// public methods:
+ /**
+ * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
+ * multiple callbacks getting fired.
+ *
+ *
Example
+ *
+ * displayObject.addEventListener("click", handleClick);
+ * function handleClick(event) {
+ * // Click happened.
+ * }
+ *
+ * @method addEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function | Object} Returns the listener for chaining or assignment.
+ **/
+ p.addEventListener = function(type, listener, useCapture) {
+ var listeners;
+ if (useCapture) {
+ listeners = this._captureListeners = this._captureListeners||{};
+ } else {
+ listeners = this._listeners = this._listeners||{};
+ }
+ var arr = listeners[type];
+ if (arr) { this.removeEventListener(type, listener, useCapture); }
+ arr = listeners[type]; // remove may have deleted the array
+ if (!arr) { listeners[type] = [listener]; }
+ else { arr.push(listener); }
+ return listener;
+ };
+
+ /**
+ * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
+ * only run once, associate arbitrary data with the listener, and remove the listener.
+ *
+ * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
+ * The wrapper function is returned for use with `removeEventListener` (or `off`).
+ *
+ * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
+ * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
+ * to `on` with the same params will create multiple listeners.
+ *
+ *
Example
+ *
+ * var listener = myBtn.on("click", handleClick, null, false, {count:3});
+ * function handleClick(evt, data) {
+ * data.count -= 1;
+ * console.log(this == myBtn); // true - scope defaults to the dispatcher
+ * if (data.count == 0) {
+ * alert("clicked 3 times!");
+ * myBtn.off("click", listener);
+ * // alternately: evt.remove();
+ * }
+ * }
+ *
+ * @method on
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
+ * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
+ * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
+ * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
+ **/
+ p.on = function(type, listener, scope, once, data, useCapture) {
+ if (listener.handleEvent) {
+ scope = scope||listener;
+ listener = listener.handleEvent;
+ }
+ scope = scope||this;
+ return this.addEventListener(type, function(evt) {
+ listener.call(scope, evt, data);
+ once&&evt.remove();
+ }, useCapture);
+ };
+
+ /**
+ * Removes the specified event listener.
+ *
+ * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
+ * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
+ * closure will not work.
+ *
+ *
Example
+ *
+ * displayObject.removeEventListener("click", handleClick);
+ *
+ * @method removeEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.removeEventListener = function(type, listener, useCapture) {
+ var listeners = useCapture ? this._captureListeners : this._listeners;
+ if (!listeners) { return; }
+ var arr = listeners[type];
+ if (!arr) { return; }
+ for (var i=0,l=arr.length; iIMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
+ * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
+ *
+ * @method off
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.off = p.removeEventListener;
+
+ /**
+ * Removes all listeners for the specified type, or all listeners of all types.
+ *
+ *
Example
+ *
+ * // Remove all listeners
+ * displayObject.removeAllEventListeners();
+ *
+ * // Remove all click listeners
+ * displayObject.removeAllEventListeners("click");
+ *
+ * @method removeAllEventListeners
+ * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
+ **/
+ p.removeAllEventListeners = function(type) {
+ if (!type) { this._listeners = this._captureListeners = null; }
+ else {
+ if (this._listeners) { delete(this._listeners[type]); }
+ if (this._captureListeners) { delete(this._captureListeners[type]); }
+ }
+ };
+
+ /**
+ * Dispatches the specified event to all listeners.
+ *
+ *
Example
+ *
+ * // Use a string event
+ * this.dispatchEvent("complete");
+ *
+ * // Use an Event instance
+ * var event = new createjs.Event("progress");
+ * this.dispatchEvent(event);
+ *
+ * @method dispatchEvent
+ * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
+ * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
+ * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
+ * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
+ * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
+ * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
+ * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
+ **/
+ p.dispatchEvent = function(eventObj, bubbles, cancelable) {
+ if (typeof eventObj == "string") {
+ // skip everything if there's no listeners and it doesn't bubble:
+ var listeners = this._listeners;
+ if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
+ eventObj = new createjs.Event(eventObj, bubbles, cancelable);
+ } else if (eventObj.target && eventObj.clone) {
+ // redispatching an active event object, so clone it:
+ eventObj = eventObj.clone();
+ }
+
+ // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
+ try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
+
+ if (!eventObj.bubbles || !this.parent) {
+ this._dispatchEvent(eventObj, 2);
+ } else {
+ var top=this, list=[top];
+ while (top.parent) { list.push(top = top.parent); }
+ var i, l=list.length;
+
+ // capture & atTarget
+ for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
+ list[i]._dispatchEvent(eventObj, 1+(i==0));
+ }
+ // bubbling
+ for (i=1; irequired. The source can either be a
- * string (recommended), or an HTML tag.
+ * 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
@@ -1057,9 +1072,8 @@ this.createjs = this.createjs || {};
this.src = null;
/**
- * 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. See the {{#crossLink "AbstractLoader"}}{{/crossLink}}
- * class for the full list of supported types.
+ * 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
@@ -1104,14 +1118,14 @@ this.createjs = this.createjs || {};
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
+ * The request method used for HTTP calls. Both {{#crossLink "Methods/GET:property"}}{{/crossLink}} or
+ * {{#crossLink "Methods/POST:property"}}{{/crossLink}} request types are supported, and are defined as
* constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @property method
* @type {String}
- * @default get
+ * @default GET
*/
- this.method = createjs.LoadItem.GET;
+ this.method = createjs.Methods.GET;
/**
* An object hash of name/value pairs to send to the server.
@@ -1154,7 +1168,7 @@ this.createjs = this.createjs || {};
* @type {boolean}
* @default Anonymous
*/
- this.crossOrigin = "Anonymous";
+ this.crossOrigin = null;
/**
* The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
@@ -1163,22 +1177,31 @@ this.createjs = this.createjs || {};
* @type {Number}
* @default 8000 (8 seconds)
*/
- this.loadTimeout = 8000;
+ this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
};
var p = LoadItem.prototype = {};
var s = LoadItem;
/**
- * Create/validate a 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
- *
Objectss are returned as-is
+ *
Objects are returned with any needed properties added
*
* @method create
* @param {LoadItem|String|Object} value The load item value
- * @returns {Object|LoadItem}
+ * @returns {LoadItem|Object}
* @static
*/
s.create = function (value) {
@@ -1188,8 +1211,10 @@ this.createjs = this.createjs || {};
return item;
} else if (value instanceof s) {
return value;
- } else if (value instanceof Object) { // Don't modify object, allows users to attach random data to the item.
- // TODO: Disallow objects with no src?
+ } 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.");
@@ -1217,188 +1242,328 @@ this.createjs = this.createjs || {};
}());
//##############################################################################
-// RequestUtils.js
+// Methods.js
//##############################################################################
-(function () {
+this.createjs = this.createjs || {};
- /**
- * Utilities that assist with parsing load items, and determining file types, etc.
- * @class RequestUtils
- */
+(function() {
var s = {};
/**
- * The Regular Expression used to test file URLS for an absolute path.
- * @property ABSOLUTE_PATH
- * @type {RegExp}
+ * Defines a POST request, use for a method value when loading data.
+ * @property POST
+ * @type {string}
+ * @default post
* @static
*/
- s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
+ s.POST = "POST";
/**
- * The Regular Expression used to test file URLS for an absolute path.
- * @property RELATIVE_PATH
- * @type {RegExp}
+ * Defines a GET request, use for a method value when loading data.
+ * @property GET
+ * @type {string}
+ * @default get
* @static
*/
- s.RELATIVE_PATT = (/^[./]*?\//i);
+ s.GET = "GET";
+
+ createjs.Methods = s;
+}());
+
+//##############################################################################
+// Types.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function() {
+ var s = {};
/**
- * 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}
+ * 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.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
+ s.BINARY = "binary";
/**
- * 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.
+ * 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.parseURI = function (path) {
- var info = {absolute: false, relative: false};
- if (path == null) { return info; }
+ s.CSS = "css";
- // 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;
+ /**
+ * The preload type for font files.
+ * @property FONT
+ * @type {String}
+ * @default font
+ * @static
+ * @since 0.9.0
+ */
+ s.FONT = "font";
- // Relative
- } else if (s.RELATIVE_PATT.test(path)) {
- info.relative = true;
- }
+ /**
+ * The preload type for fonts specified with CSS (such as Google fonts)
+ * @property FONTCSS
+ * @type {String}
+ * @default fontcss
+ * @static
+ * @since 0.9.0
+ */
+ s.FONTCSS = "fontcss";
- // Extension
- if (match = path.match(s.EXTENSION_PATT)) {
- info.extension = match[1].toLowerCase();
- }
- return info;
- };
+ /**
+ * 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";
/**
- * 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.
+ * 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.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('&');
- };
+ s.JAVASCRIPT = "javascript";
/**
- * 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.
+ * 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.buildPath = function (src, data) {
- if (data == null) {
- return src;
- }
+ s.JSON = "json";
- var query = [];
- var idx = src.indexOf('?');
+ /**
+ * 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";
- if (idx != -1) {
- var q = src.slice(idx + 1);
- query = query.concat(q.split('&'));
- }
+ /**
+ * 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";
- if (idx != -1) {
- return src.slice(0, idx) + '?' + this._formatQueryString(data, query);
- } else {
- return src + '?' + this._formatQueryString(data, query);
- }
- };
+ /**
+ * 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";
/**
- * @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.
+ * 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.isCrossDomain = function (item) {
- var target = document.createElement("a");
- target.href = item.src;
+ s.VIDEO = "video";
- var host = document.createElement("a");
- host.href = location.href;
+ /**
+ * 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";
- var crossdomain = (target.hostname != "") &&
- (target.port != host.port ||
- target.protocol != host.protocol ||
- target.hostname != host.hostname);
- return crossdomain;
- };
+ /**
+ * The preload type for SVG files.
+ * @property SVG
+ * @type {String}
+ * @default svg
+ * @static
+ * @since 0.6.0
+ */
+ s.SVG = "svg";
/**
- * @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.
+ * 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.isLocal = function (item) {
- var target = document.createElement("a");
- target.href = item.src;
- return target.hostname == "" && target.protocol == "file:";
- };
+ s.TEXT = "text";
/**
- * 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.
+ * 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.isBinary = function (type) {
- switch (type) {
- case createjs.AbstractLoader.IMAGE:
- case createjs.AbstractLoader.BINARY:
- return true;
- default:
- return false;
- }
+ s.XML = "xml";
+
+ createjs.Types = s;
+}());
+
+//##############################################################################
+// Elements.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * Convenience methods for creating various elements used by PrelaodJS.
+ *
+ * @class DomUtils
+ */
+ var s = {};
+
+ s.a = function() {
+ return s.el("a");
+ }
+
+ s.svg = function() {
+ return s.el("svg");
+ }
+
+ s.object = function() {
+ return s.el("object");
+ }
+
+ s.image = function() {
+ return s.el("image");
+ }
+
+ s.img = function() {
+ return s.el("img");
+ }
+
+ s.style = function() {
+ return s.el("style");
+ }
+
+ s.link = function() {
+ return s.el("link");
+ }
+
+ s.script = function() {
+ return s.el("script");
+ }
+
+ s.audio = function() {
+ return s.el("audio");
+ }
+
+ s.video = function() {
+ return s.el("video");
+ }
+
+ s.text = function(value) {
+ return document.createTextNode(value);
+ }
+
+ s.el = function(name) {
+ return document.createElement(name);
+ }
+
+ createjs.Elements = s;
+
+}());
+
+//##############################################################################
+// DomUtils.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * A few utilities for interacting with the dom.
+ * @class DomUtils
+ */
+ var s = {
+ container: null
};
+ s.appendToHead = function (el) {
+ s.getHead().appendChild(el);
+ }
+
+ s.appendToBody = function (el) {
+ if (s.container == null) {
+ s.container = document.createElement("div");
+ s.container.id = "preloadjs-container";
+ var style = s.container.style;
+ style.visibility = "hidden";
+ style.position = "absolute";
+ style.width = s.container.style.height = "10px";
+ style.overflow = "hidden";
+ style.transform = style.msTransform = style.webkitTransform = style.oTransform = "translate(-10px, -10px)"; //LM: Not working
+ s.getBody().appendChild(s.container);
+ }
+ s.container.appendChild(el);
+ }
+
+ s.getHead = function () {
+ return document.head || document.getElementsByTagName("head")[0];
+ }
+
+ s.getBody = function () {
+ return document.body || document.getElementsByTagName("body")[0];
+ }
+
+ s.removeChild = function(el) {
+ if (el.parent) {
+ el.parent.removeChild(el);
+ }
+ }
+
/**
* Check if item is a valid HTMLImageElement
* @method isImageTag
@@ -1428,7 +1593,7 @@ this.createjs = this.createjs || {};
/**
* Check if item is a valid HTMLVideoElement
* @method isVideoTag
- * @param {Objectitem
+ * @param {Object} item
* @returns {Boolean}
* @static
*/
@@ -1436,7 +1601,44 @@ this.createjs = this.createjs || {};
if (window.HTMLVideoElement) {
return item instanceof HTMLVideoElement;
} else {
- false;
+ return false;
+ }
+ };
+
+ createjs.DomUtils = s;
+
+}());
+
+//##############################################################################
+// RequestUtils.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * Utilities that assist with parsing load items, and determining file types, etc.
+ * @class RequestUtils
+ */
+ var s = {};
+
+ /**
+ * 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.Types.IMAGE:
+ case createjs.Types.BINARY:
+ return true;
+ default:
+ return false;
}
};
@@ -1449,13 +1651,14 @@ this.createjs = this.createjs || {};
*/
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.Types.TEXT:
+ case createjs.Types.JSON:
+ case createjs.Types.MANIFEST:
+ case createjs.Types.XML:
+ case createjs.Types.CSS:
+ case createjs.Types.SVG:
+ case createjs.Types.JAVASCRIPT:
+ case createjs.Types.SPRITESHEET:
return true;
default:
return false;
@@ -1463,51 +1666,242 @@ this.createjs = this.createjs || {};
};
/**
- * 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.
+ * 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, `createjs.Types.IMAGE`). Will return `null` if
+ * the type can not be determined by the extension.
+ * @static
+ */
+ s.getTypeByExtension = function (extension) {
+ if (extension == null) {
+ return createjs.Types.TEXT;
+ }
+
+ switch (extension.toLowerCase()) {
+ case "jpeg":
+ case "jpg":
+ case "gif":
+ case "png":
+ case "webp":
+ case "bmp":
+ return createjs.Types.IMAGE;
+ case "ogg":
+ case "mp3":
+ case "webm":
+ return createjs.Types.SOUND;
+ case "mp4":
+ case "webm":
+ case "ts":
+ return createjs.Types.VIDEO;
+ case "json":
+ return createjs.Types.JSON;
+ case "xml":
+ return createjs.Types.XML;
+ case "css":
+ return createjs.Types.CSS;
+ case "js":
+ return createjs.Types.JAVASCRIPT;
+ case 'svg':
+ return createjs.Types.SVG;
+ default:
+ return createjs.Types.TEXT;
+ }
+ };
+
+ createjs.RequestUtils = s;
+
+}());
+
+//##############################################################################
+// URLUtils.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * Utilities that assist with parsing load items, and determining file types, etc.
+ * @class URLUtils
+ */
+ 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,
+ * the pieces of the path (protocol, hostname, port, pathname, search, hash, host)
+ * as well as an optional 'extension` property, which is the lowercase extension.
+ *
+ * @static
+ */
+ s.parseURI = function (path) {
+ var info = {
+ absolute: false,
+ relative: false,
+ protocol: null,
+ hostname: null,
+ port: null,
+ pathname: null,
+ search: null,
+ hash: null,
+ host: null
+ };
+
+ if (path == null) { return info; }
+
+ // Inject the path parts.
+ var parser = createjs.Elements.a();
+ parser.href = path;
+
+ for (var n in info) {
+ if (n in parser) {
+ info[n] = parser[n];
+ }
+ }
+
+ // 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 buildURI
+ * @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.buildURI = 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 = createjs.Elements.a();
+ target.href = item.src;
+
+ var host = createjs.Elements.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.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;
- }
+ s.isLocal = function (item) {
+ var target = createjs.Elements.a();
+ target.href = item.src;
+ return target.hostname == "" && target.protocol == "file:";
};
- createjs.RequestUtils = s;
+ createjs.URLUtils = s;
}());
@@ -1525,11 +1919,11 @@ this.createjs = this.createjs || {};
* 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} The item to be loaded.
+ * @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.
- * @oaram {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
+ * @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
*/
@@ -1553,6 +1947,7 @@ this.createjs = this.createjs || {};
* @property canceled
* @type {Boolean}
* @default false
+ * @readonly
*/
this.canceled = false;
@@ -1587,7 +1982,18 @@ this.createjs = this.createjs || {};
* 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.
+ * asynchronous, such as creating a new image. The callback function is passed 2 parameters, which are callbacks
+ * to handle success and error conditions in the resultFormatter. Note that the resultFormatter method is
+ * called in the current scope, as well as the success and error callbacks.
+ *
+ *
Example asynchronous resultFormatter
+ *
+ * function _formatResult(loader) {
+ * return function(success, error) {
+ * if (errorCondition) { error(errorDetailEvent); }
+ * success(result);
+ * }
+ * }
* @property resultFormatter
* @type {Function}
* @default null
@@ -1665,170 +2071,29 @@ this.createjs = this.createjs || {};
var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
var s = AbstractLoader;
- /**
- * Defines a POST request, use for a method value when loading data.
- * @property POST
- * @type {string}
- * @default post
- */
- s.POST = "POST";
-
- /**
- * Defines a GET request, use for a method value when loading data.
- * @property GET
- * @type {string}
- * @default get
- */
- 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";
+ // Remove these @deprecated properties after 1.0
+ try {
+ Object.defineProperties(s, {
+ POST: { get: createjs.deprecate(function() { return createjs.Methods.POST; }, "AbstractLoader.POST") },
+ GET: { get: createjs.deprecate(function() { return createjs.Methods.GET; }, "AbstractLoader.GET") },
+
+ BINARY: { get: createjs.deprecate(function() { return createjs.Types.BINARY; }, "AbstractLoader.BINARY") },
+ CSS: { get: createjs.deprecate(function() { return createjs.Types.CSS; }, "AbstractLoader.CSS") },
+ FONT: { get: createjs.deprecate(function() { return createjs.Types.FONT; }, "AbstractLoader.FONT") },
+ FONTCSS: { get: createjs.deprecate(function() { return createjs.Types.FONTCSS; }, "AbstractLoader.FONTCSS") },
+ IMAGE: { get: createjs.deprecate(function() { return createjs.Types.IMAGE; }, "AbstractLoader.IMAGE") },
+ JAVASCRIPT: { get: createjs.deprecate(function() { return createjs.Types.JAVASCRIPT; }, "AbstractLoader.JAVASCRIPT") },
+ JSON: { get: createjs.deprecate(function() { return createjs.Types.JSON; }, "AbstractLoader.JSON") },
+ JSONP: { get: createjs.deprecate(function() { return createjs.Types.JSONP; }, "AbstractLoader.JSONP") },
+ MANIFEST: { get: createjs.deprecate(function() { return createjs.Types.MANIFEST; }, "AbstractLoader.MANIFEST") },
+ SOUND: { get: createjs.deprecate(function() { return createjs.Types.SOUND; }, "AbstractLoader.SOUND") },
+ VIDEO: { get: createjs.deprecate(function() { return createjs.Types.VIDEO; }, "AbstractLoader.VIDEO") },
+ SPRITESHEET: { get: createjs.deprecate(function() { return createjs.Types.SPRITESHEET; }, "AbstractLoader.SPRITESHEET") },
+ SVG: { get: createjs.deprecate(function() { return createjs.Types.SVG; }, "AbstractLoader.SVG") },
+ TEXT: { get: createjs.deprecate(function() { return createjs.Types.TEXT; }, "AbstractLoader.TEXT") },
+ XML: { get: createjs.deprecate(function() { return createjs.Types.XML; }, "AbstractLoader.XML") }
+ });
+ } catch (e) {}
// Events
/**
@@ -1867,7 +2132,7 @@ this.createjs = this.createjs || {};
* 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 {String} type The event type ("fileerror")
* @param {LoadItem|object} The item that encountered the error
* @since 0.6.0
*/
@@ -2135,13 +2400,13 @@ this.createjs = this.createjs || {};
* @return {Object} The formatted result
* @since 0.6.0
*/
- p.resultFormatter = null; //TODO: Add support for async formatting.
+ p.resultFormatter = null;
/**
* 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} The event that the internal request dispatches.
+ * @param {Event} event The event that the internal request dispatches.
* @protected
* @since 0.6.0
*/
@@ -2150,12 +2415,13 @@ this.createjs = this.createjs || {};
case "complete":
this._rawResult = event.target._response;
var result = this.resultFormatter && this.resultFormatter(this);
- var _this = this;
+ // The resultFormatter is asynchronous
if (result instanceof Function) {
- result(function(result) {
- _this._result = result;
- _this._sendComplete();
- });
+ result.call(this,
+ createjs.proxy(this._resultFormatSuccess, this),
+ createjs.proxy(this._resultFormatFailed, this)
+ );
+ // The result formatter is synchronous
} else {
this._result = result || this._rawResult;
this._sendComplete();
@@ -2173,20 +2439,33 @@ this.createjs = this.createjs || {};
case "abort":
case "timeout":
if (!this._isCanceled()) {
- this.dispatchEvent(event.type);
+ this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_" + event.type.toUpperCase() + "_ERROR"));
}
break;
}
};
/**
- * @method buildPath
- * @protected
- * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
- * instead.
+ * The "success" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
+ * functions.
+ * @method _resultFormatSuccess
+ * @param {Object} result The formatted result
+ * @private
+ */
+ p._resultFormatSuccess = function (result) {
+ this._result = result;
+ this._sendComplete();
+ };
+
+ /**
+ * The "error" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
+ * functions.
+ * @method _resultFormatSuccess
+ * @param {Object} error The error event
+ * @private
*/
- p.buildPath = function (src, data) {
- return createjs.RequestUtils.buildPath(src, data);
+ p._resultFormatFailed = function (event) {
+ this._sendError(event);
};
/**
@@ -2218,6 +2497,7 @@ this.createjs = this.createjs || {};
* @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) {
@@ -2228,6 +2508,8 @@ this.createjs = this.createjs || {};
// protected properties
this._tagSrcAttribute = "src";
+
+ this.on("initialize", this._updateXHR, this);
};
var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
@@ -2263,6 +2545,20 @@ this.createjs = this.createjs || {};
}
};
+ // protected methods
+ /**
+ * Before the item loads, set its mimeType and responseType.
+ * @property _updateXHR
+ * @param {Event} event
+ * @private
+ */
+ p._updateXHR = function (event) {
+ // Only exists for XHR
+ if (event.loader.setResponseType) {
+ event.loader.setResponseType("blob");
+ }
+ };
+
/**
* The result formatter for media files.
* @method _formatResult
@@ -2274,7 +2570,10 @@ this.createjs = this.createjs || {};
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.onstalled = null;
if (this._preferXHR) {
- loader.getTag().src = loader.getResult(true);
+ var URL = window.URL || window.webkitURL;
+ var result = loader.getResult(true);
+
+ loader.getTag().src = URL.createObjectURL(result);
}
return loader.getTag();
};
@@ -2382,35 +2681,30 @@ this.createjs = this.createjs || {};
*/
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 () {
- if (this._tag.parentNode == null) {
- window.document.body.appendChild(this._tag);
- this._addedToDOM = true;
- }
-
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) {
+ createjs.DomUtils.appendToBody(this._tag);
+ this._addedToDOM = true;
+ }
};
p.destroy = function() {
@@ -2438,6 +2732,16 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * 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
@@ -2448,11 +2752,21 @@ this.createjs = this.createjs || {};
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
@@ -2461,18 +2775,11 @@ this.createjs = this.createjs || {};
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);
}
- };
-
- p._hideTag = function() {
- this._startTagVisibility = this._tag.style.visibility;
- this._tag.style.visibility = "hidden";
- };
-
- p._showTag = function() {
- this._tag.style.visibility = this._startTagVisibility;
+ clearTimeout(this._loadTimeout);
};
/**
@@ -2521,8 +2828,14 @@ this.createjs = this.createjs || {};
// public methods
p.load = function () {
- this._tag.onstalled = createjs.proxy(this._handleStalled, this);
- this._tag.onprogress = createjs.proxy(this._handleProgress, this);
+ 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.
@@ -2565,8 +2878,8 @@ this.createjs = this.createjs || {};
// protected methods
p._clean = function () {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
- this._tag.onstalled = null;
- this._tag.onprogress = null;
+ this._tag.removeEventListener("stalled", this._stalledCallback);
+ this._tag.removeEventListener("progress", this._progressCallback);
this.TagRequest__clean();
};
@@ -2597,7 +2910,7 @@ this.createjs = this.createjs || {};
* for an overview of supported file properties.
* @extends AbstractLoader
*/
- function XHRRequest(item) {
+ function XHRRequest (item) {
this.AbstractRequest_constructor(item);
// protected properties
@@ -2724,15 +3037,28 @@ this.createjs = this.createjs || {};
}
//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);
+ if (this._request.addEventListener != null) {
+ 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);
+ } else {
+ // IE9 support
+ this._request.onloadstart = this._handleLoadStartProxy;
+ this._request.onprogress = this._handleProgressProxy;
+ this._request.onabort = this._handleAbortProxy;
+ this._request.onerror = this._handleErrorProxy;
+ this._request.ontimeout = this._handleTimeoutProxy;
- // 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);
+ // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
+ this._request.onload = this._handleLoadProxy;
+ this._request.onreadystatechange = this._handleReadyStateChangeProxy;
+ }
// Set up a timeout if we don't have XHR2
if (this._xhrLevel == 1) {
@@ -2741,10 +3067,10 @@ this.createjs = this.createjs || {};
// 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) {
+ if (!this._item.values) {
this._request.send();
- } else if (this._item.method == createjs.AbstractLoader.POST) {
- this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
+ } else {
+ this._request.send(createjs.URLUtils.formatQueryString(this._item.values));
}
} catch (error) {
this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
@@ -2752,6 +3078,11 @@ this.createjs = this.createjs || {};
};
p.setResponseType = function (type) {
+ // Some old browsers doesn't support blob, so we convert arraybuffer to blob after response is downloaded
+ if (type === 'blob') {
+ type = window.URL ? 'blob' : 'arraybuffer';
+ this._responseType = type;
+ }
this._request.responseType = type;
};
@@ -2857,6 +3188,9 @@ this.createjs = this.createjs || {};
/**
* 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.
+ *
+ * Note that This method uses {{#crossLink "_checkError"}}{{/crossLink}} to determine if the server has returned an
+ * error code.
* @method _handleLoad
* @param {Object} event The XHR load event.
* @private
@@ -2874,6 +3208,21 @@ this.createjs = this.createjs || {};
}
this._response = this._getResponse();
+ // Convert arraybuffer back to blob
+ if (this._responseType === 'arraybuffer') {
+ try {
+ this._response = new Blob([this._response]);
+ } catch (e) {
+ // Fallback to use BlobBuilder if Blob constructor is not supported
+ // Tested on Android 2.3 ~ 4.2 and iOS5 safari
+ window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
+ if (e.name === 'TypeError' && window.BlobBuilder) {
+ var builder = new BlobBuilder();
+ builder.append(this._response);
+ this._response = builder.getBlob();
+ }
+ }
+ }
this._clean();
this.dispatchEvent(new createjs.Event("complete"));
@@ -2888,30 +3237,36 @@ this.createjs = this.createjs || {};
*/
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.
+ * Determine if there is an error in the current load.
+ * Currently this checks the status of the request for problem codes, and not actual response content:
+ *
+ *
Status codes between 400 and 599 (HTTP error range)
+ *
A status of 0, but *only when the application is running on a server*. If the application is running
+ * on `file:`, then it may incorrectly treat an error on local (or embedded applications) as a successful
+ * load.
+ *
* @method _checkError
- * @return {int} If the request status returns an error code.
+ * @return {Error} An error with the status code in the `message` argument.
* @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);
+ if (status >= 400 && status <= 599) {
+ return new Error(status);
+ } else if (status == 0) {
+ if ((/^https?:/).test(location.protocol)) { return new Error(0); }
+ return null; // Likely an embedded app.
+ } else {
+ return null;
}
- 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.
@@ -2961,7 +3316,7 @@ this.createjs = this.createjs || {};
*/
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 crossdomain = createjs.URLUtils.isCrossDomain(item);
var headers = {};
// Create the request. Fallback to whatever support we have.
@@ -2976,11 +3331,19 @@ this.createjs = this.createjs || {};
for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
var axVersion = s.ACTIVEX_VERSIONS[i];
try {
- req = new ActiveXObject(axVersions);
+ req = new ActiveXObject(axVersion);
break;
- } catch (e) {}
+ } catch (e) {
+ }
+ }
+ if (req == null) {
+ return false;
}
- 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.
@@ -2992,21 +3355,21 @@ this.createjs = this.createjs || {};
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);
+ if (item.method == createjs.Methods.GET) {
+ src = createjs.URLUtils.buildURI(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);
+ req.open(item.method || createjs.Methods.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) {
+ if (item.values && item.method == createjs.Methods.POST) {
headers["Content-Type"] = "application/x-www-form-urlencoded";
}
@@ -3041,13 +3404,23 @@ this.createjs = this.createjs || {};
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);
+ if (this._request.removeEventListener != null) {
+ 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);
+ } else {
+ this._request.onloadstart = null;
+ this._request.onprogress = null;
+ this._request.onabort = null;
+ this._request.onerror = null;
+ this._request.ontimeout = null;
+ this._request.onload = null;
+ this._request.onreadystatechange = null;
+ }
};
p.toString = function () {
@@ -3076,18 +3449,19 @@ this.createjs = this.createjs || {};
* @class SoundLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
+ * @extends AbstractMediaLoader
* @constructor
*/
function SoundLoader(loadItem, preferXHR) {
- this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
+ this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.Types.SOUND);
// protected properties
- if (createjs.RequestUtils.isAudioTag(loadItem)) {
+ if (createjs.DomUtils.isAudioTag(loadItem)) {
this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
+ } else if (createjs.DomUtils.isAudioTag(loadItem.src)) {
this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
- this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
+ } else if (createjs.DomUtils.isAudioTag(loadItem.tag)) {
+ this._tag = createjs.DomUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
}
if (this._tag != null) {
@@ -3101,19 +3475,19 @@ this.createjs = this.createjs || {};
// 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}}.
+ * {{#crossLink "Types/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;
+ return item.type == createjs.Types.SOUND;
};
// protected methods
p._createTag = function (src) {
- var tag = document.createElement("audio");
+ var tag = createjs.Elements.audio();
tag.autoplay = false;
tag.preload = "none";
@@ -3126,6 +3500,239 @@ this.createjs = this.createjs || {};
}());
+//##############################################################################
+// 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
+ */
+
+//##############################################################################
+// 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 props = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
+ * createjs.Sound.play("mySound", props);
+ * // OR
+ * mySoundInstance.play(props);
+ *
+ * @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 (typeof(value) === "string") {
+ // Handle the old API gracefully.
+ console && (console.warn || console.log)("Deprecated behaviour. Sound.play takes a configuration object instead of individual arguments. See docs for info.");
+ return new createjs.PlayPropsConfig().set({interrupt:value});
+ } else if (value == null || value instanceof s || value instanceof Object) {
+ return new createjs.PlayPropsConfig().set(value);
+ } else if (value == null) {
+ throw new Error("PlayProps configuration 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) {
+ if (props != null) {
+ for (var n in props) { this[n] = props[n]; }
+ }
+ return this;
+ };
+
+ p.toString = function() {
+ return "[PlayPropsConfig]";
+ };
+
+ createjs.PlayPropsConfig = s;
+
+}());
+
//##############################################################################
// Sound.js
//##############################################################################
@@ -3164,14 +3771,15 @@ this.createjs = this.createjs || {};
*
*
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.on("fileload", 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.on("complete", this.handleComplete, this);
* instance.volume = 0.5;
* }
*
@@ -3184,37 +3792,20 @@ this.createjs = this.createjs || {};
*
* Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
* automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
- * load. As a result, it may 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.
+ * 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:event"}}{{/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 Audio Sprites, available as of version 0.6.0.
+ * 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.
*
- * 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 until now restricted how many
- * different sounds we could load 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 – 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.
- *
Audio sprites share the same core source, so if you have a sprite with 5 sounds and are limited to 2
- * concurrently playing instances, that means 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: {
@@ -3223,7 +3814,7 @@ this.createjs = this.createjs || {};
* {id:"sound2", startTime:1000, duration:400},
* {id:"sound3", startTime:1700, duration: 1000}
* ]}
- * }
+ * }
* ];
* createjs.Sound.alternateExtensions = ["mp3"];
* createjs.Sound.on("fileload", loadSound);
@@ -3231,26 +3822,60 @@ this.createjs = this.createjs || {};
* // 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.
+ * Mobile Playback
+ * Devices running iOS require the WebAudio context to be "unlocked" by playing at least one sound inside of a user-
+ * initiated event (such as touch/click). Earlier versions of SoundJS included a "MobileSafe" sample, but this is no
+ * longer necessary as of SoundJS 0.6.2.
+ *
+ *
+ * In SoundJS 0.4.1 and above, you can either initialize plugins or use the {{#crossLink "WebAudioPlugin/playEmptySound"}}{{/crossLink}}
+ * method in the call stack of a user input event to manually unlock the audio context.
+ *
+ *
+ * In SoundJS 0.6.2 and above, SoundJS will automatically listen for the first document-level "mousedown"
+ * and "touchend" event, and unlock WebAudio. This will continue to check these events until the WebAudio
+ * context becomes "unlocked" (changes from "suspended" to "running")
+ *
+ *
+ * Both the "mousedown" and "touchend" events can be used to unlock audio in iOS9+, the "touchstart" event
+ * will work in iOS8 and below. The "touchend" event will only work in iOS9 when the gesture is interpreted
+ * as a "click", so if the user long-presses the button, it will no longer work.
+ *
+ *
+ * When using the EaselJS Touch class,
+ * the "mousedown" event will not fire when a canvas is clicked, since MouseEvents are prevented, to ensure
+ * only touch events fire. To get around this, you can either rely on "touchend", or:
+ *
+ *
Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).
+ *
Set the `preventSelection` property on the EaselJS `Stage` to `false`.
+ *
+ * These settings may change how your application behaves, and are not recommended.
+ *
+ *
+ *
+ * Loading Alternate Paths and Extension-less Files
+ * SoundJS supports loading alternate paths and extension-less files by passing an object instead of a string for
+ * the `src` property, which is a hash using the format `{extension:"path", extension2:"path2"}`. These labels are
+ * how SoundJS determines if the browser will support the sound. This also enables multiple formats to live in
+ * different folders, or on CDNs, which often has completely different filenames for each file.
*
- * createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400});
+ * Priority is determined by the property order (first property is tried first). This is supported by both internal loading
+ * and loading with PreloadJS.
*
- * 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.
+ * Note: an id is required for playback.
*
*
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
- * }
+ * var sounds = {path:"./audioPath/",
+ * manifest: [
+ * {id: "cool", src: {mp3:"mp3/awesome.mp3", ogg:"noExtensionOggFile"}}
+ * ]};
+ *
+ * createjs.Sound.alternateExtensions = ["mp3"];
+ * createjs.Sound.addEventListener("fileload", handleLoad);
+ * createjs.Sound.registerSounds(sounds);
*
- *
Known Browser and OS issues
+ *
Known Browser and OS issues
* IE 9 HTML Audio 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
@@ -3259,22 +3884,23 @@ this.createjs = this.createjs || {};
* 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.
+ * 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.
+ * 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.
- *
Note HTMLAudioPlugin is not supported on iOS by default. See {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
- * for more details.
+ *
Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
+ * Mobile Playback above.
+ *
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.
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 can not be used to avoid cross domain issues.
*
* @class Sound
* @static
@@ -3392,10 +4021,11 @@ this.createjs = this.createjs || {};
* 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", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
+ * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
* @since 0.4.0
+ * @static
*/
- s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
+ 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
@@ -3407,6 +4037,7 @@ this.createjs = this.createjs || {};
* @type {Object}
* @since 0.4.0
* @default {m4a:"mp4"}
+ * @static
*/
s.EXTENSION_MAP = {
m4a:"mp4"
@@ -3418,7 +4049,7 @@ this.createjs = this.createjs || {};
* @property FILE_PATTERN
* @type {RegExp}
* @static
- * @protected
+ * @private
*/
s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
@@ -3443,47 +4074,211 @@ this.createjs = this.createjs || {};
* 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.
+ * 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
+ */
+
+ /**
+ * The internal volume level. Use {{#crossLink "Sound/volume:property"}}{{/crossLink}} to adjust the master volume.
+ * @property _masterVolume
+ * @type {number}
+ * @default 1
+ * @private
+ */
+ s._masterVolume = 1;
+
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _getMasterVolume
+ * @private
+ * @static
+ * @return {Number}
+ **/
+ s._getMasterVolume = function() {
+ return this._masterVolume;
+ };
+ // Sound.getMasterVolume is @deprecated. Remove for 1.1+
+ s.getVolume = createjs.deprecate(s._getMasterVolume, "Sound.getVolume");
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _setMasterVolume
+ * @static
+ * @private
+ **/
+ s._setMasterVolume = function(value) {
+ if (Number(value) == null) { return; }
+ 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);
+ }
+ }
+ };
+ // Sound.stMasterVolume is @deprecated. Remove for 1.1+
+ s.setVolume = createjs.deprecate(s._setMasterVolume, "Sound.setVolume");
+
+ /**
+ * 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;
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _getMute
+ * @returns {Boolean}
+ * @static
+ * @private
+ */
+ s._getMute = function () {
+ return this._masterMute;
+ };
+ // Sound.getMute is @deprecated. Remove for 1.1+
+ s.getMute = createjs.deprecate(s._getMute, "Sound.getMute");
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _setMute
+ * @param {Boolean} value The muted value
+ * @static
+ * @private
+ */
+ s._setMute = function (value) {
+ if (value == null) { return; }
+ 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);
+ }
+ }
+ };
+ // Sound.setMute is @deprecated. Remove for 1.1+
+ s.setMute = createjs.deprecate(s._setMute, "Sound.setMute");
+
+ /**
+ * 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 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
+ * var mp3 = createjs.Sound.capabilities.mp3;
*
- * @property alternateExtensions
- * @type {Array}
- * @since 0.5.2
+ * Note this property is read only.
+ *
+ * @property capabilities
+ * @type {Object}
+ * @static
+ * @readOnly
+ * @since 0.6.1
*/
- 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
+ * Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
+ * @returns {null}
+ * @private
*/
- s.activePlugin = null;
+ s._getCapabilities = function() {
+ if (s.activePlugin == null) { return null; }
+ return s.activePlugin._capabilities;
+ };
+ // Sound.getCapabilities is @deprecated. Remove for 1.1+
+ s.getCapabilities = createjs.deprecate(s._getCapabilities, "Sound.getCapabilities");
+
+ Object.defineProperties(s, {
+ volume: { get: s._getMasterVolume, set: s._setMasterVolume },
+ muted: { get: s._getMute, set: s._setMute },
+ capabilities: { get: s._getCapabilities }
+ });
// Class Private properties
/**
- * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
+ * Determines if the plugins have been registered. If false, the first call to {{#crossLink "play"}}{{/crossLink}} 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
+ * @private
*/
s._pluginsRegistered = false;
@@ -3492,42 +4287,19 @@ this.createjs = this.createjs || {};
* @property _lastID
* @type {Number}
* @static
- * @protected
+ * @private
*/
s._lastID = 0;
- /**
- * The master volume value, which affects all sounds. Use {{#crossLink "Sound/getVolume"}}{{/crossLink}} and
- * {{#crossLink "Sound/setVolume"}}{{/crossLink}} to modify the volume of all audio.
- * @property _masterVolume
- * @type {Number}
- * @default 1
- * @protected
- * @since 0.4.0
- */
- s._masterVolume = 1;
-
- /**
- * The master mute value, which affects all sounds. This is applies to all sound instances. This value can be set
- * through {{#crossLink "Sound/setMute"}}{{/crossLink}} and accessed via {{#crossLink "Sound/getMute"}}{{/crossLink}}.
- * @property _masterMute
- * @type {Boolean}
- * @default false
- * @protected
- * @static
- * @since 0.4.0
- */
- s._masterMute = false;
-
/**
* An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
- * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
+ * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/volume:property"}}{{/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
+ * @private
* @static
*/
s._instances = [];
@@ -3536,7 +4308,7 @@ this.createjs = this.createjs || {};
* An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
* @property _idHash
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._idHash = {};
@@ -3547,11 +4319,22 @@ this.createjs = this.createjs || {};
* and data.
* @property _preloadHash
* @type {Object}
- * @protected
+ * @private
* @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}
+ * @private
+ * @static
+ * @since 0.6.1
+ */
+ s._defaultPlayPropsHash = {};
+
// EventDispatcher methods:
s.addEventListener = null;
@@ -3601,9 +4384,9 @@ this.createjs = this.createjs || {};
*
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"}}{{/crossLink}}).
+ *
extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
* @static
- * @protected
+ * @private
*/
s.getPreloadHandlers = function () {
return {
@@ -3617,7 +4400,7 @@ this.createjs = this.createjs || {};
* Used to dispatch fileload events from internal loading.
* @method _handleLoadComplete
* @param event A loader event.
- * @protected
+ * @private
* @static
* @since 0.6.0
*/
@@ -3644,8 +4427,9 @@ this.createjs = this.createjs || {};
/**
* Used to dispatch error events from internal preloading.
* @param event
- * @protected
+ * @private
* @since 0.6.0
+ * @static
*/
s._handleLoadError = function(event) {
var src = event.target.getItem().src;
@@ -3720,6 +4504,7 @@ this.createjs = this.createjs || {};
* @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;}
@@ -3747,63 +4532,17 @@ this.createjs = this.createjs || {};
return (s.activePlugin != null);
};
- /**
- * 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.
- *
- * @method getCapabilities
- * @return {Object} An object containing the capabilities of the active plugin.
- * @static
- */
- s.getCapabilities = function () {
- if (s.activePlugin == null) {return null;}
- return s.activePlugin._capabilities;
- };
-
- /**
- * Get a specific capability of the active plugin. See {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} for a
- * full list of capabilities.
- *
- *
Example
- *
- * var maxAudioInstances = createjs.Sound.getCapability("tracks");
- *
- * @method getCapability
- * @param {String} key The capability to retrieve
- * @return {Number|Boolean} The value of the capability.
- * @static
- * @see getCapabilities
- */
- 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
+ * @private
* @static
*/
s.initLoad = function (loadItem) {
+ if (loadItem.type == "video") { return true; } // Don't handle video. PreloadJS's plugin model is really aggressive.
return s._registerSound(loadItem);
};
@@ -3823,13 +4562,19 @@ this.createjs = this.createjs || {};
s._registerSound = function (loadItem) {
if (!s.initializeDefaultPlugins()) {return false;}
- var details = s._parsePath(loadItem.src);
+ 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 = s.activePlugin.defaultNumChannels || null;
+ var numChannels = null;
if (data != null) {
if (!isNaN(data.channels)) {
numChannels = parseInt(data.channels);
@@ -3842,11 +4587,15 @@ this.createjs = this.createjs || {};
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, numChannels); // Note only HTML audio uses numChannels
+ var loader = s.activePlugin.register(loadItem);
SoundChannel.create(loadItem.src, numChannels);
@@ -3857,9 +4606,11 @@ this.createjs = this.createjs || {};
loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
}
- //if (loader.onload) {details.completeHandler = loader.onload;} // used by preloadJS
if (loader.type) {loadItem.type = loader.type;}
+ if (loadItem.defaultPlayProps) {
+ s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
+ }
return loader;
};
@@ -3873,34 +4624,39 @@ this.createjs = this.createjs || {};
* 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
- * @param {String} [id] An id specified by the user to play the sound later.
+ * @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 audioSprite array of objects in the following format {id, startTime, duration}.
+ * 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) {
- var loadItem = {src: src, id: id, data:data};
- if (src instanceof Object) {
+ 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 = basePath + src;}
+ if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + loadItem.src;}
var loader = s._registerSound(loadItem);
if(!loader) {return false;}
@@ -3909,8 +4665,8 @@ this.createjs = this.createjs || {};
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));
+ loader.on("complete", this._handleLoadComplete, this);
+ loader.on("error", this._handleLoadError, this);
s.activePlugin.preload(loader);
} else {
if (s._preloadHash[loadItem.src][0] == true) {return true;}
@@ -3926,10 +4682,12 @@ this.createjs = this.createjs || {};
*
*
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
@@ -3938,7 +4696,9 @@ this.createjs = this.createjs || {};
* @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 set an optional path property that will be prepended to the src of each object.
+ * 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.
@@ -3955,37 +4715,15 @@ this.createjs = this.createjs || {};
} 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);
+ returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
}
return returnValues;
};
- /**
- * Deprecated. Please use {{#crossLink "Sound/registerSounds"}}{{/crossLink} instead.
- *
- * @method registerManifest
- * @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.
- * @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.
- * @since 0.4.0
- * @depreacted
- */
- s.registerManifest = function(manifest, basePath) {
- try {
- console.log("createjs.Sound.registerManifest is deprecated, please use createjs.Sound.registerSounds.")
- } catch (error) {
-
- };
- return this.registerSounds(manifest, basePath);
- };
-
/**
* Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
@@ -3994,11 +4732,13 @@ this.createjs = this.createjs || {};
*
*
Example
*
- * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
* 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
+ * @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
@@ -4007,13 +4747,18 @@ this.createjs = this.createjs || {};
s.removeSound = function(src, basePath) {
if (s.activePlugin == null) {return false;}
- if (src instanceof Object) {src = src.src;}
- src = s._getSrcById(src).src;
- if (basePath != null) {src = basePath + src;}
+ if (src instanceof Object && src.src) {src = src.src;}
- var details = s._parsePath(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) {
@@ -4039,6 +4784,7 @@ this.createjs = this.createjs || {};
*
*
Example
*
+ * assetPath = "./myPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
@@ -4049,7 +4795,7 @@ this.createjs = this.createjs || {};
* @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 set an optional path property that will be prepended to the src of each object.
+ * 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.
@@ -4064,6 +4810,7 @@ this.createjs = this.createjs || {};
} 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);
@@ -4071,28 +4818,6 @@ this.createjs = this.createjs || {};
return returnValues;
};
- /**
- * Deprecated. Please use {{#crossLink "Sound/removeSounds"}}{{/crossLink}} instead.
- *
- * @method removeManifest
- * @param {Array} manifest An array of objects to remove. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: {srcOrID:srcURIorID}
- * @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 in manifest was
- * successfully removed.
- * @static
- * @since 0.4.1
- * @deprecated
- */
- s.removeManifest = function (manifest, basePath) {
- try {
- console.log("createjs.Sound.removeManifest is deprecated, please use createjs.Sound.removeSounds.");
- } catch (error) {
-
- };
- return s.removeSounds(manifest, basePath);
- };
-
/**
* Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
@@ -4128,6 +4853,7 @@ this.createjs = this.createjs || {};
* @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; }
@@ -4137,17 +4863,19 @@ this.createjs = this.createjs || {};
} 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
+ * 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
+ * @private
+ * @static
*/
s._parsePath = function (value) {
if (typeof(value) != "string") {value = value.toString();}
@@ -4157,7 +4885,7 @@ this.createjs = this.createjs || {};
var name = match[4];
var ext = match[5];
- var c = s.getCapabilities();
+ var c = s.capabilities;
var i = 0;
while (!c[ext]) {
ext = s.alternateExtensions[i++];
@@ -4169,15 +4897,49 @@ this.createjs = this.createjs || {};
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.
+ * @private
+ * @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.
+ * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to
+ * play, an 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 the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
+ * method, 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 still be returned, which will
+ * not play any audio, but will not generate errors.
*
*
Example
*
@@ -4185,49 +4947,25 @@ this.createjs = this.createjs || {};
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* createjs.Sound.play("myID");
- * // we can pass in options we want to set inside of an object, and store off AbstractSoundInstance for controlling
+ * // store off AbstractSoundInstance for controlling
* var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
- * // alternately, we can pass full source path and specify each argument individually
- * var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", createjs.Sound.INTERRUPT_ANY, 0, 0, -1, 1, 0);
* }
*
- * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
+ * NOTE: To create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* @method play
* @param {String} src The src or ID of the audio.
- * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
- * 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}}.
- * OR
- * This parameter can be 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).
- * @param {Number} [delay=0] The amount of time to delay the start of audio playback, in milliseconds.
- * @param {Number} [offset=0] The offset from the start of the audio to begin playback, in milliseconds.
- * @param {Number} [loop=0] How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- * @param {Number} [volume=1] The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- * @param {Number} [pan=0] The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
+ * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
+ * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
+ * @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) {
- if (interrupt instanceof Object) {
- delay = interrupt.delay;
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- startTime = interrupt.startTime;
- duration = interrupt.duration;
- interrupt = interrupt.interrupt;
-
- }
- var instance = s.createInstance(src, startTime, duration);
- var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan);
+ s.play = function (src, props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
+ var instance = s.createInstance(src, playProps.startTime, playProps.duration);
+ var ok = s._playInstance(instance, playProps);
if (!ok) {instance._playFailed();}
return instance;
};
@@ -4256,114 +4994,35 @@ this.createjs = this.createjs || {};
* @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
- */
- s.createInstance = function (src, startTime, duration) {
- if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}
-
- 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);
- } else {
- instance = new createjs.DefaultSoundInstance(src, startTime, duration);;
- }
-
- instance.uniqueId = s._lastID++;
-
- return instance;
- };
-
- /**
- * 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/setVolume"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.setVolume(0.5);
- *
- * @method setVolume
- * @param {Number} value The master volume value. The acceptable range is 0-1.
- * @static
- */
- 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);
- }
- }
- };
-
- /**
- * Get the master volume of Sound. The master volume is multiplied against each sound's individual volume.
- * To get individual sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * var masterVolume = createjs.Sound.getVolume();
- *
- * @method getVolume
- * @return {Number} The master volume, in a range of 0-1.
- * @static
- */
- s.getVolume = function () {
- return s._masterVolume;
- };
-
- /**
- * 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/setMute"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.setMute(true);
- *
- * @method setMute
- * @param {Boolean} value Whether the audio should be muted or not.
- * @return {Boolean} If the mute was set.
- * @static
+ * Unsupported extensions will return the default AbstractSoundInstance.
* @since 0.4.0
+ * @static
*/
- s.setMute = function (value) {
- if (value == null) {return false;}
+ s.createInstance = function (src, startTime, duration) {
+ if (!s.initializeDefaultPlugins()) { return new createjs.DefaultSoundInstance(src, startTime, duration); }
- 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);
+ 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);
}
- return true;
- };
- /**
- * Returns the global mute value. To get the mute value of an individual instance, use AbstractSoundInstance
- * {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * var muted = createjs.Sound.getMute();
- *
- * @method getMute
- * @return {Boolean} The mute value of Sound.
- * @static
- * @since 0.4.0
- */
- s.getMute = function () {
- return this._masterMute;
+ instance.uniqueId = s._lastID++;
+
+ return instance;
};
/**
@@ -4384,6 +5043,34 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * 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
@@ -4393,49 +5080,30 @@ this.createjs = this.createjs || {};
* control delays.
* @method _playInstance
* @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
- * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
- * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior"}}{{/crossLink}}.
- * OR
- * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
- * delay, offset, loop, volume, and pan (see the above code sample).
- * @param {Number} [delay=0] Time in milliseconds before playback begins.
- * @param {Number} [offset=instance.offset] Time into the sound to begin playback in milliseconds. Defaults to the
- * current value on the instance.
- * @param {Number} [loop=0] The number of times to loop the audio. Use 0 for no loops, and -1 for an infinite loop.
- * @param {Number} [volume] The volume of the sound between 0 and 1. Defaults to current instance value.
- * @param {Number} [pan] The pan of the sound between -1 and 1. Defaults to current instance value.
+ * @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
+ * @private
* @static
*/
- s._playInstance = function (instance, interrupt, delay, offset, loop, volume, pan) {
- if (interrupt instanceof Object) {
- delay = interrupt.delay;
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- interrupt = interrupt.interrupt;
- }
-
- interrupt = interrupt || s.defaultInterruptBehavior;
- if (delay == null) {delay = 0;}
- if (offset == null) {offset = instance.getPosition();}
- if (loop == null) {loop = instance.loop;}
- if (volume == null) {volume = instance.volume;}
- if (pan == null) {pan = instance.pan;}
-
- if (delay == 0) {
- var ok = s._beginPlaying(instance, interrupt, offset, loop, volume, pan);
+ 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.position;}
+ 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, interrupt, offset, loop, volume, pan);
- }, delay);
+ s._beginPlaying(instance, playProps);
+ }, playProps.delay);
instance.delayTimeoutId = delayTimeoutId;
}
@@ -4448,24 +5116,17 @@ this.createjs = this.createjs || {};
* 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 {String} [interrupt=none] How this sound interrupts other instances with the same source. Defaults to
- * {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}. Interrupts are defined as INTERRUPT_TYPE
- * constants on Sound.
- * @param {Number} [offset] Time in milliseconds into the sound to begin playback. Defaults to the current value on
- * the instance.
- * @param {Number} [loop=0] The number of times to loop the audio. Use 0 for no loops, and -1 for an infinite loop.
- * @param {Number} [volume] The volume of the sound between 0 and 1. Defaults to the current value on the instance.
- * @param {Number} [pan=instance.pan] The pan of the sound between -1 and 1. Defaults to current instance value.
+ * @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
+ * @private
* @static
*/
- s._beginPlaying = function (instance, interrupt, offset, loop, volume, pan) {
- if (!SoundChannel.add(instance, interrupt)) {
+ s._beginPlaying = function (instance, playProps) {
+ if (!SoundChannel.add(instance, playProps.interrupt)) {
return false;
}
- var result = instance._beginPlaying(offset, loop, volume, pan);
+ var result = instance._beginPlaying(playProps);
if (!result) {
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);}
@@ -4480,7 +5141,7 @@ this.createjs = this.createjs || {};
* @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
+ * @private
* @static
*/
s._getSrcById = function (value) {
@@ -4493,7 +5154,7 @@ this.createjs = this.createjs || {};
* instances themselves.
* @method _playFinished
* @param {AbstractSoundInstance} instance The instance that finished playback.
- * @protected
+ * @private
* @static
*/
s._playFinished = function (instance) {
@@ -4757,8 +5418,8 @@ this.createjs = this.createjs || {};
}
// 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())) {
+ if ((interrupt == Sound.INTERRUPT_EARLY && target.position < replacement.position) ||
+ (interrupt == Sound.INTERRUPT_LATE && target.position > replacement.position)) {
replacement = target;
}
}
@@ -4873,62 +5534,59 @@ this.createjs = this.createjs || {};
// private properties
- /**
- * Audio sprite property used to determine the starting offset.
- * @type {Number}
- * @default null
- * @protected
- */
- this._startTime = Math.max(0, startTime || 0);
- //TODO add a getter / setter for startTime?
-
-
// 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.
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getVolume"}}{{/crossLink}}.
*
* The actual output volume of a sound can be calculated using:
- * myInstance.volume * createjs.Sound.getVolume();
+ * myInstance.volume * createjs.Sound._getVolume();
*
* @property volume
* @type {Number}
* @default 1
*/
this._volume = 1;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "volume", {
- get: this.getVolume,
- set: this.setVolume
- });
- }
+ Object.defineProperty(this, "volume", {
+ get: this._getVolume,
+ set: this._setVolume
+ });
+ this.getVolume = createjs.deprecate(this._getVolume, "AbstractSoundInstance.getVolume");
+ this.setVolume = createjs.deprecate(this._setVolume, "AbstractSoundInstance.setVolume");
/**
* The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPan"}}{{/crossLink}}.
- * Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D 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;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "pan", {
- get: this.getPan,
- set: this.setPan
- });
- }
+ Object.defineProperty(this, "pan", {
+ get: this._getPan,
+ set: this._setPan
+ });
+ this.getPan = createjs.deprecate(this._getPan, "AbstractSoundInstance.getPan");
+ this.setPan = createjs.deprecate(this._setPan, "AbstractSoundInstance.setPan");
/**
- * The length of the audio clip, in milliseconds.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setDuration"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getDuration"}}{{/crossLink}}.
+ * 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
+ });
+ this.getStartTime = createjs.deprecate(this._getStartTime, "AbstractSoundInstance.getStartTime");
+ this.setStartTime = createjs.deprecate(this._setStartTime, "AbstractSoundInstance.setStartTime");
+
+ /**
+ * Sets or gets the length of the audio clip, value is in milliseconds.
*
* @property duration
* @type {Number}
@@ -4936,12 +5594,12 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._duration = Math.max(0, duration || 0);
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "duration", {
- get: this.getDuration,
- set: this.setDuration
- });
- }
+ Object.defineProperty(this, "duration", {
+ get: this._getDuration,
+ set: this._setDuration
+ });
+ this.getDuration = createjs.deprecate(this._getDuration, "AbstractSoundInstance.getDuration");
+ this.setDuration = createjs.deprecate(this._setDuration, "AbstractSoundInstance.setDuration");
/**
* Object that holds plugin specific resource need for audio playback.
@@ -4953,39 +5611,33 @@ this.createjs = this.createjs || {};
* @default null
*/
this._playbackResource = null;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "playbackResource", {
- get: this.getPlaybackResource,
- set: this.setPlaybackResource
- });
- }
- if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
+ Object.defineProperty(this, "playbackResource", {
+ get: this._getPlaybackResource,
+ set: this._setPlaybackResource
+ });
+ if(playbackResource !== false && playbackResource !== true) { this._setPlaybackResource(playbackResource); }
+ this.getPlaybackResource = createjs.deprecate(this._getPlaybackResource, "AbstractSoundInstance.getPlaybackResource");
+ this.setPlaybackResource = createjs.deprecate(this._setPlaybackResource, "AbstractSoundInstance.setPlaybackResource");
/**
* The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPosition"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPosition"}}{{/crossLink}}.
- *
* @property position
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._position = 0;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "position", {
- get: this.getPosition,
- set: this.setPosition
- });
- }
+ Object.defineProperty(this, "position", {
+ get: this._getPosition,
+ set: this._setPosition
+ });
+ this.getPosition = createjs.deprecate(this._getPosition, "AbstractSoundInstance.getPosition");
+ this.setPosition = createjs.deprecate(this._setPosition, "AbstractSoundInstance.setPosition");
/**
* The number of play loops remaining. Negative values will loop infinitely.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setLoop"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getLoop"}}{{/crossLink}}.
- *
* @property loop
* @type {Number}
* @default 0
@@ -4993,18 +5645,15 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._loop = 0;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "loop", {
- get: this.getLoop,
- set: this.setLoop
- });
- }
+ Object.defineProperty(this, "loop", {
+ get: this._getLoop,
+ set: this._setLoop
+ });
+ this.getLoop = createjs.deprecate(this._getLoop, "AbstractSoundInstance.getLoop");
+ this.setLoop = createjs.deprecate(this._setLoop, "AbstractSoundInstance.setLoop");
/**
- * Determines if the audio is currently muted.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setMute"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}}.
+ * Mutes or unmutes the current audio instance.
*
* @property muted
* @type {Boolean}
@@ -5012,30 +5661,26 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._muted = false;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "muted", {
- get: this.getMuted,
- set: this.setMuted
- });
- }
+ Object.defineProperty(this, "muted", {
+ get: this._getMuted,
+ set: this._setMuted
+ });
+ this.getMuted = createjs.deprecate(this._getMuted, "AbstractSoundInstance.getMuted");
+ this.setMuted = createjs.deprecate(this._setMuted, "AbstractSoundInstance.setMuted");
/**
- * Tells you if the audio is currently paused.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower.
- * Use {{#crossLink "AbstractSoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/resume:method"}}{{/crossLink}} to set.
+ * Pauses or resumes the current audio instance.
*
* @property paused
* @type {Boolean}
*/
this._paused = false;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "paused", {
- get: this.getPaused,
- set: this.setPaused
- });
- }
+ Object.defineProperty(this, "paused", {
+ get: this._getPaused,
+ set: this._setPaused
+ });
+ this.getPaused = createjs.deprecate(this._getPaused, "AbstractSoundInstance.getPaused");
+ this.setPaused = createjs.deprecate(this._setPaused, "AbstractSoundInstance.setPaused");
// Events
@@ -5086,7 +5731,6 @@ this.createjs = this.createjs || {};
var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);
-
// Public Methods:
/**
* Play an instance. This method is intended to be called on SoundInstances that already exist (created
@@ -5095,75 +5739,35 @@ this.createjs = this.createjs || {};
*
Example
*
* var myInstance = createjs.Sound.createInstance(mySrc);
- * myInstance.play({offset:1, loop:2, pan:0.5}); // options as object properties
- * myInstance.play(createjs.Sound.INTERRUPT_ANY); // options as parameters
+ * myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
*
- * Note that if this sound is already playing, this call will do nothing.
+ * 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] 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 Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- * OR
- * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
- * delay, offset, loop, volume, and pan (see the above code sample).
- * @param {Number} [delay=0] The delay in milliseconds before the sound starts
- * @param {Number} [offset=0] How far into the sound to begin playback, in milliseconds.
- * @param {Number} [loop=0] The number of times to loop the audio. Use -1 for infinite loops.
- * @param {Number} [volume=1] The volume of the sound, between 0 and 1.
- * @param {Number} [pan=0] The pan of the sound between -1 (left) and 1 (right). Note that pan is not supported
- * for HTML Audio.
+ * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
+ * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
- p.play = function (interrupt, delay, offset, loop, volume, pan) {
+ p.play = function (props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- if (interrupt instanceof Object) {
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- }
- if (offset != null) { this.setPosition(offset) }
- if (loop != null) { this.setLoop(loop); }
- if (volume != null) { this.setVolume(volume); }
- if (pan != null) { this.setPan(pan); }
- if (this._paused) { this.setPaused(false); }
+ this.applyPlayProps(playProps);
+ if (this._paused) { this._setPaused(false); }
return;
}
this._cleanUp();
- createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); // make this an event dispatch??
+ createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
return this;
};
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead.
- *
- * @method pause
- * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing.
- * @deprecated
- */
- p.pause = function () {
- if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;}
- this.setPaused(true);
- return true;
- };
-
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead.
- *
- * @method resume
- * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused.
- * @deprecated
- */
- p.resume = function () {
- if (!this._paused) {return false;}
- this.setPaused(false);
- return true;
- };
-
/**
* 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}}.
+ * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
+ *
+ * If you don't want to lose your position use yourSoundInstance.paused = true instead. {{#crossLink "AbstractSoundInstance/paused"}}{{/crossLink}}.
*
*
Example
*
@@ -5194,37 +5798,50 @@ this.createjs = this.createjs || {};
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
/**
- * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property,
- * and getPaused remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Returns true if the instance is currently paused.
- *
- * @method getPaused
- * @returns {boolean} If the instance is currently paused
+ * Please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property.
+ * @method _getPaused
+ * @protected
+ * @return {boolean} If the instance is currently paused
* @since 0.6.0
*/
- p.getPaused = function() {
+ p._getPaused = function() {
return this._paused;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPaused remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Pause or resume the instance. Note you can also resume playback with {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
- *
+ * Please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
+ * @method _setPaused
+ * @protected
* @param {boolean} value
* @since 0.6.0
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
- p.setPaused = function (value) {
+ 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;
@@ -5238,23 +5855,13 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property,
- * setVolume remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the volume of the instance.
- *
- *
Example
- *
- * myInstance.setVolume(0.5);
- *
- * Note that the master volume set using the Sound API method {{#crossLink "Sound/setVolume"}}{{/crossLink}}
- * will be applied to the instance volume.
- *
- * @method setVolume
- * @param value The volume to set, between 0 and 1.
+ * Please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ * @method _setVolume
+ * @protected
+ * @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) {
+ p._setVolume = function (value) {
if (value == this._volume) { return this; }
this._volume = Math.max(0, Math.min(1, value));
if (!this._muted) {
@@ -5264,59 +5871,24 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property,
- * getVolume remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the volume of the instance. The actual output volume of a sound can be calculated using:
- * myInstance.getVolume() * createjs.Sound.getVolume();
- *
- * @method getVolume
- * @return The current volume of the sound instance.
+ * Please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ * @method _getVolume
+ * @protected
+ * @return {Number} The current volume of the sound instance.
*/
- p.getVolume = function () {
+ p._getVolume = function () {
return this._volume;
};
/**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * @method setMute
- * @param {Boolean} value If the sound should be muted.
- * @return {Boolean} If the mute call succeeds.
- * @deprecated
- */
- p.setMute = function (value) {
- this.setMuted(value);
- };
-
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * @method getMute
- * @return {Boolean} If the sound is muted.
- * @deprecated
- */
- p.getMute = function () {
- return this._muted;
- };
-
- /**
- * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property,
- * setMuted exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be
- * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound muted.
- *
- *
Example
- *
- * myInstance.setMuted(true);
- *
- * @method setMute
+ * Please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ * @method _setMuted
+ * @protected
* @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) {
+ p._setMuted = function (value) {
if (value !== true && value !== false) {return;}
this._muted = value;
this._updateVolume();
@@ -5324,40 +5896,24 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property,
- * getMuted remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the mute value of the instance.
- *
- *
Example
- *
- * var isMuted = myInstance.getMuted();
- *
- * @method getMute
+ * Please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ * @method _getMuted
+ * @protected
* @return {Boolean} If the sound is muted.
* @since 0.6.0
*/
- p.getMuted = function () {
+ p._getMuted = function () {
return this._muted;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPan remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not
- * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
- * The default pan value is 0 (center).
- *
- *
Example
- *
- * myInstance.setPan(-1); // to the left!
- *
- * @method setPan
+ * Please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ * @method _setPan
+ * @protected
* @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) {
+ p._setPan = function (value) {
if(value == this._pan) { return this; }
this._pan = Math.max(-1, Math.min(1, value));
this._updatePan();
@@ -5365,59 +5921,36 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPan remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is
- * actually 3D audio.
- *
- *
Example
- *
- * var myPan = myInstance.getPan();
- *
- * @method getPan
+ * Please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ * @method _getPan
+ * @protected
* @return {Number} The value of the pan, between -1 (left) and 1 (right).
*/
- p.getPan = function () {
+ p._getPan = function () {
return this._pan;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPosition remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the position of the playhead of the instance in milliseconds.
- *
- *
Example
- *
- * var currentOffset = myInstance.getPosition();
- *
- * @method getPosition
+ * Please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ * @method _getPosition
+ * @protected
* @return {Number} The position of the playhead in the sound, in milliseconds.
*/
- p.getPosition = function () {
+ p._getPosition = function () {
if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- return this._calculateCurrentPosition(); // sets this._position
+ this._position = this._calculateCurrentPosition();
}
return this._position;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPosition remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or
- * stopped.
- *
- *
Example
- *
- * myInstance.setPosition(myInstance.getDuration()/2); // set audio to its halfway point.
- *
- * @method setPosition
+ * Please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ * @method _setPosition
+ * @protected
* @param {Number} value The position to place the playhead, in milliseconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
- p.setPosition = function (value) {
+ p._setPosition = function (value) {
this._position = Math.max(0, value);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._updatePosition();
@@ -5426,35 +5959,48 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property,
- * getDuration exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the duration of the instance, in milliseconds.
- * Note a sound needs to be loaded before it will have duration, unless it was set manually to create an audio sprite.
- *
- *
Example
- *
- * var soundDur = myInstance.getDuration();
- *
- * @method getDuration
+ * Please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ * @method _getStartTime
+ * @protected
+ * @return {Number} The startTime of the sound instance in milliseconds.
+ */
+ p._getStartTime = function () {
+ return this._startTime;
+ };
+
+ /**
+ * Please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ * @method _setStartTime
+ * @protected
+ * @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;
+ };
+
+ /**
+ * Please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ * @method _getDuration
+ * @protected
* @return {Number} The duration of the sound instance in milliseconds.
*/
- p.getDuration = function () {
+ p._getDuration = function () {
return this._duration;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property,
- * setDuration exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the duration of the audio. Generally this is not called, but it can be used to create an audio sprite out of an existing AbstractSoundInstance.
- *
- * @method setDuration
+ * Please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ * @method _setDuration
+ * @protected
* @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) {
+ p._setDuration = function (value) {
if (value == this._duration) { return this; }
this._duration = Math.max(0, value || 0);
this._updateDuration();
@@ -5462,70 +6008,57 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPlaybackResource exists to allow support for IE8 with FlashAudioPlugin.
- *
- * An object containing any resources needed for audio playback, set by the plugin.
- * Only meant for use by advanced users.
- *
- * @method setPlayback
+ * Please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ * @method _setPlaybackResource
+ * @protected
* @param {Object} value The new playback resource.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
**/
- p.setPlaybackResource = function (value) {
+ p._setPlaybackResource = function (value) {
this._playbackResource = value;
- if (this._duration == 0) { this._setDurationFromSource(); }
+ if (this._duration == 0 && this._playbackResource) { this._setDurationFromSource(); }
return this;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPlaybackResource exists to allow support for IE8 with FlashAudioPlugin.
- *
- * An object containing any resources needed for audio playback, usually set by the plugin.
- *
- * @method setPlayback
+ * Please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ * @method _getPlaybackResource
+ * @protected
* @param {Object} value The new playback resource.
* @return {Object} playback resource used for playing audio
* @since 0.6.0
**/
- p.getPlaybackResource = function () {
+ p._getPlaybackResource = function () {
return this._playbackResource;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property,
- * getLoop exists to allow support for IE8 with FlashAudioPlugin.
- *
- * The number of play loops remaining. Negative values will loop infinitely.
- *
- * @method getLoop
+ * Please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ * @method _getLoop
+ * @protected
* @return {number}
* @since 0.6.0
**/
- p.getLoop = function () {
+ p._getLoop = function () {
return this._loop;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property,
- * setLoop exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the number of play loops remaining.
- *
- * @method setLoop
+ * Please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ * @method _setLoop
+ * @protected
* @param {number} value The number of times to loop after play.
* @since 0.6.0
*/
- p.setLoop = function (value) {
+ p._setLoop = function (value) {
if(this._playbackResource != null) {
// remove looping
if (this._loop != 0 && value == 0) {
this._removeLooping(value);
}
// add looping
- if (this._loop == 0 && value != 0) {
+ else if (this._loop == 0 && value != 0) {
this._addLooping(value);
}
}
@@ -5573,18 +6106,20 @@ this.createjs = this.createjs || {};
* 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 {Number} offset How far into the sound to begin playback, in milliseconds.
- * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops.
- * @param {Number} volume The volume of the sound, between 0 and 1.
- * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio.
+ * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If playback succeeded.
* @protected
*/
- p._beginPlaying = function (offset, loop, volume, pan) {
- this.setPosition(offset);
- this.setLoop(loop);
- this.setVolume(volume);
- this.setPan(pan);
+ // 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;
@@ -5661,6 +6196,16 @@ this.createjs = this.createjs || {};
// 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
@@ -5682,8 +6227,8 @@ this.createjs = this.createjs || {};
};
/**
- * Internal function that calculates the current position of the playhead and sets it on this._position
- * @method _updatePosition
+ * Internal function that calculates the current position of the playhead and sets this._position to that value
+ * @method _calculateCurrentPosition
* @protected
* @since 0.6.0
*/
@@ -5704,20 +6249,22 @@ this.createjs = this.createjs || {};
/**
* 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 () {
+ 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 () {
+ p._addLooping = function (value) {
// plugin specific code
};
@@ -5763,7 +6310,7 @@ this.createjs = this.createjs || {};
/**
* Internal function called when AbstractSoundInstance has played to end and is looping
- * @method _handleCleanUp
+ * @method _handleLoop
* @protected
* @since 0.6.0
*/
@@ -5831,6 +6378,15 @@ this.createjs = this.createjs || {};
*/
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}
@@ -5847,16 +6403,14 @@ this.createjs = this.createjs || {};
};
var p = AbstractPlugin.prototype;
-
// Static Properties:
// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
/**
- * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
- * method and is used internally.
+ * The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
- * @protected
+ * @private
* @static
*/
AbstractPlugin._capabilities = null;
@@ -5879,17 +6433,17 @@ this.createjs = this.createjs || {};
* can use to assist with preloading.
* @method register
* @param {String} loadItem An Object containing the source of the audio
- * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time.
* Note that not every plugin will manage this value.
* @return {Object} A result object, containing a "tag" for preloading purposes.
*/
- p.register = function (loadItem, instances) {
+ 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] = [];
- if(this._loaders[loadItem.src]) {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?
- var loader = new this._loaderClass(loadItem);
- loader.on("complete", createjs.proxy(this._handlePreloadComplete, this));
+ loader = new this._loaderClass(loadItem);
+ loader.on("complete", this._handlePreloadComplete, this);
this._loaders[loadItem.src] = loader;
return loader;
};
@@ -5901,7 +6455,7 @@ this.createjs = this.createjs || {};
* @param {Loader} loader The sound URI to load.
*/
p.preload = function (loader) {
- loader.on("error", createjs.proxy(this._handlePreloadError, this));
+ loader.on("error", this._handlePreloadError, this);
loader.load();
};
@@ -5967,12 +6521,18 @@ this.createjs = this.createjs || {};
this.preload(this.register(src));
}
var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
- this._soundInstances[src].push(si);
+ if(this._soundInstances[src]){
+ this._soundInstances[src].push(si);
+ }
+
+ // Plugins that don't have a setVolume should implement a setMasterVolune/setMasterMute
+ // So we have to check that here.
+ si.setMasterVolume && si.setMasterVolume(createjs.Sound.volume);
+ si.setMasterMute && si.setMasterMute(createjs.Sound.muted);
+
return si;
};
- // TODO Volume & mute Getter / Setter??
- // TODO change calls to return nothing or this for chaining??
// 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.
@@ -5990,7 +6550,7 @@ this.createjs = this.createjs || {};
/**
* Get the master volume of the plugin, which affects all SoundInstances.
* @method getVolume
- * @return The volume level, between 0 and 1.
+ * @return {Number} The volume level, between 0 and 1.
*/
p.getVolume = function () {
return this._volume;
@@ -6000,7 +6560,7 @@ this.createjs = this.createjs || {};
* 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.
+ * the mute value of Sound {{#crossLink "Sound/muted:property"}}{{/crossLink}}, so this property is not used here.
* @return {Boolean} If the mute call succeeds.
*/
p.setMute = function (value) {
@@ -6018,6 +6578,7 @@ this.createjs = this.createjs || {};
/**
* Handles internal preload completion.
* @method _handlePreloadComplete
+ * @param event
* @protected
*/
p._handlePreloadComplete = function (event) {
@@ -6025,13 +6586,14 @@ this.createjs = this.createjs || {};
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]);
+ item.playbackResource = this._audioSources[src];
// ToDo consider adding play call here if playstate == playfailed
+ this._soundInstances[src] = null;
}
};
/**
- * Handles internal preload erros
+ * Handles internal preload errors
* @method _handlePreloadError
* @param event
* @protected
@@ -6067,12 +6629,11 @@ this.createjs = this.createjs || {};
*
* @class WebAudioLoader
* @param {String} loadItem The item to be loaded
- * @param {Object} flash The flash instance that will do the preloading.
* @extends XHRRequest
* @protected
*/
function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
+ this.AbstractLoader_constructor(loadItem, true, createjs.Types.SOUND);
};
var p = createjs.extend(Loader, createjs.AbstractLoader);
@@ -6102,14 +6663,14 @@ this.createjs = this.createjs || {};
// 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._handleError, this));
+ createjs.proxy(this._sendError, this));
};
/**
* The audio has been decoded.
* @method handleAudioDecoded
- * @param decoded
+ * @param decoded
* @protected
*/
p._handleAudioDecoded = function (decodedAudio) {
@@ -6168,6 +6729,7 @@ this.createjs = this.createjs || {};
this.panNode = s.context.createPanner();
this.panNode.panningModel = s._panningModel;
this.panNode.connect(this.gainNode);
+ this._updatePan();
/**
* NOTE this is only intended for use by advanced users.
@@ -6231,6 +6793,16 @@ this.createjs = this.createjs || {};
*/
s.context = null;
+ /**
+ * Note this is only intended for use by advanced users.
+ * The scratch buffer that will be assigned to the buffer property of a source node on close.
+ * This is and should be the same scratch buffer referenced by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
+ * @property _scratchBuffer
+ * @type {AudioBufferSourceNode}
+ * @static
+ */
+ s._scratchBuffer = null;
+
/**
* Note this is only intended for use by advanced users.
* Audio node from WebAudioPlugin that sequences to context.destination
@@ -6273,11 +6845,11 @@ this.createjs = this.createjs || {};
// z need to be -0.5 otherwise the sound only plays in left, right, or center
};
- p._removeLooping = function() {
+ p._removeLooping = function(value) {
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
- p._addLooping = function() {
+ p._addLooping = function(value) {
if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
};
@@ -6297,7 +6869,7 @@ this.createjs = this.createjs || {};
clearTimeout(this._soundCompleteTimeout);
- this._playbackStartTime = 0; // This is used by getPosition
+ this._playbackStartTime = 0; // This is used by _getPosition
};
/**
@@ -6312,6 +6884,11 @@ this.createjs = this.createjs || {};
if(audioNode) {
audioNode.stop(0);
audioNode.disconnect(0);
+ // necessary to prevent leak on iOS Safari 7-9. will throw in almost all other
+ // browser implementations.
+ if ( createjs.BrowserDetect.isIOS ) {
+ try { audioNode.buffer = s._scratchBuffer; } catch(e) {}
+ }
audioNode = null;
}
return audioNode;
@@ -6320,8 +6897,8 @@ this.createjs = this.createjs || {};
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
- var dur = this._duration * 0.001;
- var pos = this._position * 0.001;
+ var dur = this._duration * 0.001,
+ pos = Math.min(Math.max(0, this._position) * 0.001, dur);
this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
@@ -6403,8 +6980,10 @@ this.createjs = this.createjs || {};
};
p._updateDuration = function () {
- this._pause();
- this._resume();
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._pause();
+ this._resume();
+ }
};
createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
@@ -6427,18 +7006,29 @@ this.createjs = this.createjs || {};
*
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.
- *
+ *
+ * mp3 audio files do not load properly on all windows machines, reported here.
+ * For this reason it is recommended to pass another FireFox-supported type (i.e. ogg) as the default
+ * extension, until this bug is resolved
+ *
+ *
* 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.
- *
+ *
+ * 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.
+ *
+ *
+ * Sound is initially muted and will only unmute through play being called inside a user initiated event
+ * (touch/click). Please read the mobile playback notes in the the {{#crossLink "Sound"}}{{/crossLink}}
+ * class for a full overview of the limitations, and how to get around them.
+ *
+ *
+ * A bug exists that will distort un-cached 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 sample rate.
+ *
*
* @class WebAudioPlugin
* @extends AbstractPlugin
@@ -6458,15 +7048,6 @@ this.createjs = this.createjs || {};
*/
this._panningModel = s._panningModel;;
- /**
- * The internal master volume value of the plugin.
- * @property _volume
- * @type {Number}
- * @default 1
- * @protected
- */
- this._volume = 1;
-
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
@@ -6506,7 +7087,6 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);
-
// Static Properties
var s = WebAudioPlugin;
/**
@@ -6515,7 +7095,7 @@ this.createjs = this.createjs || {};
* @property _capabilities
* @type {Object}
* @default null
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
@@ -6524,7 +7104,7 @@ this.createjs = this.createjs || {};
* 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
+ * @private
* @static
*/
s._panningModel = "equalpower";
@@ -6542,6 +7122,38 @@ this.createjs = this.createjs || {};
*/
s.context = null;
+ /**
+ * The scratch buffer that will be assigned to the buffer property of a source node on close.
+ * Works around an iOS Safari bug: https://github.com/CreateJS/SoundJS/issues/102
+ *
+ * Advanced users can set this to an existing source node, but must do so before they call
+ * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
+ *
+ * @property _scratchBuffer
+ * @type {AudioBuffer}
+ * @private
+ * @static
+ */
+ s._scratchBuffer = null;
+
+ /**
+ * Indicated whether audio on iOS has been unlocked, which requires a touchend/mousedown event that plays an
+ * empty sound.
+ * @property _unlocked
+ * @type {boolean}
+ * @since 0.6.2
+ * @private
+ */
+ s._unlocked = false;
+
+ /**
+ * The default sample rate used when checking for iOS compatibility. See {{#crossLink "WebAudioPlugin/_createAudioContext"}}{{/crossLink}}.
+ * @property DEFAULT_SAMPLE_REATE
+ * @type {number}
+ * @default 44100
+ * @static
+ */
+ s.DEFAULT_SAMPLE_RATE = 44100;
// Static Public Methods
/**
@@ -6577,8 +7189,9 @@ this.createjs = this.createjs || {};
* @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.buffer = s._scratchBuffer;
source.connect(s.context.destination);
source.start(0, 0, 0);
};
@@ -6590,7 +7203,7 @@ this.createjs = this.createjs || {};
* @method _isFileXHRSupported
* @return {Boolean} If XHR is supported.
* @since 0.4.2
- * @protected
+ * @private
* @static
*/
s._isFileXHRSupported = function() {
@@ -6619,11 +7232,11 @@ this.createjs = this.createjs || {};
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -6632,19 +7245,22 @@ this.createjs = this.createjs || {};
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.context = s._createAudioContext();
+ if (s.context == null) { return null; }
+ }
+ if (s._scratchBuffer == null) {
+ s._scratchBuffer = s.context.createBuffer(1, 1, 22050);
}
s._compatibilitySetUp();
- // playing this inside of a touch event will enable audio on iOS, which starts muted
- s.playEmptySound();
+ // Listen for document level clicks to unlock WebAudio on iOS. See the _unlock method.
+ if ("ontouchstart" in window && s.context.state != "running") {
+ s._unlock(); // When played inside of a touch event, this will enable audio on iOS immediately.
+ document.addEventListener("mousedown", s._unlock, true);
+ document.addEventListener("touchstart", s._unlock, true);
+ document.addEventListener("touchend", s._unlock, true);
+ }
s._capabilities = {
panning:true,
@@ -6668,6 +7284,43 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * Create an audio context for the sound.
+ *
+ * This method handles both vendor prefixes (specifically webkit support), as well as a case on iOS where
+ * audio played with a different sample rate may play garbled when first started. The default sample rate is
+ * 44,100, however it can be changed using the {{#crossLink "WebAudioPlugin/DEFAULT_SAMPLE_RATE:property"}}{{/crossLink}}.
+ * @method _createAudioContext
+ * @return {AudioContext | webkitAudioContext}
+ * @private
+ * @static
+ * @since 1.0.0
+ */
+ s._createAudioContext = function() {
+ // Slightly modified version of https://github.com/Jam3/ios-safe-audio-context
+ // Resolves issues with first-run contexts playing garbled on iOS.
+ var AudioCtor = (window.AudioContext || window.webkitAudioContext);
+ if (AudioCtor == null) { return null; }
+ var context = new AudioCtor();
+
+ // Check if hack is necessary. Only occurs in iOS6+ devices
+ // and only when you first boot the iPhone, or play a audio/video
+ // with a different sample rate
+ if (/(iPhone|iPad)/i.test(navigator.userAgent)
+ && context.sampleRate !== s.DEFAULT_SAMPLE_RATE) {
+ var buffer = context.createBuffer(1, 1, s.DEFAULT_SAMPLE_RATE),
+ dummy = context.createBufferSource();
+ dummy.buffer = buffer;
+ dummy.connect(context.destination);
+ dummy.start(0);
+ dummy.disconnect();
+ context.close() // dispose old context
+
+ context = new AudioCtor();
+ }
+ return context;
+ }
+
/**
* Set up compatibility if only deprecated web audio calls are supported.
* See http://www.w3.org/TR/webaudio/#DeprecationNotes
@@ -6676,7 +7329,7 @@ this.createjs = this.createjs || {};
*
* @method _compatibilitySetUp
* @static
- * @protected
+ * @private
* @since 0.4.2
*/
s._compatibilitySetUp = function() {
@@ -6696,6 +7349,29 @@ this.createjs = this.createjs || {};
s._panningModel = 0;
};
+ /**
+ * Try to unlock audio on iOS. This is triggered from either WebAudio plugin setup (which will work if inside of
+ * a `mousedown` or `touchend` event stack), or the first document touchend/mousedown event. If it fails (touchend
+ * will fail if the user presses for too long, indicating a scroll event instead of a click event.
+ *
+ * Note that earlier versions of iOS supported `touchstart` for this, but iOS9 removed this functionality. Adding
+ * a `touchstart` event to support older platforms may preclude a `mousedown` even from getting fired on iOS9, so we
+ * stick with `mousedown` and `touchend`.
+ * @method _unlock
+ * @since 0.6.2
+ * @private
+ */
+ s._unlock = function() {
+ if (s._unlocked) { return; }
+ s.playEmptySound();
+ if (s.context.state == "running") {
+ document.removeEventListener("mousedown", s._unlock, true);
+ document.removeEventListener("touchend", s._unlock, true);
+ document.removeEventListener("touchstart", s._unlock, true);
+ s._unlocked = true;
+ }
+ };
+
// Public Methods
p.toString = function () {
@@ -6714,6 +7390,7 @@ this.createjs = this.createjs || {};
p._addPropsToClasses = function() {
var c = this._soundInstanceClass;
c.context = this.context;
+ c._scratchBuffer = s._scratchBuffer;
c.destinationNode = this.gainNode;
c._panningModel = this._panningModel;
@@ -6742,192 +7419,158 @@ this.createjs = this.createjs || {};
this.createjs = this.createjs || {};
-//TODO verify that tags no longer need to be precreated (mac and pc)
-//TODO modify this now that tags do not need to be precreated
(function () {
"use strict";
/**
- * The TagPool is an object pool for HTMLAudio tag instances. In Chrome, we have to pre-create the number of HTML
- * audio tag instances that we are going to play before we load the data, otherwise the audio stalls.
- * (Note: This seems to be a bug in Chrome)
+ * HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
* @class HTMLAudioTagPool
* @param {String} src The source of the channel.
* @protected
*/
- function TagPool(src) {
-
-
-//Public Properties
- /**
- * The source of the tag pool.
- * #property src
- * @type {String}
- * @protected
- */
- this.src = src;
-
- /**
- * The total number of HTMLAudio tags in this pool. This is the maximum number of instance of a certain sound
- * that can play at one time.
- * #property length
- * @type {Number}
- * @default 0
- * @protected
- */
- this.length = 0;
-
- /**
- * The number of unused HTMLAudio tags.
- * #property available
- * @type {Number}
- * @default 0
- * @protected
- */
- this.available = 0;
-
- /**
- * A list of all available tags in the pool.
- * #property tags
- * @type {Array}
- * @protected
- */
- this.tags = [];
-
- /**
- * The duration property of all audio tags, converted to milliseconds, which originally is only available on the
- * last tag in the tags array because that is the one that is loaded.
- * #property
- * @type {Number}
- * @protected
- */
- this.duration = 0;
- };
-
- var p = TagPool.prototype;
- p.constructor = TagPool;
- var s = TagPool;
+ function HTMLAudioTagPool() {
+ throw "HTMLAudioTagPool cannot be instantiated";
+ }
+ var s = HTMLAudioTagPool;
// Static Properties
/**
- * A hash lookup of each sound channel, indexed by the audio source.
- * #property tags
+ * A hash lookup of each base audio tag, indexed by the audio source.
+ * @property _tags
+ * @type {{}}
* @static
- * @protected
+ * @private
*/
- s.tags = {};
+ s._tags = {};
+ /**
+ * An object pool for html audio tags
+ * @property _tagPool
+ * @type {TagPool}
+ * @static
+ * @private
+ */
+ s._tagPool = new TagPool();
-// Static Methods
/**
- * Get a tag pool. If the pool doesn't exist, create it.
- * #method get
- * @param {String} src The source file used by the audio tag.
+ * A hash lookup of if a base audio tag is available, indexed by the audio source
+ * @property _tagsUsed
+ * @type {{}}
+ * @private
* @static
- * @protected
*/
- s.get = function (src) {
- var channel = s.tags[src];
- if (channel == null) {
- channel = s.tags[src] = new TagPool(src);
- }
- return channel;
- };
+ s._tagUsed = {};
+// Static Methods
/**
- * Delete a TagPool and all related tags. Note that if the TagPool does not exist, this will fail.
- * #method remove
+ * 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 channel = s.tags[src];
- if (channel == null) {return false;}
- channel.removeAll();
- delete(s.tags[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;
};
- /**
- * Get a tag instance. This is a shortcut method.
- * #method getInstance
- * @param {String} src The source file used by the audio tag.
- * @static
- * @protected
- */
- s.getInstance = function (src) {
- var channel = s.tags[src];
- if (channel == null) {return null;}
- return channel.get();
- };
-
- /**
- * Return a tag instance. This is a shortcut method.
- * #method setInstance
- * @param {String} src The source file used by the audio tag.
- * @param {HTMLElement} tag Audio tag to set.
- * @static
- * @protected
- */
- s.setInstance = function (src, tag) {
- var channel = s.tags[src];
- if (channel == null) {return null;}
- return channel.set(tag);
- };
-
/**
* Gets the duration of the src audio in milliseconds
- * #method getDuration
+ * @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 channel = s.tags[src];
- if (channel == null) {return 0;}
- return channel.getDuration();
+ var t = s._tags[src];
+ if (t == null || !t.duration) {return 0;} // OJR duration is NaN if loading has not completed
+ return t.duration * 1000;
};
+ createjs.HTMLAudioTagPool = HTMLAudioTagPool;
-// Public Methods
- /**
- * Add an HTMLAudio tag into the pool.
- * #method add
- * @param {HTMLAudioElement} tag A tag to be used for playback.
- */
- p.add = function (tag) {
- this.tags.push(tag);
- this.length++;
- this.available++;
- };
+// ************************************************************************************************************
/**
- * Remove all tags from the channel. Usually in response to a delete call.
- * #method removeAll
+ * The TagPool is an object pool for HTMLAudio tag instances.
+ * #class TagPool
+ * @param {String} src The source of the channel.
+ * @protected
*/
- p.removeAll = function () {
- var tag;
- while(this.length--) {
- tag = this.tags[this.length];
- if(tag.parentNode) {
- tag.parentNode.removeChild(tag);
- }
- delete(this.tags[this.length]); // NOTE that the audio playback is already stopped by this point
- }
- this.src = null;
- this.tags.length = 0;
+ 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 () {
- if (this.tags.length == 0) {return null;}
- this.available = this.tags.length;
- var tag = this.tags.pop();
+ 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;
};
@@ -6938,27 +7581,35 @@ this.createjs = this.createjs || {};
* @param {HTMLAudioElement} tag HTML audio tag
*/
p.set = function (tag) {
- var index = createjs.indexOf(this.tags, tag);
- if (index == -1) {this.tags.push(tag);}
- this.available = this.tags.length;
+ // OJR this first step seems unnecessary
+ var index = createjs.indexOf(this._tags, tag);
+ if (index == -1) {
+ this._tags.src = null;
+ this._tags.push(tag);
+ }
};
- /**
- * Gets the duration for the src audio and on first call stores it to this.duration
- * #method getDuration
- * @return {Number} Duration of the src in milliseconds
- */
- p.getDuration = function () {
- // this will work because this will be only be run the first time a sound instance is created and before any tags are taken from the pool
- if (!this.duration) {this.duration = this.tags[this.tags.length - 1].duration * 1000;}
- return this.duration;
+ p.toString = function () {
+ return "[TagPool]";
};
- p.toString = function () {
- return "[HTMLAudioTagPool]";
+
+// 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;
};
- createjs.HTMLAudioTagPool = TagPool;
}());
//##############################################################################
@@ -6993,7 +7644,7 @@ this.createjs = this.createjs || {};
// 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._stalledHandler = createjs.proxy(this._playFailed, this);
this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
@@ -7005,7 +7656,6 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);
-
// Public Methods
/**
* Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
@@ -7059,14 +7709,14 @@ this.createjs = this.createjs || {};
tag.currentTime = this._startTime;
} catch (e) {
} // Reset Position
- createjs.HTMLAudioTagPool.setInstance(this.src, tag);
+ createjs.HTMLAudioTagPool.set(this.src, tag);
this._playbackResource = null;
}
};
- p._beginPlaying = function (offset, loop, volume, pan) {
- this._playbackResource = createjs.HTMLAudioTagPool.getInstance(this.src);
- return this.AbstractSoundInstance__beginPlaying(offset, loop, volume, pan);
+ p._beginPlaying = function (playProps) {
+ this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
+ return this.AbstractSoundInstance__beginPlaying(playProps);
};
p._handleSoundReady = function (event) {
@@ -7180,8 +7830,17 @@ this.createjs = this.createjs || {};
}
};
+ 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 = (startTime + duration) * 0.001;
+ 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);
@@ -7189,11 +7848,10 @@ this.createjs = this.createjs || {};
}
};
- /* This should never change
p._setDurationFromSource = function () {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
+ this._playbackResource = null;
};
- */
createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());
@@ -7216,9 +7874,8 @@ this.createjs = this.createjs || {};
*
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. This will be seen as soon as you register sounds, as
- * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as
- * a guide to how many total audio tags you can safely use in all browsers.
+ * 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
@@ -7227,7 +7884,7 @@ this.createjs = this.createjs || {};
*
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 and play at once, which appears to be determined by
+ *
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 requires Quicktime to be installed for audio playback.
*
* iOS 6 limitations
- *
Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)
- *
HTML Audio is disabled by default because
- *
can only have one <audio> tag
+ *
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
*
*
@@ -7265,19 +7921,6 @@ this.createjs = this.createjs || {};
// Public Properties
- /**
- * The default number of instances to allow. Used by {{#crossLink "Sound"}}{{/crossLink}} when a source
- * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method. This is only used if
- * a value is not provided.
- *
- * NOTE this property only exists as a limitation of HTML audio.
- * @property defaultNumChannels
- * @type {Number}
- * @default 2
- * @since 0.4.0
- */
- this.defaultNumChannels = 2;
-
this._capabilities = s._capabilities;
this._loaderClass = createjs.SoundLoader;
@@ -7287,10 +7930,9 @@ this.createjs = this.createjs || {};
var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
var s = HTMLAudioPlugin;
-
// Static Properties
/**
- * The maximum number of instances that can be loaded and played. This is a browser limitation, primarily limited to IE9.
+ * 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
@@ -7306,7 +7948,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default canplaythrough
* @static
- * @protected
+ * @private
*/
s._AUDIO_READY = "canplaythrough";
@@ -7316,7 +7958,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default ended
* @static
- * @protected
+ * @private
*/
s._AUDIO_ENDED = "ended";
@@ -7326,7 +7968,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default seeked
* @static
- * @protected
+ * @private
*/
s._AUDIO_SEEKED = "seeked";
@@ -7336,7 +7978,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default stalled
* @static
- * @protected
+ * @private
*/
s._AUDIO_STALLED = "stalled";
@@ -7347,35 +7989,21 @@ this.createjs = this.createjs || {};
* @type {String}
* @default timeupdate
* @static
- * @protected
+ * @private
*/
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
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
- /**
- * Deprecated now that we have audio sprite support. Audio sprites are strongly recommend on iOS for the following reasons:
- *
it 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
- *
- * @property enableIOS
- * @type {Boolean}
- * @default false
- * @deprecated
- */
- s.enableIOS = false;
-
// Static Methods
/**
@@ -7387,16 +8015,15 @@ this.createjs = this.createjs || {};
*/
s.isSupported = function () {
s._generateCapabilities();
- if (s._capabilities == null) {return false;}
- return true;
+ return (s._capabilities != null);
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -7404,7 +8031,7 @@ this.createjs = this.createjs || {};
if (t.canPlayType == null) {return null;}
s._capabilities = {
- panning:true,
+ panning:false,
volume:true,
tracks:-1
};
@@ -7421,15 +8048,9 @@ this.createjs = this.createjs || {};
// public methods
- p.register = function (loadItem, instances) {
- var channel = createjs.HTMLAudioTagPool.get(loadItem.src);
- var tag = null;
- for (var i = 0; i < instances; i++) {
- tag = this._createTag(loadItem.src);
- channel.add(tag);
- }
-
- var loader = this.AbstractPlugin_register(loadItem, instances);
+ p.register = function (loadItem) {
+ var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
+ var loader = this.AbstractPlugin_register(loadItem);
loader.setTag(tag);
return loader;
@@ -7442,7 +8063,7 @@ this.createjs = this.createjs || {};
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
- si.setPlaybackResource(null);
+ si.playbackResource = null;
return si;
};
@@ -7454,23 +8075,5 @@ this.createjs = this.createjs || {};
p.setVolume = p.getVolume = p.setMute = null;
-// 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
- */
- // TODO move this to tagpool when it changes to be a standard object pool
- 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.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 841003f0..12c58262 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,6 @@
* SoundJS FlashAudioPlugin also includes swfobject (http://code.google.com/p/swfobject/)
*/
-this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="NEXT",a.buildDate="Thu, 11 Dec 2014 23:16:15 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";var a=Object.defineProperty?!0:!1,b={};try{Object.defineProperty(b,"bar",{get:function(){return this._bar},set:function(a){this._bar=a}})}catch(c){a=!1}createjs.definePropertySupported=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"BrowserDetect cannot be instantiated"}var b=a.agent=window.navigator.userAgent;a.isWindowPhone=b.indexOf("IEMobile")>-1||b.indexOf("Windows Phone")>-1,a.isFirefox=b.indexOf("Firefox")>-1,a.isOpera=null!=window.opera,a.isChrome=b.indexOf("Chrome")>-1,a.isIOS=(b.indexOf("iPod")>-1||b.indexOf("iPhone")>-1||b.indexOf("iPad")>-1)&&!a.isWindowPhone,a.isAndroid=b.indexOf("Android")>-1&&!a.isWindowPhone,a.isBlackberry=b.indexOf("Blackberry")>-1,createjs.BrowserDetect=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this._listeners=null,this._captureListeners=null}var b=a.prototype;a.initialize=function(a){a.addEventListener=b.addEventListener,a.on=b.on,a.removeEventListener=a.off=b.removeEventListener,a.removeAllEventListeners=b.removeAllEventListeners,a.hasEventListener=b.hasEventListener,a.dispatchEvent=b.dispatchEvent,a._dispatchEvent=b._dispatchEvent,a.willTrigger=b.willTrigger},b.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},b.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)},b.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}}},b.off=b.removeEventListener,b.removeAllEventListeners=function(a){a?(this._listeners&&delete this._listeners[a],this._captureListeners&&delete this._captureListeners[a]):this._listeners=this._captureListeners=null},b.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},b.hasEventListener=function(a){var b=this._listeners,c=this._captureListeners;return!!(b&&b[a]||c&&c[a])},b.willTrigger=function(a){for(var b=this;b;){if(b.hasEventListener(a))return!0;b=b.parent}return!1},b.toString=function(){return"[EventDispatcher]"},b._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=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=a.prototype;b.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},b.stopPropagation=function(){this.propagationStopped=!0},b.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},b.remove=function(){this.removed=!0},b.clone=function(){return new a(this.type,this.bubbles,this.cancelable)},b.set=function(a){for(var b in a)this[b]=a[b];return this},b.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var b=createjs.extend(a,createjs.Event);b.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(a,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.Event);b.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(a,"Event")}(window),this.createjs=this.createjs||{},function(){"use strict";function a(){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="Anonymous",this.loadTimeout=8e3}var b=a.prototype={},c=a;c.create=function(b){if("string"==typeof b){var d=new a;return d.src=b,d}if(b instanceof c)return b;if(b instanceof Object)return b;throw new Error("Type not recognized.")},b.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=c}(),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:void 0},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: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 a(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 b=createjs.extend(a,createjs.EventDispatcher),c=a;c.POST="POST",c.GET="GET",c.BINARY="binary",c.CSS="css",c.IMAGE="image",c.JAVASCRIPT="javascript",c.JSON="json",c.JSONP="jsonp",c.MANIFEST="manifest",c.SOUND="sound",c.VIDEO="video",c.SPRITESHEET="spritesheet",c.SVG="svg",c.TEXT="text",c.XML="xml",b.getItem=function(){return this._item},b.getResult=function(a){return a?this._rawResult:this._result},b.getTag=function(){return this._tag},b.setTag=function(a){this._tag=a},b.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()},b.cancel=function(){this.canceled=!0,this.destroy()},b.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()},b.getLoadedItems=function(){return this._loadedItems},b._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},b._createTag=function(){return null},b._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},b._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)}},b._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)}},b._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},b._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},b.resultFormatter=null,b.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)}},b.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},b.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src"}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},b._createTag=function(){},b._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},b._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(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(a){this._item=a},b=createjs.extend(a,createjs.EventDispatcher);b.load=function(){},b.destroy=function(){},b.cancel=function(){},createjs.AbstractRequest=createjs.promote(a,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractRequest);b.load=function(){null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0),this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._tag[this._tagSrcAttribute]=this._item.src},b.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},b._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},b._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},b._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag)},b._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},b._showTag=function(){this._tag.style.visibility=this._startTagVisibility},b._handleStalled=function(){},createjs.TagRequest=createjs.promote(a,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var b=createjs.extend(a,createjs.TagRequest);b.load=function(){this._tag.onstalled=createjs.proxy(this._handleStalled,this),this._tag.onprogress=createjs.proxy(this._handleProgress,this),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},b._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},b._handleStalled=function(){},b._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},b._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._tag.onprogress=null,this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(a,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractRequest);a.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],b.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},b.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},b.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))}},b.setResponseType=function(a){this._request.responseType=a},b.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},b.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},b._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},b._handleLoadStart=function(){clearTimeout(this._loadTimeout),this.dispatchEvent("loadstart")},b._handleAbort=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED",null,a))},b._handleError=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent(a.message))},b._handleReadyStateChange=function(){4==this._request.readyState&&this._handleLoad()},b._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"))}},b._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},b._checkError=function(){var a=parseInt(this._request.status);switch(a){case 404:case 0:return new Error(a)}return null},b._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},b._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}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},b._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)},b.toString=function(){return"[PreloadJS XHRRequest]"},createjs.XHRRequest=createjs.promote(a,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractMediaLoader),c=a;c.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},b._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(a,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){throw"Sound cannot be instantiated"}function b(a,b){this.init(a,b)}var c=a;c.INTERRUPT_ANY="any",c.INTERRUPT_EARLY="early",c.INTERRUPT_LATE="late",c.INTERRUPT_NONE="none",c.PLAY_INITED="playInited",c.PLAY_SUCCEEDED="playSucceeded",c.PLAY_INTERRUPTED="playInterrupted",c.PLAY_FINISHED="playFinished",c.PLAY_FAILED="playFailed",c.SUPPORTED_EXTENSIONS=["mp3","ogg","mpeg","wav","m4a","mp4","aiff","wma","mid"],c.EXTENSION_MAP={m4a:"mp4"},c.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/,c.defaultInterruptBehavior=c.INTERRUPT_NONE,c.alternateExtensions=[],c.activePlugin=null,c._pluginsRegistered=!1,c._lastID=0,c._masterVolume=1,c._masterMute=!1,c._instances=[],c._idHash={},c._preloadHash={},c.addEventListener=null,c.removeEventListener=null,c.removeAllEventListeners=null,c.dispatchEvent=null,c.hasEventListener=null,c._listeners=null,createjs.EventDispatcher.initialize(c),c.getPreloadHandlers=function(){return{callback:createjs.proxy(c.initLoad,c),types:["sound"],extensions:c.SUPPORTED_EXTENSIONS}},c._handleLoadComplete=function(a){var b=a.target.getItem().src;if(c._preloadHash[b])for(var d=0,e=c._preloadHash[b].length;e>d;d++){var f=c._preloadHash[b][d];if(c._preloadHash[b][d]=!0,c.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,c.dispatchEvent(a)}}},c._handleLoadError=function(a){var b=a.target.getItem().src;if(c._preloadHash[b])for(var d=0,e=c._preloadHash[b].length;e>d;d++){var f=c._preloadHash[b][d];if(c._preloadHash[b][d]=!1,c.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,c.dispatchEvent(a)}}},c._registerPlugin=function(a){return a.isSupported()?(c.activePlugin=new a,!0):!1},c.registerPlugins=function(a){c._pluginsRegistered=!0;for(var b=0,d=a.length;d>b;b++)if(c._registerPlugin(a[b]))return!0;return!1},c.initializeDefaultPlugins=function(){return null!=c.activePlugin?!0:c._pluginsRegistered?!1:c.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},c.isReady=function(){return null!=c.activePlugin},c.getCapabilities=function(){return null==c.activePlugin?null:c.activePlugin._capabilities},c.getCapability=function(a){return null==c.activePlugin?null:c.activePlugin._capabilities[a]},c.initLoad=function(a){return c._registerSound(a)},c._registerSound=function(a){if(!c.initializeDefaultPlugins())return!1;var d=c._parsePath(a.src);if(null==d)return!1;a.src=d.src,a.type="sound";var e=a.data,f=c.activePlugin.defaultNumChannels||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],c._idHash[g.id]={src:a.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)};null!=a.id&&(c._idHash[a.id]={src:a.src});var i=c.activePlugin.register(a,f);return b.create(a.src,f),null!=e&&isNaN(e)?a.data.channels=f||b.maxPerChannel():a.data=f||b.maxPerChannel(),i.type&&(a.type=i.type),i},c.registerSound=function(a,b,d,e){var f={src:a,id:b,data:d};a instanceof Object&&(e=b,f=a),f=createjs.LoadItem.create(f),null!=e&&(f.src=e+a);var g=c._registerSound(f);if(!g)return!1;if(c._preloadHash[f.src]||(c._preloadHash[f.src]=[]),c._preloadHash[f.src].push(f),1==c._preloadHash[f.src].length)g.on("complete",createjs.proxy(this._handleLoadComplete,this)),g.on("error",createjs.proxy(this._handleLoadError,this)),c.activePlugin.preload(g);else if(1==c._preloadHash[f.src][0])return!0;return f},c.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path);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);return c},c.registerManifest=function(a,b){try{console.log("createjs.Sound.registerManifest is deprecated, please use createjs.Sound.registerSounds.")}catch(c){}return this.registerSounds(a,b)},c.removeSound=function(a,d){if(null==c.activePlugin)return!1;a instanceof Object&&(a=a.src),a=c._getSrcById(a).src,null!=d&&(a=d+a);var e=c._parsePath(a);if(null==e)return!1;a=e.src;for(var f in c._idHash)c._idHash[f].src==a&&delete c._idHash[f];return b.removeSrc(a),delete c._preloadHash[a],c.activePlugin.removeSound(a),!0},c.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},c.removeManifest=function(a,b){try{console.log("createjs.Sound.removeManifest is deprecated, please use createjs.Sound.removeSounds.")}catch(d){}return c.removeSounds(a,b)},c.removeAllSounds=function(){c._idHash={},c._preloadHash={},b.removeAll(),c.activePlugin&&c.activePlugin.removeAllSounds()},c.loadComplete=function(a){if(!c.isReady())return!1;var b=c._parsePath(a);return a=b?c._getSrcById(b.src).src:c._getSrcById(a).src,1==c._preloadHash[a][0]},c._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var b=a.match(c.FILE_PATTERN);if(null==b)return!1;for(var d=b[4],e=b[5],f=c.getCapabilities(),g=0;!f[e];)if(e=c.alternateExtensions[g++],g>c.alternateExtensions.length)return null;a=a.replace("."+b[5],"."+e);var h={name:d,src:a,extension:e};return h},c.play=function(a,b,d,e,f,g,h,i,j){b instanceof Object&&(d=b.delay,e=b.offset,f=b.loop,g=b.volume,h=b.pan,i=b.startTime,j=b.duration,b=b.interrupt);var k=c.createInstance(a,i,j),l=c._playInstance(k,b,d,e,f,g,h);return l||k._playFailed(),k},c.createInstance=function(a,d,e){if(!c.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(a,d,e);a=c._getSrcById(a);var f=c._parsePath(a.src),g=null;return null!=f&&null!=f.src?(b.create(f.src),null==d&&(d=a.startTime),g=c.activePlugin.create(f.src,d,e||a.duration)):g=new createjs.DefaultSoundInstance(a,d,e),g.uniqueId=c._lastID++,g},c.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),c._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var b=this._instances,d=0,e=b.length;e>d;d++)b[d].setMasterVolume(a)},c.getVolume=function(){return c._masterVolume},c.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},c.getMute=function(){return this._masterMute},c.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},c._playInstance=function(a,b,d,e,f,g,h){if(b instanceof Object&&(d=b.delay,e=b.offset,f=b.loop,g=b.volume,h=b.pan,b=b.interrupt),b=b||c.defaultInterruptBehavior,null==d&&(d=0),null==e&&(e=a.getPosition()),null==f&&(f=a.loop),null==g&&(g=a.volume),null==h&&(h=a.pan),0==d){var i=c._beginPlaying(a,b,e,f,g,h);if(!i)return!1}else{var j=setTimeout(function(){c._beginPlaying(a,b,e,f,g,h)},d);a.delayTimeoutId=j}return this._instances.push(a),!0},c._beginPlaying=function(a,c,d,e,f,g){if(!b.add(a,c))return!1;var h=a._beginPlaying(d,e,f,g);if(!h){var i=createjs.indexOf(this._instances,a);return i>-1&&this._instances.splice(i,1),!1}return!0},c._getSrcById=function(a){return c._idHash[a]||{src:a}},c._playFinished=function(a){b.remove(a);var c=createjs.indexOf(this._instances,a);c>-1&&this._instances.splice(c,1)},createjs.Sound=a,b.channels={},b.create=function(a,c){var d=b.get(a);return null==d?(b.channels[a]=new b(a,c),!0):!1},b.removeSrc=function(a){var c=b.get(a);return null==c?!1:(c._removeAll(),delete b.channels[a],!0)},b.removeAll=function(){for(var a in b.channels)b.channels[a]._removeAll();b.channels={}},b.add=function(a,c){var d=b.get(a.src);return null==d?!1:d._add(a,c)},b.remove=function(a){var c=b.get(a.src);return null==c?!1:(c._remove(a),!0)},b.maxPerChannel=function(){return d.maxDefault},b.get=function(a){return b.channels[a]};var d=b.prototype;d.constructor=b,d.src=null,d.max=null,d.maxDefault=100,d.length=0,d.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},d._get=function(a){return this._instances[a]},d._add=function(a,b){return this._getSlot(b,a)?(this._instances.push(a),this.length++,!0):!1},d._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1==b?!1:(this._instances.splice(b,1),this.length--,!0)},d._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},d._getSlot=function(b){var c,d;if(b!=a.INTERRUPT_NONE&&(d=this._get(0),null==d))return!0;for(var e=0,f=this.max;f>e;e++){if(c=this._get(e),null==c)return!0;if(c.playState==a.PLAY_FINISHED||c.playState==a.PLAY_INTERRUPTED||c.playState==a.PLAY_FAILED){d=c;break}b!=a.INTERRUPT_NONE&&(b==a.INTERRUPT_EARLY&&c.getPosition()d.getPosition())&&(d=c)}return null!=d?(d._interrupt(),this._remove(d),!0):!1},d.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var a=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._startTime=Math.max(0,b||0),this._volume=1,createjs.definePropertySupported&&Object.defineProperty(this,"volume",{get:this.getVolume,set:this.setVolume}),this._pan=0,createjs.definePropertySupported&&Object.defineProperty(this,"pan",{get:this.getPan,set:this.setPan}),this._duration=Math.max(0,c||0),createjs.definePropertySupported&&Object.defineProperty(this,"duration",{get:this.getDuration,set:this.setDuration}),this._playbackResource=null,createjs.definePropertySupported&&Object.defineProperty(this,"playbackResource",{get:this.getPlaybackResource,set:this.setPlaybackResource}),d!==!1&&d!==!0&&this.setPlaybackResource(d),this._position=0,createjs.definePropertySupported&&Object.defineProperty(this,"position",{get:this.getPosition,set:this.setPosition}),this._loop=0,createjs.definePropertySupported&&Object.defineProperty(this,"loop",{get:this.getLoop,set:this.setLoop}),this._muted=!1,createjs.definePropertySupported&&Object.defineProperty(this,"muted",{get:this.getMuted,set:this.setMuted}),this._paused=!1,createjs.definePropertySupported&&Object.defineProperty(this,"paused",{get:this.getPaused,set:this.setPaused})
-},b=createjs.extend(a,createjs.EventDispatcher);b.play=function(a,b,c,d,e,f){return this.playState==createjs.Sound.PLAY_SUCCEEDED?(a instanceof Object&&(c=a.offset,d=a.loop,e=a.volume,f=a.pan),null!=c&&this.setPosition(c),null!=d&&this.setLoop(d),null!=e&&this.setVolume(e),null!=f&&this.setPan(f),void(this._paused&&this.setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,a,b,c,d,e,f),this)},b.pause=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED?!1:(this.setPaused(!0),!0)},b.resume=function(){return this._paused?(this.setPaused(!1),!0):!1},b.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},b.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},b.toString=function(){return"[AbstractSoundInstance]"},b.getPaused=function(){return this._paused},b.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)},b.setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},b.getVolume=function(){return this._volume},b.setMute=function(a){this.setMuted(a)},b.getMute=function(){return this._muted},b.setMuted=function(a){return a===!0||a===!1?(this._muted=a,this._updateVolume(),this):void 0},b.getMuted=function(){return this._muted},b.setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},b.getPan=function(){return this._pan},b.getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED?this._position:this._calculateCurrentPosition()},b.setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},b.getDuration=function(){return this._duration},b.setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},b.setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._setDurationFromSource(),this},b.getPlaybackResource=function(){return this._playbackResource},b.getLoop=function(){return this._loop},b.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},b._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},b._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},b._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},b._beginPlaying=function(a,b,c,d){return this.setPosition(a),this.setLoop(b),this.setVolume(c),this.setPan(d),null!=this._playbackResource&&this._positionc;c++){var e=this._soundInstances[b][c];e.setPlaybackResource(this._audioSources[b])}},b._handlePreloadError=function(){},b._updateVolume=function(){},createjs.AbstractPlugin=a}(),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._handleError,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 a(a,b,d,e){this.AbstractSoundInstance_constructor(a,b,d,e),this.gainNode=c.context.createGain(),this.panNode=c.context.createPanner(),this.panNode.panningModel=c._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 b=createjs.extend(a,createjs.AbstractSoundInstance),c=a;c.context=null,c.destinationNode=null,c._panningModel="equalpower",b.destroy=function(){this.AbstractSoundInstance_destroy(),this.panNode.disconnect(0),this.panNode=null,this.gainNode.disconnect(0),this.gainNode=null},b.toString=function(){return"[WebAudioSoundInstance]"},b._updatePan=function(){this.panNode.setPosition(this._pan,0,-.5)},b._removeLooping=function(){this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext)},b._addLooping=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},b._setDurationFromSource=function(){this._duration=1e3*this.playbackResource.duration},b._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},b._cleanUpAudioNode=function(a){return a&&(a.stop(0),a.disconnect(0),a=null),a},b._handleSoundReady=function(){this.gainNode.connect(c.destinationNode);var a=.001*this._duration,b=.001*this._position;this.sourceNode=this._createAndPlayAudioNode(c.context.currentTime-a,b),this._playbackStartTime=this.sourceNode.startTime-b,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(a-b)),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},b._createAndPlayAudioNode=function(a,b){var d=c.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,b+.001*this._startTime,e-b),d},b._pause=function(){this._position=1e3*(c.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)},b._resume=function(){this._handleSoundReady()},b._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},b._calculateCurrentPosition=function(){return 1e3*(c.context.currentTime-this._playbackStartTime)},b._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},b._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)},b._updateDuration=function(){this._pause(),this._resume()},createjs.WebAudioSoundInstance=createjs.promote(a,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.AbstractPlugin_constructor(),this._panningModel=c._panningModel,this._volume=1,this.context=c.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=c._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var b=createjs.extend(a,createjs.AbstractPlugin),c=a;c._capabilities=null,c._panningModel="equalpower",c.context=null,c.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return"file:"!=location.protocol||a||this._isFileXHRSupported()?(c._generateCapabilities(),null==c.context?!1:!0):!1},c.playEmptySound=function(){var a=c.context.createBufferSource();a.buffer=c.context.createBuffer(1,1,22050),a.connect(c.context.destination),a.start(0,0,0)},c._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},c._generateCapabilities=function(){if(null==c._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;if(null==c.context)if(window.AudioContext)c.context=new AudioContext;else{if(!window.webkitAudioContext)return null;c.context=new webkitAudioContext}c._compatibilitySetUp(),c.playEmptySound(),c._capabilities={panning:!0,volume:!0,tracks:-1};for(var b=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=b.length;f>e;e++){var g=b[e],h=d[g]||g;c._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}c.context.destination.numberOfChannels<2&&(c._capabilities.panning=!1)}},c._compatibilitySetUp=function(){if(c._panningModel="equalpower",!c.context.createGain){c.context.createGain=c.context.createGainNode;var a=c.context.createBufferSource();a.__proto__.start=a.__proto__.noteGrainOn,a.__proto__.stop=a.__proto__.noteOff,c._panningModel=0}},b.toString=function(){return"[WebAudioPlugin]"},b._addPropsToClasses=function(){var a=this._soundInstanceClass;a.context=this.context,a.destinationNode=this.gainNode,a._panningModel=this._panningModel,this._loaderClass.context=this.context},b._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},createjs.WebAudioPlugin=createjs.promote(a,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.src=a,this.length=0,this.available=0,this.tags=[],this.duration=0}var b=a.prototype;b.constructor=a;var c=a;c.tags={},c.get=function(b){var d=c.tags[b];return null==d&&(d=c.tags[b]=new a(b)),d},c.remove=function(a){var b=c.tags[a];return null==b?!1:(b.removeAll(),delete c.tags[a],!0)},c.getInstance=function(a){var b=c.tags[a];return null==b?null:b.get()},c.setInstance=function(a,b){var d=c.tags[a];return null==d?null:d.set(b)},c.getDuration=function(a){var b=c.tags[a];return null==b?0:b.getDuration()},b.add=function(a){this.tags.push(a),this.length++,this.available++},b.removeAll=function(){for(var a;this.length--;)a=this.tags[this.length],a.parentNode&&a.parentNode.removeChild(a),delete this.tags[this.length];this.src=null,this.tags.length=0},b.get=function(){if(0==this.tags.length)return null;this.available=this.tags.length;var a=this.tags.pop();return null==a.parentNode&&document.body.appendChild(a),a},b.set=function(a){var b=createjs.indexOf(this.tags,a);-1==b&&this.tags.push(a),this.available=this.tags.length},b.getDuration=function(){return this.duration||(this.duration=1e3*this.tags[this.tags.length-1].duration),this.duration},b.toString=function(){return"[HTMLAudioTagPool]"},createjs.HTMLAudioTagPool=a}(),this.createjs=this.createjs||{},function(){"use strict";function a(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 b=createjs.extend(a,createjs.AbstractSoundInstance);b.setMasterVolume=function(){this._updateVolume()},b.setMasterMute=function(){this._updateVolume()},b.toString=function(){return"[HTMLAudioSoundInstance]"},b._removeLooping=function(){null!=this._playbackResource&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._addLooping=function(){null==this._playbackResource||this._audioSpriteStopTime||(this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1),this._playbackResource.loop=!0)},b._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.setInstance(this.src,a),this._playbackResource=null}},b._beginPlaying=function(a,b,c,d){return this._playbackResource=createjs.HTMLAudioTagPool.getInstance(this.src),this.AbstractSoundInstance__beginPlaying(a,b,c,d)},b._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()},b._handleTagReady=function(){this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY,this._readyHandler,!1),this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED,this._stalledHandler,!1),this._handleSoundReady()},b._pause=function(){this._playbackResource.pause()},b._resume=function(){this._playbackResource.play()},b._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)}},b._calculateCurrentPosition=function(){return 1e3*this._playbackResource.currentTime-this._startTime},b._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)}},b._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))},b._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")))},b._handleLoop=function(){0==this._loop&&(this._playbackResource.loop=!1,this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED,this._loopHandler,!1))},b._updateDuration=function(){this._audioSpriteStopTime=.001*(startTime+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(a,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function a(){this.AbstractPlugin_constructor(),this.defaultNumChannels=2,this._capabilities=c._capabilities,this._loaderClass=createjs.SoundLoader,this._soundInstanceClass=createjs.HTMLAudioSoundInstance}var b=createjs.extend(a,createjs.AbstractPlugin),c=a;c.MAX_INSTANCES=30,c._AUDIO_READY="canplaythrough",c._AUDIO_ENDED="ended",c._AUDIO_SEEKED="seeked",c._AUDIO_STALLED="stalled",c._TIME_UPDATE="timeupdate",c._capabilities=null,c.enableIOS=!1,c.isSupported=function(){return c._generateCapabilities(),null==c._capabilities?!1:!0},c._generateCapabilities=function(){if(null==c._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;c._capabilities={panning:!0,volume:!0,tracks:-1};for(var b=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=b.length;f>e;e++){var g=b[e],h=d[g]||g;c._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},b.register=function(a,b){for(var c=createjs.HTMLAudioTagPool.get(a.src),d=null,e=0;b>e;e++)d=this._createTag(a.src),c.add(d);var f=this.AbstractPlugin_register(a,b);return f.setTag(d),f},b.removeSound=function(a){this.AbstractPlugin_removeSound(a),createjs.HTMLAudioTagPool.remove(a)},b.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.setPlaybackResource(null),d},b.toString=function(){return"[HTMLAudioPlugin]"},b.setVolume=b.getVolume=b.setMute=null,b._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.HTMLAudioPlugin=createjs.promote(a,"AbstractPlugin")}();
\ No newline at end of file
+this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="NEXT",a.buildDate="Thu, 12 Oct 2017 16:33:45 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.deprecate=function(a,b){"use strict";return function(){var c="Deprecated property or method '"+b+"'. See docs for info.";return console&&(console.warn?console.warn(c):console.log(c)),a&&a.apply(this,arguments)}},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;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,b,c){if("string"==typeof a){var d=this._listeners;if(!(b||d&&d[a]))return!0;a=new createjs.Event(a,b,c)}else a.target&&a.clone&&(a=a.clone());try{a.target=this}catch(e){}if(a.bubbles&&this.parent){for(var f=this,g=[f];f.parent;)g.push(f=f.parent);var h,i=g.length;for(h=i-1;h>=0&&!a.propagationStopped;h--)g[h]._dispatchEvent(a,1+(0==h));for(h=1;i>h&&!a.propagationStopped;h++)g[h]._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,e=2>=b?this._captureListeners:this._listeners;if(a&&e&&(d=e[a.type])&&(c=d.length)){try{a.currentTarget=this}catch(f){}try{a.eventPhase=0|b}catch(f){}a.removed=!1,d=d.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=d[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}2===b&&this._dispatchEvent(a,2.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.Methods.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}(),this.createjs=this.createjs||{},function(){var a={};a.POST="POST",a.GET="GET",createjs.Methods=a}(),this.createjs=this.createjs||{},function(){var a={};a.BINARY="binary",a.CSS="css",a.FONT="font",a.FONTCSS="fontcss",a.IMAGE="image",a.JAVASCRIPT="javascript",a.JSON="json",a.JSONP="jsonp",a.MANIFEST="manifest",a.SOUND="sound",a.VIDEO="video",a.SPRITESHEET="spritesheet",a.SVG="svg",a.TEXT="text",a.XML="xml",createjs.Types=a}(),function(){var a={};a.a=function(){return a.el("a")},a.svg=function(){return a.el("svg")},a.object=function(){return a.el("object")},a.image=function(){return a.el("image")},a.img=function(){return a.el("img")},a.style=function(){return a.el("style")},a.link=function(){return a.el("link")},a.script=function(){return a.el("script")},a.audio=function(){return a.el("audio")},a.video=function(){return a.el("video")},a.text=function(a){return document.createTextNode(a)},a.el=function(a){return document.createElement(a)},createjs.Elements=a}(),function(){var a={container:null};a.appendToHead=function(b){a.getHead().appendChild(b)},a.appendToBody=function(b){if(null==a.container){a.container=document.createElement("div"),a.container.id="preloadjs-container";var c=a.container.style;c.visibility="hidden",c.position="absolute",c.width=a.container.style.height="10px",c.overflow="hidden",c.transform=c.msTransform=c.webkitTransform=c.oTransform="translate(-10px, -10px)",a.getBody().appendChild(a.container)}a.container.appendChild(b)},a.getHead=function(){return document.head||document.getElementsByTagName("head")[0]},a.getBody=function(){return document.body||document.getElementsByTagName("body")[0]},a.removeChild=function(a){a.parent&&a.parent.removeChild(a)},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},createjs.DomUtils=a}(),function(){var a={};a.isBinary=function(a){switch(a){case createjs.Types.IMAGE:case createjs.Types.BINARY:return!0;default:return!1}},a.isText=function(a){switch(a){case createjs.Types.TEXT:case createjs.Types.JSON:case createjs.Types.MANIFEST:case createjs.Types.XML:case createjs.Types.CSS:case createjs.Types.SVG:case createjs.Types.JAVASCRIPT:case createjs.Types.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.Types.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.Types.IMAGE;case"ogg":case"mp3":case"webm":return createjs.Types.SOUND;case"mp4":case"webm":case"ts":return createjs.Types.VIDEO;case"json":return createjs.Types.JSON;case"xml":return createjs.Types.XML;case"css":return createjs.Types.CSS;case"js":return createjs.Types.JAVASCRIPT;case"svg":return createjs.Types.SVG;default:return createjs.Types.TEXT}},createjs.RequestUtils=a}(),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,protocol:null,hostname:null,port:null,pathname:null,search:null,hash:null,host:null};if(null==b)return c;var d=createjs.Elements.a();d.href=b;for(var e in c)e in d&&(c[e]=d[e]);var f=b.indexOf("?");f>-1&&(b=b.substr(0,f));var g;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(g=b.match(a.EXTENSION_PATT))&&(c.extension=g[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildURI=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=createjs.Elements.a();b.href=a.src;var c=createjs.Elements.a();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=createjs.Elements.a();return b.href=a.src,""==b.hostname&&"file:"==b.protocol},createjs.URLUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;try{Object.defineProperties(b,{POST:{get:createjs.deprecate(function(){return createjs.Methods.POST},"AbstractLoader.POST")},GET:{get:createjs.deprecate(function(){return createjs.Methods.GET},"AbstractLoader.GET")},BINARY:{get:createjs.deprecate(function(){return createjs.Types.BINARY},"AbstractLoader.BINARY")},CSS:{get:createjs.deprecate(function(){return createjs.Types.CSS},"AbstractLoader.CSS")},FONT:{get:createjs.deprecate(function(){return createjs.Types.FONT},"AbstractLoader.FONT")},FONTCSS:{get:createjs.deprecate(function(){return createjs.Types.FONTCSS},"AbstractLoader.FONTCSS")},IMAGE:{get:createjs.deprecate(function(){return createjs.Types.IMAGE},"AbstractLoader.IMAGE")},JAVASCRIPT:{get:createjs.deprecate(function(){return createjs.Types.JAVASCRIPT},"AbstractLoader.JAVASCRIPT")},JSON:{get:createjs.deprecate(function(){return createjs.Types.JSON},"AbstractLoader.JSON")},JSONP:{get:createjs.deprecate(function(){return createjs.Types.JSONP},"AbstractLoader.JSONP")},MANIFEST:{get:createjs.deprecate(function(){return createjs.Types.MANIFEST},"AbstractLoader.MANIFEST")},SOUND:{get:createjs.deprecate(function(){return createjs.Types.SOUND},"AbstractLoader.SOUND")},VIDEO:{get:createjs.deprecate(function(){return createjs.Types.VIDEO},"AbstractLoader.VIDEO")},SPRITESHEET:{get:createjs.deprecate(function(){return createjs.Types.SPRITESHEET},"AbstractLoader.SPRITESHEET")},SVG:{get:createjs.deprecate(function(){return createjs.Types.SVG},"AbstractLoader.SVG")},TEXT:{get:createjs.deprecate(function(){return createjs.Types.TEXT},"AbstractLoader.TEXT")},XML:{get:createjs.deprecate(function(){return createjs.Types.XML},"AbstractLoader.XML")}})}catch(c){}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);b instanceof Function?b.call(this,createjs.proxy(this._resultFormatSuccess,this),createjs.proxy(this._resultFormatFailed,this)):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_"+a.type.toUpperCase()+"_ERROR"))}},a._resultFormatSuccess=function(a){this._result=a,this._sendComplete()},a._resultFormatFailed=function(a){this._sendError(a)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",this.on("initialize",this._updateXHR,this)}var a=createjs.extend(AbstractMediaLoader,createjs.AbstractLoader);a.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},a._createTag=function(){},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._updateXHR=function(a){a.loader.setResponseType&&a.loader.setResponseType("blob")},a._formatResult=function(a){if(this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR){var b=window.URL||window.webkitURL,c=a.getResult(!0);a.getTag().src=b.createObjectURL(c)}return a.getTag()},createjs.AbstractMediaLoader=createjs.promote(AbstractMediaLoader,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractRequest=function(a){this._item=a},a=createjs.extend(AbstractRequest,createjs.EventDispatcher);a.load=function(){},a.destroy=function(){},a.cancel=function(){},createjs.AbstractRequest=createjs.promote(AbstractRequest,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function TagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(createjs.DomUtils.appendToBody(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();null!=this._request.addEventListener?(this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1)):(this._request.onloadstart=this._handleLoadStartProxy,this._request.onprogress=this._handleProgressProxy,this._request.onabort=this._handleAbortProxy,this._request.onerror=this._handleErrorProxy,this._request.ontimeout=this._handleTimeoutProxy,this._request.onload=this._handleLoadProxy,this._request.onreadystatechange=this._handleReadyStateChangeProxy),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values?this._request.send(createjs.URLUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){"blob"===a&&(a=window.URL?"blob":"arraybuffer",this._responseType=a),this._request.responseType=a},a.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},a.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._handleLoadStart=function(){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);if(this._response=this._getResponse(),"arraybuffer"===this._responseType)try{this._response=new Blob([this._response])}catch(b){if(window.BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,"TypeError"===b.name&&window.BlobBuilder){var c=new BlobBuilder;c.append(this._response),this._response=c.getBlob()}}this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},a._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},a._checkError=function(){var a=parseInt(this._request.status);return a>=400&&599>=a?new Error(a):0==a&&/^https?:/.test(location.protocol)?new Error(0):null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.URLUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){var g=s.ACTIVEX_VERSIONS[e];try{d=new ActiveXObject(g);break}catch(h){}}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 i=null;if(i=a.method==createjs.Methods.GET?createjs.URLUtils.buildURI(a.src,a.values):a.src,d.open(a.method||createjs.Methods.GET,i,!0),b&&d instanceof XMLHttpRequest&&1==this._xhrLevel&&(c.Origin=location.origin),a.values&&a.method==createjs.Methods.POST&&(c["Content-Type"]="application/x-www-form-urlencoded"),b||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest"),a.headers)for(var j in a.headers)c[j]=a.headers[j];for(j in c)d.setRequestHeader(j,c[j]);return d instanceof XMLHttpRequest&&void 0!==a.withCredentials&&(d.withCredentials=a.withCredentials),this._request=d,!0},a._clean=function(){clearTimeout(this._loadTimeout),null!=this._request.removeEventListener?(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)):(this._request.onloadstart=null,this._request.onprogress=null,this._request.onabort=null,this._request.onerror=null,this._request.ontimeout=null,this._request.onload=null,this._request.onreadystatechange=null)},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.Types.SOUND),createjs.DomUtils.isAudioTag(a)?this._tag=a:createjs.DomUtils.isAudioTag(a.src)?this._tag=a:createjs.DomUtils.isAudioTag(a.tag)&&(this._tag=createjs.DomUtils.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.Types.SOUND},a._createTag=function(a){var b=createjs.Elements.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("string"==typeof a)return console&&(console.warn||console.log)("Deprecated behaviour. Sound.play takes a configuration object instead of individual arguments. See docs for info."),(new createjs.PlayPropsConfig).set({interrupt:a});if(null==a||a instanceof b||a instanceof Object)return(new createjs.PlayPropsConfig).set(a);if(null==a)throw new Error("PlayProps configuration not recognized.")},a.set=function(a){if(null!=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,b._getMasterVolume=function(){return this._masterVolume},b.getVolume=createjs.deprecate(b._getMasterVolume,"Sound.getVolume"),b._setMasterVolume=function(a){if(null!=Number(a)&&(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.setVolume=createjs.deprecate(b._setMasterVolume,"Sound.setVolume"),b._masterMute=!1,b._getMute=function(){return this._masterMute},b.getMute=createjs.deprecate(b._getMute,"Sound.getMute"),b._setMute=function(a){if(null!=a&&(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)},b.setMute=createjs.deprecate(b._setMute,"Sound.setMute"),b._getCapabilities=function(){return null==b.activePlugin?null:b.activePlugin._capabilities},b.getCapabilities=createjs.deprecate(b._getCapabilities,"Sound.getCapabilities"),Object.defineProperties(b,{volume:{get:b._getMasterVolume,set:b._setMasterVolume},muted:{get:b._getMute,set:b._setMute},capabilities:{get:b._getCapabilities}}),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.initLoad=function(a){return"video"==a.type?!0: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+g.src);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",this._handleLoadComplete,this),h.on("error",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){var d=createjs.PlayPropsConfig.create(c),e=b.createInstance(a,d.startTime,d.duration),f=b._playInstance(e,d);return f||e._playFailed(),e},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),f=f||b._defaultPlayPropsHash[g.src],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.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.position),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){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.positionc.position)&&(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.getVolume=createjs.deprecate(this._getVolume,"AbstractSoundInstance.getVolume"),this.setVolume=createjs.deprecate(this._setVolume,"AbstractSoundInstance.setVolume"),this._pan=0,Object.defineProperty(this,"pan",{get:this._getPan,set:this._setPan}),this.getPan=createjs.deprecate(this._getPan,"AbstractSoundInstance.getPan"),this.setPan=createjs.deprecate(this._setPan,"AbstractSoundInstance.setPan"),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this._getStartTime,set:this._setStartTime}),this.getStartTime=createjs.deprecate(this._getStartTime,"AbstractSoundInstance.getStartTime"),this.setStartTime=createjs.deprecate(this._setStartTime,"AbstractSoundInstance.setStartTime"),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this._getDuration,set:this._setDuration}),this.getDuration=createjs.deprecate(this._getDuration,"AbstractSoundInstance.getDuration"),this.setDuration=createjs.deprecate(this._setDuration,"AbstractSoundInstance.setDuration"),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this._getPlaybackResource,set:this._setPlaybackResource}),d!==!1&&d!==!0&&this._setPlaybackResource(d),this.getPlaybackResource=createjs.deprecate(this._getPlaybackResource,"AbstractSoundInstance.getPlaybackResource"),this.setPlaybackResource=createjs.deprecate(this._setPlaybackResource,"AbstractSoundInstance.setPlaybackResource"),this._position=0,Object.defineProperty(this,"position",{get:this._getPosition,set:this._setPosition}),this.getPosition=createjs.deprecate(this._getPosition,"AbstractSoundInstance.getPosition"),this.setPosition=createjs.deprecate(this._setPosition,"AbstractSoundInstance.setPosition"),this._loop=0,Object.defineProperty(this,"loop",{get:this._getLoop,set:this._setLoop}),this.getLoop=createjs.deprecate(this._getLoop,"AbstractSoundInstance.getLoop"),this.setLoop=createjs.deprecate(this._setLoop,"AbstractSoundInstance.setLoop"),this._muted=!1,Object.defineProperty(this,"muted",{get:this._getMuted,set:this._setMuted}),this.getMuted=createjs.deprecate(this._getMuted,"AbstractSoundInstance.getMuted"),this.setMuted=createjs.deprecate(this._setMuted,"AbstractSoundInstance.setMuted"),this._paused=!1,Object.defineProperty(this,"paused",{get:this._getPaused,set:this._setPaused}),this.getPaused=createjs.deprecate(this._getPaused,"AbstractSoundInstance.getPaused"),this.setPaused=createjs.deprecate(this._setPaused,"AbstractSoundInstance.setPaused")},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a){var b=createjs.PlayPropsConfig.create(a);return this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(b),void(this._paused&&this._setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,b),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this._setPosition(a.offset),null!=a.loop&&this._setLoop(a.loop),null!=a.volume&&this._setVolume(a.volume),null!=a.pan&&this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),this},a.toString=function(){return"[AbstractSoundInstance]"},a._getPaused=function(){return this._paused},a._setPaused=function(a){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._playbackResource&&this._setDurationFromSource(),this},a._getPlaybackResource=function(){return this._playbackResource},a._getLoop=function(){return this._loop},a._setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this._setPosition(a.offset),this._setLoop(a.loop),this._setVolume(a.volume),this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),null!=this._playbackResource&&this._positionc;c++){var e=this._soundInstances[b][c];e.playbackResource=this._audioSources[b],this._soundInstances[b]=null}},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.Types.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._updatePan(),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._scratchBuffer=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){if(a){if(a.stop(0),a.disconnect(0),createjs.BrowserDetect.isIOS)try{a.buffer=b._scratchBuffer}catch(c){}a=null}return a},a._handleSoundReady=function(){this.gainNode.connect(b.destinationNode);var a=.001*this._duration,c=Math.min(.001*Math.max(0,this._position),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._scratchBuffer=null,b._unlocked=!1,b.DEFAULT_SAMPLE_RATE=44100,b.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return"file:"!=location.protocol||a||this._isFileXHRSupported()?(b._generateCapabilities(),null==b.context?!1:!0):!1},b.playEmptySound=function(){if(null!=b.context){var a=b.context.createBufferSource();a.buffer=b._scratchBuffer,a.connect(b.context.destination),a.start(0,0,0)}},b._isFileXHRSupported=function(){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&&(b.context=b._createAudioContext(),null==b.context))return null;null==b._scratchBuffer&&(b._scratchBuffer=b.context.createBuffer(1,1,22050)),b._compatibilitySetUp(),"ontouchstart"in window&&"running"!=b.context.state&&(b._unlock(),document.addEventListener("mousedown",b._unlock,!0),document.addEventListener("touchstart",b._unlock,!0),document.addEventListener("touchend",b._unlock,!0)),b._capabilities={panning:!0,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;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._createAudioContext=function(){var a=window.AudioContext||window.webkitAudioContext;if(null==a)return null;var c=new a;if(/(iPhone|iPad)/i.test(navigator.userAgent)&&c.sampleRate!==b.DEFAULT_SAMPLE_RATE){var d=c.createBuffer(1,1,b.DEFAULT_SAMPLE_RATE),e=c.createBufferSource();e.buffer=d,e.connect(c.destination),e.start(0),e.disconnect(),c.close(),c=new a}return c},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}},b._unlock=function(){b._unlocked||(b.playEmptySound(),"running"==b.context.state&&(document.removeEventListener("mousedown",b._unlock,!0),document.removeEventListener("touchend",b._unlock,!0),document.removeEventListener("touchstart",b._unlock,!0),b._unlocked=!0))},a.toString=function(){return"[WebAudioPlugin]"},a._addPropsToClasses=function(){var a=this._soundInstanceClass;a.context=this.context,a._scratchBuffer=b._scratchBuffer,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&&c.duration?1e3*c.duration:0},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))},a._setDurationFromSource=function(){this._duration=createjs.HTMLAudioTagPool.getDuration(this.src),this._playbackResource=null},createjs.HTMLAudioSoundInstance=createjs.promote(HTMLAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function HTMLAudioPlugin(){this.AbstractPlugin_constructor(),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.playbackResource=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
diff --git a/lib/soundjs-0.6.0.combined.js b/lib/soundjs.js
similarity index 75%
rename from lib/soundjs-0.6.0.combined.js
rename to lib/soundjs.js
index 57ca3b33..8797a113 100644
--- a/lib/soundjs-0.6.0.combined.js
+++ b/lib/soundjs.js
@@ -49,7 +49,7 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.version = /*=version*/"0.6.0"; // injected by build process
+ s.version = /*=version*/"1.0.0"; // injected by build process
/**
* The build date for this release in UTC format.
@@ -57,7 +57,7 @@ this.createjs = this.createjs || {};
* @type String
* @static
**/
- s.buildDate = /*=date*/"Thu, 11 Dec 2014 23:32:09 GMT"; // injected by build process
+ s.buildDate = /*=date*/"Thu, 12 Oct 2017 16:34:05 GMT"; // injected by build process
})();
@@ -78,13 +78,13 @@ this.createjs = this.createjs||{};
*
* function MySubClass() {}
* createjs.extend(MySubClass, MySuperClass);
- * ClassB.prototype.doSomething = function() { }
+ * MySubClass.prototype.doSomething = function() { }
*
* var foo = new MySubClass();
* console.log(foo instanceof MySuperClass); // true
* console.log(foo.prototype.constructor === MySubClass); // true
*
- * @method extends
+ * @method extend
* @param {Function} subclass The subclass.
* @param {Function} superclass The superclass to extend.
* @return {Function} Returns the subclass's new prototype.
@@ -158,7 +158,49 @@ createjs.promote = function(subclass, prefix) {
};
//##############################################################################
-// IndexOf.js
+// deprecate.js
+//##############################################################################
+
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Wraps deprecated methods so they still be used, but throw warnings to developers.
+ *
+ * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
+ *
+ * The recommended approach for deprecated properties is:
+ *
+ * try {
+ * Obj ect.defineProperties(object, {
+ * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
+ * readWriteProp: {
+ * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
+ * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
+ * });
+ * } catch (e) {}
+ *
+ * @method deprecate
+ * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
+ * @param {String} [name=null] The name of the method or property to display in the console warning.
+ * to deprecate properties.
+ * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
+ * logging the warning in the console.
+ */
+createjs.deprecate = function(fallbackMethod, name) {
+ "use strict";
+ return function() {
+ var msg = "Deprecated property or method '"+name+"'. See docs for info.";
+ console && (console.warn ? console.warn(msg) : console.log(msg));
+ return fallbackMethod && fallbackMethod.apply(this, arguments);
+ }
+};
+
+//##############################################################################
+// indexOf.js
//##############################################################################
this.createjs = this.createjs||{};
@@ -190,7 +232,7 @@ createjs.indexOf = function (array, searchElement){
};
//##############################################################################
-// Proxy.js
+// proxy.js
//##############################################################################
this.createjs = this.createjs||{};
@@ -241,49 +283,6 @@ this.createjs = this.createjs||{};
}());
-//##############################################################################
-// definePropertySupported.js
-//##############################################################################
-
-this.createjs = this.createjs||{};
-
-/**
- * @class Utility Methods
- */
-(function() {
- "use strict";
-
- /**
- * Boolean value indicating if Object.defineProperty is supported.
- *
- *
Example
- *
- * if (createjs.definePropertySupported) { // add getter / setter}
- *
- * @property definePropertySupported
- * @type {Boolean}
- * @default true
- */
- var t = Object.defineProperty ? true : false;
-
- // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
- var foo = {};
- try {
- Object.defineProperty(foo, "bar", {
- get: function () {
- return this._bar;
- },
- set: function (value) {
- this._bar = value;
- }
- });
- } catch (e) {
- t = false;
- }
-
- createjs.definePropertySupported = t;
-}());
-
//##############################################################################
// BrowserDetect.js
//##############################################################################
@@ -339,366 +338,381 @@ this.createjs = this.createjs||{};
// EventDispatcher.js
//##############################################################################
-this.createjs = this.createjs||{};
-
-(function() {
- "use strict";
-
-
-// constructor:
- /**
- * EventDispatcher provides methods for managing queues of event listeners and dispatching events.
- *
- * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the
- * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method.
- *
- * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the
- * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports
- * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent.
- *
- * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier
- * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The
- * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to
- * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}.
- *
- * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}}
- * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also
- * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener.
- *
- *
Example
- * Add EventDispatcher capabilities to the "MyClass" class.
- *
- * EventDispatcher.initialize(MyClass.prototype);
- *
- * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
- *
- * instance.addEventListener("eventName", handlerMethod);
- * function handlerMethod(event) {
- * console.log(event.target + " Was Clicked");
- * }
- *
- * Maintaining proper scope
- * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
- * method to subscribe to events simplifies this.
- *
- * instance.addEventListener("click", function(event) {
- * console.log(instance == this); // false, scope is ambiguous.
- * });
- *
- * instance.on("click", function(event) {
- * console.log(instance == this); // true, "on" uses dispatcher scope by default.
- * });
- *
- * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage scope.
- *
- *
- * @class EventDispatcher
- * @constructor
- **/
- function EventDispatcher() {
-
-
- // private properties:
- /**
- * @protected
- * @property _listeners
- * @type Object
- **/
- this._listeners = null;
-
- /**
- * @protected
- * @property _captureListeners
- * @type Object
- **/
- this._captureListeners = null;
- }
- var p = EventDispatcher.prototype;
-
-
-// static public methods:
- /**
- * Static initializer to mix EventDispatcher methods into a target object or prototype.
- *
- * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
- * EventDispatcher.initialize(myObject); // add to a specific instance
- *
- * @method initialize
- * @static
- * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
- * prototype.
- **/
- EventDispatcher.initialize = function(target) {
- target.addEventListener = p.addEventListener;
- target.on = p.on;
- target.removeEventListener = target.off = p.removeEventListener;
- target.removeAllEventListeners = p.removeAllEventListeners;
- target.hasEventListener = p.hasEventListener;
- target.dispatchEvent = p.dispatchEvent;
- target._dispatchEvent = p._dispatchEvent;
- target.willTrigger = p.willTrigger;
- };
-
-
-// public methods:
- /**
- * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
- * multiple callbacks getting fired.
- *
- *
Example
- *
- * displayObject.addEventListener("click", handleClick);
- * function handleClick(event) {
- * // Click happened.
- * }
- *
- * @method addEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function | Object} Returns the listener for chaining or assignment.
- **/
- p.addEventListener = function(type, listener, useCapture) {
- var listeners;
- if (useCapture) {
- listeners = this._captureListeners = this._captureListeners||{};
- } else {
- listeners = this._listeners = this._listeners||{};
- }
- var arr = listeners[type];
- if (arr) { this.removeEventListener(type, listener, useCapture); }
- arr = listeners[type]; // remove may have deleted the array
- if (!arr) { listeners[type] = [listener]; }
- else { arr.push(listener); }
- return listener;
- };
-
- /**
- * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
- * only run once, associate arbitrary data with the listener, and remove the listener.
- *
- * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
- * The created anonymous function is returned for use with .removeEventListener (or .off).
- *
- *
Example
- *
- * var listener = myBtn.on("click", handleClick, null, false, {count:3});
- * function handleClick(evt, data) {
- * data.count -= 1;
- * console.log(this == myBtn); // true - scope defaults to the dispatcher
- * if (data.count == 0) {
- * alert("clicked 3 times!");
- * myBtn.off("click", listener);
- * // alternately: evt.remove();
- * }
- * }
- *
- * @method on
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
- * the event is dispatched.
- * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
- * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
- * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
- * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
- **/
- p.on = function(type, listener, scope, once, data, useCapture) {
- if (listener.handleEvent) {
- scope = scope||listener;
- listener = listener.handleEvent;
- }
- scope = scope||this;
- return this.addEventListener(type, function(evt) {
- listener.call(scope, evt, data);
- once&&evt.remove();
- }, useCapture);
- };
-
- /**
- * Removes the specified event listener.
- *
- * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
- * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
- * closure will not work.
- *
- *
Example
- *
- * displayObject.removeEventListener("click", handleClick);
- *
- * @method removeEventListener
- * @param {String} type The string type of the event.
- * @param {Function | Object} listener The listener function or object.
- * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
- **/
- p.removeEventListener = function(type, listener, useCapture) {
- var listeners = useCapture ? this._captureListeners : this._listeners;
- if (!listeners) { return; }
- var arr = listeners[type];
- if (!arr) { return; }
- for (var i=0,l=arr.length; iExample
- *
- * // Remove all listeners
- * displayObject.removeAllEventListeners();
- *
- * // Remove all click listeners
- * displayObject.removeAllEventListeners("click");
- *
- * @method removeAllEventListeners
- * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
- **/
- p.removeAllEventListeners = function(type) {
- if (!type) { this._listeners = this._captureListeners = null; }
- else {
- if (this._listeners) { delete(this._listeners[type]); }
- if (this._captureListeners) { delete(this._captureListeners[type]); }
- }
- };
-
- /**
- * Dispatches the specified event to all listeners.
- *
- *
Example
- *
- * // Use a string event
- * this.dispatchEvent("complete");
- *
- * // Use an Event instance
- * var event = new createjs.Event("progress");
- * this.dispatchEvent(event);
- *
- * @method dispatchEvent
- * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
- * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
- * dispatchEvent will construct an Event instance with the specified type.
- * @return {Boolean} Returns the value of eventObj.defaultPrevented.
- **/
- p.dispatchEvent = function(eventObj) {
- if (typeof eventObj == "string") {
- // won't bubble, so skip everything if there's no listeners:
- var listeners = this._listeners;
- if (!listeners || !listeners[eventObj]) { return false; }
- eventObj = new createjs.Event(eventObj);
- } else if (eventObj.target && eventObj.clone) {
- // redispatching an active event object, so clone it:
- eventObj = eventObj.clone();
- }
- try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
-
- if (!eventObj.bubbles || !this.parent) {
- this._dispatchEvent(eventObj, 2);
- } else {
- var top=this, list=[top];
- while (top.parent) { list.push(top = top.parent); }
- var i, l=list.length;
-
- // capture & atTarget
- for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
- list[i]._dispatchEvent(eventObj, 1+(i==0));
- }
- // bubbling
- for (i=1; iExample
+ * Add EventDispatcher capabilities to the "MyClass" class.
+ *
+ * EventDispatcher.initialize(MyClass.prototype);
+ *
+ * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
+ *
+ * instance.addEventListener("eventName", handlerMethod);
+ * function handlerMethod(event) {
+ * console.log(event.target + " Was Clicked");
+ * }
+ *
+ * Maintaining proper scope
+ * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
+ * method to subscribe to events simplifies this.
+ *
+ * instance.addEventListener("click", function(event) {
+ * console.log(instance == this); // false, scope is ambiguous.
+ * });
+ *
+ * instance.on("click", function(event) {
+ * console.log(instance == this); // true, "on" uses dispatcher scope by default.
+ * });
+ *
+ * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
+ * scope.
+ *
+ * Browser support
+ * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
+ * requires modern browsers (IE9+).
+ *
+ *
+ * @class EventDispatcher
+ * @constructor
+ **/
+ function EventDispatcher() {
+
+
+ // private properties:
+ /**
+ * @protected
+ * @property _listeners
+ * @type Object
+ **/
+ this._listeners = null;
+
+ /**
+ * @protected
+ * @property _captureListeners
+ * @type Object
+ **/
+ this._captureListeners = null;
+ }
+ var p = EventDispatcher.prototype;
+
+// static public methods:
+ /**
+ * Static initializer to mix EventDispatcher methods into a target object or prototype.
+ *
+ * EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
+ * EventDispatcher.initialize(myObject); // add to a specific instance
+ *
+ * @method initialize
+ * @static
+ * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
+ * prototype.
+ **/
+ EventDispatcher.initialize = function(target) {
+ target.addEventListener = p.addEventListener;
+ target.on = p.on;
+ target.removeEventListener = target.off = p.removeEventListener;
+ target.removeAllEventListeners = p.removeAllEventListeners;
+ target.hasEventListener = p.hasEventListener;
+ target.dispatchEvent = p.dispatchEvent;
+ target._dispatchEvent = p._dispatchEvent;
+ target.willTrigger = p.willTrigger;
+ };
+
+
+// public methods:
+ /**
+ * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
+ * multiple callbacks getting fired.
+ *
+ *
Example
+ *
+ * displayObject.addEventListener("click", handleClick);
+ * function handleClick(event) {
+ * // Click happened.
+ * }
+ *
+ * @method addEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function | Object} Returns the listener for chaining or assignment.
+ **/
+ p.addEventListener = function(type, listener, useCapture) {
+ var listeners;
+ if (useCapture) {
+ listeners = this._captureListeners = this._captureListeners||{};
+ } else {
+ listeners = this._listeners = this._listeners||{};
+ }
+ var arr = listeners[type];
+ if (arr) { this.removeEventListener(type, listener, useCapture); }
+ arr = listeners[type]; // remove may have deleted the array
+ if (!arr) { listeners[type] = [listener]; }
+ else { arr.push(listener); }
+ return listener;
+ };
+
+ /**
+ * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
+ * only run once, associate arbitrary data with the listener, and remove the listener.
+ *
+ * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
+ * The wrapper function is returned for use with `removeEventListener` (or `off`).
+ *
+ * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
+ * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
+ * to `on` with the same params will create multiple listeners.
+ *
+ *
Example
+ *
+ * var listener = myBtn.on("click", handleClick, null, false, {count:3});
+ * function handleClick(evt, data) {
+ * data.count -= 1;
+ * console.log(this == myBtn); // true - scope defaults to the dispatcher
+ * if (data.count == 0) {
+ * alert("clicked 3 times!");
+ * myBtn.off("click", listener);
+ * // alternately: evt.remove();
+ * }
+ * }
+ *
+ * @method on
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
+ * the event is dispatched.
+ * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
+ * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
+ * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
+ * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
+ **/
+ p.on = function(type, listener, scope, once, data, useCapture) {
+ if (listener.handleEvent) {
+ scope = scope||listener;
+ listener = listener.handleEvent;
+ }
+ scope = scope||this;
+ return this.addEventListener(type, function(evt) {
+ listener.call(scope, evt, data);
+ once&&evt.remove();
+ }, useCapture);
+ };
+
+ /**
+ * Removes the specified event listener.
+ *
+ * Important Note: that you must pass the exact function reference used when the event was added. If a proxy
+ * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
+ * closure will not work.
+ *
+ *
Example
+ *
+ * displayObject.removeEventListener("click", handleClick);
+ *
+ * @method removeEventListener
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.removeEventListener = function(type, listener, useCapture) {
+ var listeners = useCapture ? this._captureListeners : this._listeners;
+ if (!listeners) { return; }
+ var arr = listeners[type];
+ if (!arr) { return; }
+ for (var i=0,l=arr.length; iIMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
+ * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
+ *
+ * @method off
+ * @param {String} type The string type of the event.
+ * @param {Function | Object} listener The listener function or object.
+ * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
+ **/
+ p.off = p.removeEventListener;
+
+ /**
+ * Removes all listeners for the specified type, or all listeners of all types.
+ *
+ *
Example
+ *
+ * // Remove all listeners
+ * displayObject.removeAllEventListeners();
+ *
+ * // Remove all click listeners
+ * displayObject.removeAllEventListeners("click");
+ *
+ * @method removeAllEventListeners
+ * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
+ **/
+ p.removeAllEventListeners = function(type) {
+ if (!type) { this._listeners = this._captureListeners = null; }
+ else {
+ if (this._listeners) { delete(this._listeners[type]); }
+ if (this._captureListeners) { delete(this._captureListeners[type]); }
+ }
+ };
+
+ /**
+ * Dispatches the specified event to all listeners.
+ *
+ *
Example
+ *
+ * // Use a string event
+ * this.dispatchEvent("complete");
+ *
+ * // Use an Event instance
+ * var event = new createjs.Event("progress");
+ * this.dispatchEvent(event);
+ *
+ * @method dispatchEvent
+ * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
+ * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
+ * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
+ * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
+ * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
+ * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
+ * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
+ **/
+ p.dispatchEvent = function(eventObj, bubbles, cancelable) {
+ if (typeof eventObj == "string") {
+ // skip everything if there's no listeners and it doesn't bubble:
+ var listeners = this._listeners;
+ if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
+ eventObj = new createjs.Event(eventObj, bubbles, cancelable);
+ } else if (eventObj.target && eventObj.clone) {
+ // redispatching an active event object, so clone it:
+ eventObj = eventObj.clone();
+ }
+
+ // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
+ try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
+
+ if (!eventObj.bubbles || !this.parent) {
+ this._dispatchEvent(eventObj, 2);
+ } else {
+ var top=this, list=[top];
+ while (top.parent) { list.push(top = top.parent); }
+ var i, l=list.length;
+
+ // capture & atTarget
+ for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
+ list[i]._dispatchEvent(eventObj, 1+(i==0));
+ }
+ // bubbling
+ for (i=1; irequired. The source can either be a
- * string (recommended), or an HTML tag.
+ * 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
@@ -1057,9 +1072,8 @@ this.createjs = this.createjs || {};
this.src = null;
/**
- * 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. See the {{#crossLink "AbstractLoader"}}{{/crossLink}}
- * class for the full list of supported types.
+ * 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
@@ -1104,14 +1118,14 @@ this.createjs = this.createjs || {};
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
+ * The request method used for HTTP calls. Both {{#crossLink "Methods/GET:property"}}{{/crossLink}} or
+ * {{#crossLink "Methods/POST:property"}}{{/crossLink}} request types are supported, and are defined as
* constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @property method
* @type {String}
- * @default get
+ * @default GET
*/
- this.method = createjs.LoadItem.GET;
+ this.method = createjs.Methods.GET;
/**
* An object hash of name/value pairs to send to the server.
@@ -1154,7 +1168,7 @@ this.createjs = this.createjs || {};
* @type {boolean}
* @default Anonymous
*/
- this.crossOrigin = "Anonymous";
+ this.crossOrigin = null;
/**
* The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
@@ -1163,22 +1177,31 @@ this.createjs = this.createjs || {};
* @type {Number}
* @default 8000 (8 seconds)
*/
- this.loadTimeout = 8000;
+ this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
};
var p = LoadItem.prototype = {};
var s = LoadItem;
/**
- * Create/validate a 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
- *
Objectss are returned as-is
+ *
Objects are returned with any needed properties added
*
* @method create
* @param {LoadItem|String|Object} value The load item value
- * @returns {Object|LoadItem}
+ * @returns {LoadItem|Object}
* @static
*/
s.create = function (value) {
@@ -1188,8 +1211,10 @@ this.createjs = this.createjs || {};
return item;
} else if (value instanceof s) {
return value;
- } else if (value instanceof Object) { // Don't modify object, allows users to attach random data to the item.
- // TODO: Disallow objects with no src?
+ } 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.");
@@ -1217,188 +1242,328 @@ this.createjs = this.createjs || {};
}());
//##############################################################################
-// RequestUtils.js
+// Methods.js
//##############################################################################
-(function () {
+this.createjs = this.createjs || {};
- /**
- * Utilities that assist with parsing load items, and determining file types, etc.
- * @class RequestUtils
- */
+(function() {
var s = {};
/**
- * The Regular Expression used to test file URLS for an absolute path.
- * @property ABSOLUTE_PATH
- * @type {RegExp}
+ * Defines a POST request, use for a method value when loading data.
+ * @property POST
+ * @type {string}
+ * @default post
* @static
*/
- s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
+ s.POST = "POST";
/**
- * The Regular Expression used to test file URLS for an absolute path.
- * @property RELATIVE_PATH
- * @type {RegExp}
+ * Defines a GET request, use for a method value when loading data.
+ * @property GET
+ * @type {string}
+ * @default get
* @static
*/
- s.RELATIVE_PATT = (/^[./]*?\//i);
+ s.GET = "GET";
+
+ createjs.Methods = s;
+}());
+
+//##############################################################################
+// Types.js
+//##############################################################################
+
+this.createjs = this.createjs || {};
+
+(function() {
+ var s = {};
/**
- * 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}
+ * 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.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
+ s.BINARY = "binary";
/**
- * 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.
+ * 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.parseURI = function (path) {
- var info = {absolute: false, relative: false};
- if (path == null) { return info; }
+ s.CSS = "css";
- // 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;
+ /**
+ * The preload type for font files.
+ * @property FONT
+ * @type {String}
+ * @default font
+ * @static
+ * @since 0.9.0
+ */
+ s.FONT = "font";
- // Relative
- } else if (s.RELATIVE_PATT.test(path)) {
- info.relative = true;
- }
+ /**
+ * The preload type for fonts specified with CSS (such as Google fonts)
+ * @property FONTCSS
+ * @type {String}
+ * @default fontcss
+ * @static
+ * @since 0.9.0
+ */
+ s.FONTCSS = "fontcss";
- // Extension
- if (match = path.match(s.EXTENSION_PATT)) {
- info.extension = match[1].toLowerCase();
- }
- return info;
- };
+ /**
+ * 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";
/**
- * 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.
+ * 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.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('&');
- };
+ s.JAVASCRIPT = "javascript";
/**
- * 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.
+ * 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.buildPath = function (src, data) {
- if (data == null) {
- return src;
- }
+ s.JSON = "json";
- var query = [];
- var idx = src.indexOf('?');
+ /**
+ * 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";
- if (idx != -1) {
- var q = src.slice(idx + 1);
- query = query.concat(q.split('&'));
- }
+ /**
+ * 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";
- if (idx != -1) {
- return src.slice(0, idx) + '?' + this._formatQueryString(data, query);
- } else {
- return src + '?' + this._formatQueryString(data, query);
- }
- };
+ /**
+ * 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";
/**
- * @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.
+ * 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.isCrossDomain = function (item) {
- var target = document.createElement("a");
- target.href = item.src;
+ s.VIDEO = "video";
- var host = document.createElement("a");
- host.href = location.href;
+ /**
+ * 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";
- var crossdomain = (target.hostname != "") &&
- (target.port != host.port ||
- target.protocol != host.protocol ||
- target.hostname != host.hostname);
- return crossdomain;
- };
+ /**
+ * The preload type for SVG files.
+ * @property SVG
+ * @type {String}
+ * @default svg
+ * @static
+ * @since 0.6.0
+ */
+ s.SVG = "svg";
/**
- * @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.
+ * 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.isLocal = function (item) {
- var target = document.createElement("a");
- target.href = item.src;
- return target.hostname == "" && target.protocol == "file:";
- };
+ s.TEXT = "text";
/**
- * 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.
+ * 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.isBinary = function (type) {
- switch (type) {
- case createjs.AbstractLoader.IMAGE:
- case createjs.AbstractLoader.BINARY:
- return true;
- default:
- return false;
- }
+ s.XML = "xml";
+
+ createjs.Types = s;
+}());
+
+//##############################################################################
+// Elements.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * Convenience methods for creating various elements used by PrelaodJS.
+ *
+ * @class DomUtils
+ */
+ var s = {};
+
+ s.a = function() {
+ return s.el("a");
+ }
+
+ s.svg = function() {
+ return s.el("svg");
+ }
+
+ s.object = function() {
+ return s.el("object");
+ }
+
+ s.image = function() {
+ return s.el("image");
+ }
+
+ s.img = function() {
+ return s.el("img");
+ }
+
+ s.style = function() {
+ return s.el("style");
+ }
+
+ s.link = function() {
+ return s.el("link");
+ }
+
+ s.script = function() {
+ return s.el("script");
+ }
+
+ s.audio = function() {
+ return s.el("audio");
+ }
+
+ s.video = function() {
+ return s.el("video");
+ }
+
+ s.text = function(value) {
+ return document.createTextNode(value);
+ }
+
+ s.el = function(name) {
+ return document.createElement(name);
+ }
+
+ createjs.Elements = s;
+
+}());
+
+//##############################################################################
+// DomUtils.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * A few utilities for interacting with the dom.
+ * @class DomUtils
+ */
+ var s = {
+ container: null
};
+ s.appendToHead = function (el) {
+ s.getHead().appendChild(el);
+ }
+
+ s.appendToBody = function (el) {
+ if (s.container == null) {
+ s.container = document.createElement("div");
+ s.container.id = "preloadjs-container";
+ var style = s.container.style;
+ style.visibility = "hidden";
+ style.position = "absolute";
+ style.width = s.container.style.height = "10px";
+ style.overflow = "hidden";
+ style.transform = style.msTransform = style.webkitTransform = style.oTransform = "translate(-10px, -10px)"; //LM: Not working
+ s.getBody().appendChild(s.container);
+ }
+ s.container.appendChild(el);
+ }
+
+ s.getHead = function () {
+ return document.head || document.getElementsByTagName("head")[0];
+ }
+
+ s.getBody = function () {
+ return document.body || document.getElementsByTagName("body")[0];
+ }
+
+ s.removeChild = function(el) {
+ if (el.parent) {
+ el.parent.removeChild(el);
+ }
+ }
+
/**
* Check if item is a valid HTMLImageElement
* @method isImageTag
@@ -1428,7 +1593,7 @@ this.createjs = this.createjs || {};
/**
* Check if item is a valid HTMLVideoElement
* @method isVideoTag
- * @param {Objectitem
+ * @param {Object} item
* @returns {Boolean}
* @static
*/
@@ -1436,7 +1601,44 @@ this.createjs = this.createjs || {};
if (window.HTMLVideoElement) {
return item instanceof HTMLVideoElement;
} else {
- false;
+ return false;
+ }
+ };
+
+ createjs.DomUtils = s;
+
+}());
+
+//##############################################################################
+// RequestUtils.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * Utilities that assist with parsing load items, and determining file types, etc.
+ * @class RequestUtils
+ */
+ var s = {};
+
+ /**
+ * 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.Types.IMAGE:
+ case createjs.Types.BINARY:
+ return true;
+ default:
+ return false;
}
};
@@ -1449,13 +1651,14 @@ this.createjs = this.createjs || {};
*/
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.Types.TEXT:
+ case createjs.Types.JSON:
+ case createjs.Types.MANIFEST:
+ case createjs.Types.XML:
+ case createjs.Types.CSS:
+ case createjs.Types.SVG:
+ case createjs.Types.JAVASCRIPT:
+ case createjs.Types.SPRITESHEET:
return true;
default:
return false;
@@ -1463,51 +1666,242 @@ this.createjs = this.createjs || {};
};
/**
- * 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.
+ * 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, `createjs.Types.IMAGE`). Will return `null` if
+ * the type can not be determined by the extension.
+ * @static
+ */
+ s.getTypeByExtension = function (extension) {
+ if (extension == null) {
+ return createjs.Types.TEXT;
+ }
+
+ switch (extension.toLowerCase()) {
+ case "jpeg":
+ case "jpg":
+ case "gif":
+ case "png":
+ case "webp":
+ case "bmp":
+ return createjs.Types.IMAGE;
+ case "ogg":
+ case "mp3":
+ case "webm":
+ return createjs.Types.SOUND;
+ case "mp4":
+ case "webm":
+ case "ts":
+ return createjs.Types.VIDEO;
+ case "json":
+ return createjs.Types.JSON;
+ case "xml":
+ return createjs.Types.XML;
+ case "css":
+ return createjs.Types.CSS;
+ case "js":
+ return createjs.Types.JAVASCRIPT;
+ case 'svg':
+ return createjs.Types.SVG;
+ default:
+ return createjs.Types.TEXT;
+ }
+ };
+
+ createjs.RequestUtils = s;
+
+}());
+
+//##############################################################################
+// URLUtils.js
+//##############################################################################
+
+(function () {
+
+ /**
+ * Utilities that assist with parsing load items, and determining file types, etc.
+ * @class URLUtils
+ */
+ 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,
+ * the pieces of the path (protocol, hostname, port, pathname, search, hash, host)
+ * as well as an optional 'extension` property, which is the lowercase extension.
+ *
+ * @static
+ */
+ s.parseURI = function (path) {
+ var info = {
+ absolute: false,
+ relative: false,
+ protocol: null,
+ hostname: null,
+ port: null,
+ pathname: null,
+ search: null,
+ hash: null,
+ host: null
+ };
+
+ if (path == null) { return info; }
+
+ // Inject the path parts.
+ var parser = createjs.Elements.a();
+ parser.href = path;
+
+ for (var n in info) {
+ if (n in parser) {
+ info[n] = parser[n];
+ }
+ }
+
+ // 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 buildURI
+ * @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.buildURI = 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 = createjs.Elements.a();
+ target.href = item.src;
+
+ var host = createjs.Elements.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.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;
- }
+ s.isLocal = function (item) {
+ var target = createjs.Elements.a();
+ target.href = item.src;
+ return target.hostname == "" && target.protocol == "file:";
};
- createjs.RequestUtils = s;
+ createjs.URLUtils = s;
}());
@@ -1525,11 +1919,11 @@ this.createjs = this.createjs || {};
* 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} The item to be loaded.
+ * @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.
- * @oaram {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
+ * @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
*/
@@ -1553,6 +1947,7 @@ this.createjs = this.createjs || {};
* @property canceled
* @type {Boolean}
* @default false
+ * @readonly
*/
this.canceled = false;
@@ -1587,7 +1982,18 @@ this.createjs = this.createjs || {};
* 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.
+ * asynchronous, such as creating a new image. The callback function is passed 2 parameters, which are callbacks
+ * to handle success and error conditions in the resultFormatter. Note that the resultFormatter method is
+ * called in the current scope, as well as the success and error callbacks.
+ *
+ *
Example asynchronous resultFormatter
+ *
+ * function _formatResult(loader) {
+ * return function(success, error) {
+ * if (errorCondition) { error(errorDetailEvent); }
+ * success(result);
+ * }
+ * }
* @property resultFormatter
* @type {Function}
* @default null
@@ -1665,170 +2071,29 @@ this.createjs = this.createjs || {};
var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
var s = AbstractLoader;
- /**
- * Defines a POST request, use for a method value when loading data.
- * @property POST
- * @type {string}
- * @default post
- */
- s.POST = "POST";
-
- /**
- * Defines a GET request, use for a method value when loading data.
- * @property GET
- * @type {string}
- * @default get
- */
- 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";
+ // Remove these @deprecated properties after 1.0
+ try {
+ Object.defineProperties(s, {
+ POST: { get: createjs.deprecate(function() { return createjs.Methods.POST; }, "AbstractLoader.POST") },
+ GET: { get: createjs.deprecate(function() { return createjs.Methods.GET; }, "AbstractLoader.GET") },
+
+ BINARY: { get: createjs.deprecate(function() { return createjs.Types.BINARY; }, "AbstractLoader.BINARY") },
+ CSS: { get: createjs.deprecate(function() { return createjs.Types.CSS; }, "AbstractLoader.CSS") },
+ FONT: { get: createjs.deprecate(function() { return createjs.Types.FONT; }, "AbstractLoader.FONT") },
+ FONTCSS: { get: createjs.deprecate(function() { return createjs.Types.FONTCSS; }, "AbstractLoader.FONTCSS") },
+ IMAGE: { get: createjs.deprecate(function() { return createjs.Types.IMAGE; }, "AbstractLoader.IMAGE") },
+ JAVASCRIPT: { get: createjs.deprecate(function() { return createjs.Types.JAVASCRIPT; }, "AbstractLoader.JAVASCRIPT") },
+ JSON: { get: createjs.deprecate(function() { return createjs.Types.JSON; }, "AbstractLoader.JSON") },
+ JSONP: { get: createjs.deprecate(function() { return createjs.Types.JSONP; }, "AbstractLoader.JSONP") },
+ MANIFEST: { get: createjs.deprecate(function() { return createjs.Types.MANIFEST; }, "AbstractLoader.MANIFEST") },
+ SOUND: { get: createjs.deprecate(function() { return createjs.Types.SOUND; }, "AbstractLoader.SOUND") },
+ VIDEO: { get: createjs.deprecate(function() { return createjs.Types.VIDEO; }, "AbstractLoader.VIDEO") },
+ SPRITESHEET: { get: createjs.deprecate(function() { return createjs.Types.SPRITESHEET; }, "AbstractLoader.SPRITESHEET") },
+ SVG: { get: createjs.deprecate(function() { return createjs.Types.SVG; }, "AbstractLoader.SVG") },
+ TEXT: { get: createjs.deprecate(function() { return createjs.Types.TEXT; }, "AbstractLoader.TEXT") },
+ XML: { get: createjs.deprecate(function() { return createjs.Types.XML; }, "AbstractLoader.XML") }
+ });
+ } catch (e) {}
// Events
/**
@@ -1867,7 +2132,7 @@ this.createjs = this.createjs || {};
* 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 {String} type The event type ("fileerror")
* @param {LoadItem|object} The item that encountered the error
* @since 0.6.0
*/
@@ -2135,13 +2400,13 @@ this.createjs = this.createjs || {};
* @return {Object} The formatted result
* @since 0.6.0
*/
- p.resultFormatter = null; //TODO: Add support for async formatting.
+ p.resultFormatter = null;
/**
* 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} The event that the internal request dispatches.
+ * @param {Event} event The event that the internal request dispatches.
* @protected
* @since 0.6.0
*/
@@ -2150,12 +2415,13 @@ this.createjs = this.createjs || {};
case "complete":
this._rawResult = event.target._response;
var result = this.resultFormatter && this.resultFormatter(this);
- var _this = this;
+ // The resultFormatter is asynchronous
if (result instanceof Function) {
- result(function(result) {
- _this._result = result;
- _this._sendComplete();
- });
+ result.call(this,
+ createjs.proxy(this._resultFormatSuccess, this),
+ createjs.proxy(this._resultFormatFailed, this)
+ );
+ // The result formatter is synchronous
} else {
this._result = result || this._rawResult;
this._sendComplete();
@@ -2173,20 +2439,33 @@ this.createjs = this.createjs || {};
case "abort":
case "timeout":
if (!this._isCanceled()) {
- this.dispatchEvent(event.type);
+ this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_" + event.type.toUpperCase() + "_ERROR"));
}
break;
}
};
/**
- * @method buildPath
- * @protected
- * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
- * instead.
+ * The "success" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
+ * functions.
+ * @method _resultFormatSuccess
+ * @param {Object} result The formatted result
+ * @private
+ */
+ p._resultFormatSuccess = function (result) {
+ this._result = result;
+ this._sendComplete();
+ };
+
+ /**
+ * The "error" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
+ * functions.
+ * @method _resultFormatSuccess
+ * @param {Object} error The error event
+ * @private
*/
- p.buildPath = function (src, data) {
- return createjs.RequestUtils.buildPath(src, data);
+ p._resultFormatFailed = function (event) {
+ this._sendError(event);
};
/**
@@ -2218,6 +2497,7 @@ this.createjs = this.createjs || {};
* @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) {
@@ -2228,6 +2508,8 @@ this.createjs = this.createjs || {};
// protected properties
this._tagSrcAttribute = "src";
+
+ this.on("initialize", this._updateXHR, this);
};
var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
@@ -2263,6 +2545,20 @@ this.createjs = this.createjs || {};
}
};
+ // protected methods
+ /**
+ * Before the item loads, set its mimeType and responseType.
+ * @property _updateXHR
+ * @param {Event} event
+ * @private
+ */
+ p._updateXHR = function (event) {
+ // Only exists for XHR
+ if (event.loader.setResponseType) {
+ event.loader.setResponseType("blob");
+ }
+ };
+
/**
* The result formatter for media files.
* @method _formatResult
@@ -2274,7 +2570,10 @@ this.createjs = this.createjs || {};
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.onstalled = null;
if (this._preferXHR) {
- loader.getTag().src = loader.getResult(true);
+ var URL = window.URL || window.webkitURL;
+ var result = loader.getResult(true);
+
+ loader.getTag().src = URL.createObjectURL(result);
}
return loader.getTag();
};
@@ -2382,35 +2681,30 @@ this.createjs = this.createjs || {};
*/
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 () {
- if (this._tag.parentNode == null) {
- window.document.body.appendChild(this._tag);
- this._addedToDOM = true;
- }
-
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) {
+ createjs.DomUtils.appendToBody(this._tag);
+ this._addedToDOM = true;
+ }
};
p.destroy = function() {
@@ -2438,6 +2732,16 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * 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
@@ -2448,11 +2752,21 @@ this.createjs = this.createjs || {};
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
@@ -2461,18 +2775,11 @@ this.createjs = this.createjs || {};
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);
}
- };
-
- p._hideTag = function() {
- this._startTagVisibility = this._tag.style.visibility;
- this._tag.style.visibility = "hidden";
- };
-
- p._showTag = function() {
- this._tag.style.visibility = this._startTagVisibility;
+ clearTimeout(this._loadTimeout);
};
/**
@@ -2521,8 +2828,14 @@ this.createjs = this.createjs || {};
// public methods
p.load = function () {
- this._tag.onstalled = createjs.proxy(this._handleStalled, this);
- this._tag.onprogress = createjs.proxy(this._handleProgress, this);
+ 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.
@@ -2565,8 +2878,8 @@ this.createjs = this.createjs || {};
// protected methods
p._clean = function () {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
- this._tag.onstalled = null;
- this._tag.onprogress = null;
+ this._tag.removeEventListener("stalled", this._stalledCallback);
+ this._tag.removeEventListener("progress", this._progressCallback);
this.TagRequest__clean();
};
@@ -2597,7 +2910,7 @@ this.createjs = this.createjs || {};
* for an overview of supported file properties.
* @extends AbstractLoader
*/
- function XHRRequest(item) {
+ function XHRRequest (item) {
this.AbstractRequest_constructor(item);
// protected properties
@@ -2724,15 +3037,28 @@ this.createjs = this.createjs || {};
}
//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);
+ if (this._request.addEventListener != null) {
+ 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);
+ } else {
+ // IE9 support
+ this._request.onloadstart = this._handleLoadStartProxy;
+ this._request.onprogress = this._handleProgressProxy;
+ this._request.onabort = this._handleAbortProxy;
+ this._request.onerror = this._handleErrorProxy;
+ this._request.ontimeout = this._handleTimeoutProxy;
- // 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);
+ // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
+ this._request.onload = this._handleLoadProxy;
+ this._request.onreadystatechange = this._handleReadyStateChangeProxy;
+ }
// Set up a timeout if we don't have XHR2
if (this._xhrLevel == 1) {
@@ -2741,10 +3067,10 @@ this.createjs = this.createjs || {};
// 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) {
+ if (!this._item.values) {
this._request.send();
- } else if (this._item.method == createjs.AbstractLoader.POST) {
- this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
+ } else {
+ this._request.send(createjs.URLUtils.formatQueryString(this._item.values));
}
} catch (error) {
this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
@@ -2752,6 +3078,11 @@ this.createjs = this.createjs || {};
};
p.setResponseType = function (type) {
+ // Some old browsers doesn't support blob, so we convert arraybuffer to blob after response is downloaded
+ if (type === 'blob') {
+ type = window.URL ? 'blob' : 'arraybuffer';
+ this._responseType = type;
+ }
this._request.responseType = type;
};
@@ -2857,6 +3188,9 @@ this.createjs = this.createjs || {};
/**
* 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.
+ *
+ * Note that This method uses {{#crossLink "_checkError"}}{{/crossLink}} to determine if the server has returned an
+ * error code.
* @method _handleLoad
* @param {Object} event The XHR load event.
* @private
@@ -2874,6 +3208,21 @@ this.createjs = this.createjs || {};
}
this._response = this._getResponse();
+ // Convert arraybuffer back to blob
+ if (this._responseType === 'arraybuffer') {
+ try {
+ this._response = new Blob([this._response]);
+ } catch (e) {
+ // Fallback to use BlobBuilder if Blob constructor is not supported
+ // Tested on Android 2.3 ~ 4.2 and iOS5 safari
+ window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
+ if (e.name === 'TypeError' && window.BlobBuilder) {
+ var builder = new BlobBuilder();
+ builder.append(this._response);
+ this._response = builder.getBlob();
+ }
+ }
+ }
this._clean();
this.dispatchEvent(new createjs.Event("complete"));
@@ -2888,30 +3237,36 @@ this.createjs = this.createjs || {};
*/
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.
+ * Determine if there is an error in the current load.
+ * Currently this checks the status of the request for problem codes, and not actual response content:
+ *
+ *
Status codes between 400 and 599 (HTTP error range)
+ *
A status of 0, but *only when the application is running on a server*. If the application is running
+ * on `file:`, then it may incorrectly treat an error on local (or embedded applications) as a successful
+ * load.
+ *
* @method _checkError
- * @return {int} If the request status returns an error code.
+ * @return {Error} An error with the status code in the `message` argument.
* @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);
+ if (status >= 400 && status <= 599) {
+ return new Error(status);
+ } else if (status == 0) {
+ if ((/^https?:/).test(location.protocol)) { return new Error(0); }
+ return null; // Likely an embedded app.
+ } else {
+ return null;
}
- 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.
@@ -2961,7 +3316,7 @@ this.createjs = this.createjs || {};
*/
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 crossdomain = createjs.URLUtils.isCrossDomain(item);
var headers = {};
// Create the request. Fallback to whatever support we have.
@@ -2976,11 +3331,19 @@ this.createjs = this.createjs || {};
for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
var axVersion = s.ACTIVEX_VERSIONS[i];
try {
- req = new ActiveXObject(axVersions);
+ req = new ActiveXObject(axVersion);
break;
- } catch (e) {}
+ } catch (e) {
+ }
+ }
+ if (req == null) {
+ return false;
}
- 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.
@@ -2992,21 +3355,21 @@ this.createjs = this.createjs || {};
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);
+ if (item.method == createjs.Methods.GET) {
+ src = createjs.URLUtils.buildURI(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);
+ req.open(item.method || createjs.Methods.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) {
+ if (item.values && item.method == createjs.Methods.POST) {
headers["Content-Type"] = "application/x-www-form-urlencoded";
}
@@ -3041,13 +3404,23 @@ this.createjs = this.createjs || {};
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);
+ if (this._request.removeEventListener != null) {
+ 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);
+ } else {
+ this._request.onloadstart = null;
+ this._request.onprogress = null;
+ this._request.onabort = null;
+ this._request.onerror = null;
+ this._request.ontimeout = null;
+ this._request.onload = null;
+ this._request.onreadystatechange = null;
+ }
};
p.toString = function () {
@@ -3076,18 +3449,19 @@ this.createjs = this.createjs || {};
* @class SoundLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
+ * @extends AbstractMediaLoader
* @constructor
*/
function SoundLoader(loadItem, preferXHR) {
- this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
+ this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.Types.SOUND);
// protected properties
- if (createjs.RequestUtils.isAudioTag(loadItem)) {
+ if (createjs.DomUtils.isAudioTag(loadItem)) {
this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
+ } else if (createjs.DomUtils.isAudioTag(loadItem.src)) {
this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
- this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
+ } else if (createjs.DomUtils.isAudioTag(loadItem.tag)) {
+ this._tag = createjs.DomUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
}
if (this._tag != null) {
@@ -3101,19 +3475,19 @@ this.createjs = this.createjs || {};
// 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}}.
+ * {{#crossLink "Types/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;
+ return item.type == createjs.Types.SOUND;
};
// protected methods
p._createTag = function (src) {
- var tag = document.createElement("audio");
+ var tag = createjs.Elements.audio();
tag.autoplay = false;
tag.preload = "none";
@@ -3126,6 +3500,239 @@ this.createjs = this.createjs || {};
}());
+//##############################################################################
+// 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
+ */
+
+//##############################################################################
+// 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 props = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
+ * createjs.Sound.play("mySound", props);
+ * // OR
+ * mySoundInstance.play(props);
+ *
+ * @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 (typeof(value) === "string") {
+ // Handle the old API gracefully.
+ console && (console.warn || console.log)("Deprecated behaviour. Sound.play takes a configuration object instead of individual arguments. See docs for info.");
+ return new createjs.PlayPropsConfig().set({interrupt:value});
+ } else if (value == null || value instanceof s || value instanceof Object) {
+ return new createjs.PlayPropsConfig().set(value);
+ } else if (value == null) {
+ throw new Error("PlayProps configuration 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) {
+ if (props != null) {
+ for (var n in props) { this[n] = props[n]; }
+ }
+ return this;
+ };
+
+ p.toString = function() {
+ return "[PlayPropsConfig]";
+ };
+
+ createjs.PlayPropsConfig = s;
+
+}());
+
//##############################################################################
// Sound.js
//##############################################################################
@@ -3164,14 +3771,15 @@ this.createjs = this.createjs || {};
*
*
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.on("fileload", 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.on("complete", this.handleComplete, this);
* instance.volume = 0.5;
* }
*
@@ -3184,37 +3792,20 @@ this.createjs = this.createjs || {};
*
* Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
* automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
- * load. As a result, it may 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.
+ * 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:event"}}{{/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 Audio Sprites, available as of version 0.6.0.
+ * 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.
*
- * 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 until now restricted how many
- * different sounds we could load 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 – 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.
- *
Audio sprites share the same core source, so if you have a sprite with 5 sounds and are limited to 2
- * concurrently playing instances, that means 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: {
@@ -3223,7 +3814,7 @@ this.createjs = this.createjs || {};
* {id:"sound2", startTime:1000, duration:400},
* {id:"sound3", startTime:1700, duration: 1000}
* ]}
- * }
+ * }
* ];
* createjs.Sound.alternateExtensions = ["mp3"];
* createjs.Sound.on("fileload", loadSound);
@@ -3231,26 +3822,60 @@ this.createjs = this.createjs || {};
* // 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.
+ * Mobile Playback
+ * Devices running iOS require the WebAudio context to be "unlocked" by playing at least one sound inside of a user-
+ * initiated event (such as touch/click). Earlier versions of SoundJS included a "MobileSafe" sample, but this is no
+ * longer necessary as of SoundJS 0.6.2.
+ *
+ *
+ * In SoundJS 0.4.1 and above, you can either initialize plugins or use the {{#crossLink "WebAudioPlugin/playEmptySound"}}{{/crossLink}}
+ * method in the call stack of a user input event to manually unlock the audio context.
+ *
+ *
+ * In SoundJS 0.6.2 and above, SoundJS will automatically listen for the first document-level "mousedown"
+ * and "touchend" event, and unlock WebAudio. This will continue to check these events until the WebAudio
+ * context becomes "unlocked" (changes from "suspended" to "running")
+ *
+ *
+ * Both the "mousedown" and "touchend" events can be used to unlock audio in iOS9+, the "touchstart" event
+ * will work in iOS8 and below. The "touchend" event will only work in iOS9 when the gesture is interpreted
+ * as a "click", so if the user long-presses the button, it will no longer work.
+ *
+ *
+ * When using the EaselJS Touch class,
+ * the "mousedown" event will not fire when a canvas is clicked, since MouseEvents are prevented, to ensure
+ * only touch events fire. To get around this, you can either rely on "touchend", or:
+ *
+ *
Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).
+ *
Set the `preventSelection` property on the EaselJS `Stage` to `false`.
+ *
+ * These settings may change how your application behaves, and are not recommended.
+ *
+ *
+ *
+ * Loading Alternate Paths and Extension-less Files
+ * SoundJS supports loading alternate paths and extension-less files by passing an object instead of a string for
+ * the `src` property, which is a hash using the format `{extension:"path", extension2:"path2"}`. These labels are
+ * how SoundJS determines if the browser will support the sound. This also enables multiple formats to live in
+ * different folders, or on CDNs, which often has completely different filenames for each file.
*
- * createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400});
+ * Priority is determined by the property order (first property is tried first). This is supported by both internal loading
+ * and loading with PreloadJS.
*
- * 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.
+ * Note: an id is required for playback.
*
*
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
- * }
+ * var sounds = {path:"./audioPath/",
+ * manifest: [
+ * {id: "cool", src: {mp3:"mp3/awesome.mp3", ogg:"noExtensionOggFile"}}
+ * ]};
+ *
+ * createjs.Sound.alternateExtensions = ["mp3"];
+ * createjs.Sound.addEventListener("fileload", handleLoad);
+ * createjs.Sound.registerSounds(sounds);
*
- *
Known Browser and OS issues
+ *
Known Browser and OS issues
* IE 9 HTML Audio 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
@@ -3259,22 +3884,23 @@ this.createjs = this.createjs || {};
* 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.
+ * 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.
+ * 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.
- *
Note HTMLAudioPlugin is not supported on iOS by default. See {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
- * for more details.
+ *
Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
+ * Mobile Playback above.
+ *
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.
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 can not be used to avoid cross domain issues.
*
* @class Sound
* @static
@@ -3392,10 +4021,11 @@ this.createjs = this.createjs || {};
* 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", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
+ * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
* @since 0.4.0
+ * @static
*/
- s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
+ 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
@@ -3407,6 +4037,7 @@ this.createjs = this.createjs || {};
* @type {Object}
* @since 0.4.0
* @default {m4a:"mp4"}
+ * @static
*/
s.EXTENSION_MAP = {
m4a:"mp4"
@@ -3418,7 +4049,7 @@ this.createjs = this.createjs || {};
* @property FILE_PATTERN
* @type {RegExp}
* @static
- * @protected
+ * @private
*/
s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
@@ -3443,47 +4074,211 @@ this.createjs = this.createjs || {};
* 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.
+ * 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
+ */
+
+ /**
+ * The internal volume level. Use {{#crossLink "Sound/volume:property"}}{{/crossLink}} to adjust the master volume.
+ * @property _masterVolume
+ * @type {number}
+ * @default 1
+ * @private
+ */
+ s._masterVolume = 1;
+
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _getMasterVolume
+ * @private
+ * @static
+ * @return {Number}
+ **/
+ s._getMasterVolume = function() {
+ return this._masterVolume;
+ };
+ // Sound.getMasterVolume is @deprecated. Remove for 1.1+
+ s.getVolume = createjs.deprecate(s._getMasterVolume, "Sound.getVolume");
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _setMasterVolume
+ * @static
+ * @private
+ **/
+ s._setMasterVolume = function(value) {
+ if (Number(value) == null) { return; }
+ 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);
+ }
+ }
+ };
+ // Sound.stMasterVolume is @deprecated. Remove for 1.1+
+ s.setVolume = createjs.deprecate(s._setMasterVolume, "Sound.setVolume");
+
+ /**
+ * 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;
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _getMute
+ * @returns {Boolean}
+ * @static
+ * @private
+ */
+ s._getMute = function () {
+ return this._masterMute;
+ };
+ // Sound.getMute is @deprecated. Remove for 1.1+
+ s.getMute = createjs.deprecate(s._getMute, "Sound.getMute");
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _setMute
+ * @param {Boolean} value The muted value
+ * @static
+ * @private
+ */
+ s._setMute = function (value) {
+ if (value == null) { return; }
+ 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);
+ }
+ }
+ };
+ // Sound.setMute is @deprecated. Remove for 1.1+
+ s.setMute = createjs.deprecate(s._setMute, "Sound.setMute");
+
+ /**
+ * 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 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
+ * var mp3 = createjs.Sound.capabilities.mp3;
*
- * @property alternateExtensions
- * @type {Array}
- * @since 0.5.2
+ * Note this property is read only.
+ *
+ * @property capabilities
+ * @type {Object}
+ * @static
+ * @readOnly
+ * @since 0.6.1
*/
- 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
+ * Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
+ * @returns {null}
+ * @private
*/
- s.activePlugin = null;
+ s._getCapabilities = function() {
+ if (s.activePlugin == null) { return null; }
+ return s.activePlugin._capabilities;
+ };
+ // Sound.getCapabilities is @deprecated. Remove for 1.1+
+ s.getCapabilities = createjs.deprecate(s._getCapabilities, "Sound.getCapabilities");
+
+ Object.defineProperties(s, {
+ volume: { get: s._getMasterVolume, set: s._setMasterVolume },
+ muted: { get: s._getMute, set: s._setMute },
+ capabilities: { get: s._getCapabilities }
+ });
// Class Private properties
/**
- * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
+ * Determines if the plugins have been registered. If false, the first call to {{#crossLink "play"}}{{/crossLink}} 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
+ * @private
*/
s._pluginsRegistered = false;
@@ -3492,42 +4287,19 @@ this.createjs = this.createjs || {};
* @property _lastID
* @type {Number}
* @static
- * @protected
+ * @private
*/
s._lastID = 0;
- /**
- * The master volume value, which affects all sounds. Use {{#crossLink "Sound/getVolume"}}{{/crossLink}} and
- * {{#crossLink "Sound/setVolume"}}{{/crossLink}} to modify the volume of all audio.
- * @property _masterVolume
- * @type {Number}
- * @default 1
- * @protected
- * @since 0.4.0
- */
- s._masterVolume = 1;
-
- /**
- * The master mute value, which affects all sounds. This is applies to all sound instances. This value can be set
- * through {{#crossLink "Sound/setMute"}}{{/crossLink}} and accessed via {{#crossLink "Sound/getMute"}}{{/crossLink}}.
- * @property _masterMute
- * @type {Boolean}
- * @default false
- * @protected
- * @static
- * @since 0.4.0
- */
- s._masterMute = false;
-
/**
* An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
- * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
+ * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/volume:property"}}{{/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
+ * @private
* @static
*/
s._instances = [];
@@ -3536,7 +4308,7 @@ this.createjs = this.createjs || {};
* An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
* @property _idHash
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._idHash = {};
@@ -3547,11 +4319,22 @@ this.createjs = this.createjs || {};
* and data.
* @property _preloadHash
* @type {Object}
- * @protected
+ * @private
* @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}
+ * @private
+ * @static
+ * @since 0.6.1
+ */
+ s._defaultPlayPropsHash = {};
+
// EventDispatcher methods:
s.addEventListener = null;
@@ -3601,9 +4384,9 @@ this.createjs = this.createjs || {};
*
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"}}{{/crossLink}}).
+ *
extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
* @static
- * @protected
+ * @private
*/
s.getPreloadHandlers = function () {
return {
@@ -3617,7 +4400,7 @@ this.createjs = this.createjs || {};
* Used to dispatch fileload events from internal loading.
* @method _handleLoadComplete
* @param event A loader event.
- * @protected
+ * @private
* @static
* @since 0.6.0
*/
@@ -3644,8 +4427,9 @@ this.createjs = this.createjs || {};
/**
* Used to dispatch error events from internal preloading.
* @param event
- * @protected
+ * @private
* @since 0.6.0
+ * @static
*/
s._handleLoadError = function(event) {
var src = event.target.getItem().src;
@@ -3720,6 +4504,7 @@ this.createjs = this.createjs || {};
* @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;}
@@ -3747,63 +4532,17 @@ this.createjs = this.createjs || {};
return (s.activePlugin != null);
};
- /**
- * 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.
- *
- * @method getCapabilities
- * @return {Object} An object containing the capabilities of the active plugin.
- * @static
- */
- s.getCapabilities = function () {
- if (s.activePlugin == null) {return null;}
- return s.activePlugin._capabilities;
- };
-
- /**
- * Get a specific capability of the active plugin. See {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} for a
- * full list of capabilities.
- *
- *
Example
- *
- * var maxAudioInstances = createjs.Sound.getCapability("tracks");
- *
- * @method getCapability
- * @param {String} key The capability to retrieve
- * @return {Number|Boolean} The value of the capability.
- * @static
- * @see getCapabilities
- */
- 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
+ * @private
* @static
*/
s.initLoad = function (loadItem) {
+ if (loadItem.type == "video") { return true; } // Don't handle video. PreloadJS's plugin model is really aggressive.
return s._registerSound(loadItem);
};
@@ -3823,13 +4562,19 @@ this.createjs = this.createjs || {};
s._registerSound = function (loadItem) {
if (!s.initializeDefaultPlugins()) {return false;}
- var details = s._parsePath(loadItem.src);
+ 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 = s.activePlugin.defaultNumChannels || null;
+ var numChannels = null;
if (data != null) {
if (!isNaN(data.channels)) {
numChannels = parseInt(data.channels);
@@ -3842,11 +4587,15 @@ this.createjs = this.createjs || {};
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, numChannels); // Note only HTML audio uses numChannels
+ var loader = s.activePlugin.register(loadItem);
SoundChannel.create(loadItem.src, numChannels);
@@ -3857,9 +4606,11 @@ this.createjs = this.createjs || {};
loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
}
- //if (loader.onload) {details.completeHandler = loader.onload;} // used by preloadJS
if (loader.type) {loadItem.type = loader.type;}
+ if (loadItem.defaultPlayProps) {
+ s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
+ }
return loader;
};
@@ -3873,34 +4624,39 @@ this.createjs = this.createjs || {};
* 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
- * @param {String} [id] An id specified by the user to play the sound later.
+ * @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 audioSprite array of objects in the following format {id, startTime, duration}.
+ * 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) {
- var loadItem = {src: src, id: id, data:data};
- if (src instanceof Object) {
+ 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 = basePath + src;}
+ if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + loadItem.src;}
var loader = s._registerSound(loadItem);
if(!loader) {return false;}
@@ -3909,8 +4665,8 @@ this.createjs = this.createjs || {};
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));
+ loader.on("complete", this._handleLoadComplete, this);
+ loader.on("error", this._handleLoadError, this);
s.activePlugin.preload(loader);
} else {
if (s._preloadHash[loadItem.src][0] == true) {return true;}
@@ -3926,10 +4682,12 @@ this.createjs = this.createjs || {};
*
*
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
@@ -3938,7 +4696,9 @@ this.createjs = this.createjs || {};
* @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 set an optional path property that will be prepended to the src of each object.
+ * 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.
@@ -3955,37 +4715,15 @@ this.createjs = this.createjs || {};
} 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);
+ returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
}
return returnValues;
};
- /**
- * Deprecated. Please use {{#crossLink "Sound/registerSounds"}}{{/crossLink} instead.
- *
- * @method registerManifest
- * @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.
- * @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.
- * @since 0.4.0
- * @depreacted
- */
- s.registerManifest = function(manifest, basePath) {
- try {
- console.log("createjs.Sound.registerManifest is deprecated, please use createjs.Sound.registerSounds.")
- } catch (error) {
-
- };
- return this.registerSounds(manifest, basePath);
- };
-
/**
* Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
@@ -3994,11 +4732,13 @@ this.createjs = this.createjs || {};
*
*
Example
*
- * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
* 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
+ * @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
@@ -4007,13 +4747,18 @@ this.createjs = this.createjs || {};
s.removeSound = function(src, basePath) {
if (s.activePlugin == null) {return false;}
- if (src instanceof Object) {src = src.src;}
- src = s._getSrcById(src).src;
- if (basePath != null) {src = basePath + src;}
+ if (src instanceof Object && src.src) {src = src.src;}
- var details = s._parsePath(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) {
@@ -4039,6 +4784,7 @@ this.createjs = this.createjs || {};
*
*
Example
*
+ * assetPath = "./myPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
@@ -4049,7 +4795,7 @@ this.createjs = this.createjs || {};
* @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 set an optional path property that will be prepended to the src of each object.
+ * 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.
@@ -4064,6 +4810,7 @@ this.createjs = this.createjs || {};
} 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);
@@ -4071,28 +4818,6 @@ this.createjs = this.createjs || {};
return returnValues;
};
- /**
- * Deprecated. Please use {{#crossLink "Sound/removeSounds"}}{{/crossLink}} instead.
- *
- * @method removeManifest
- * @param {Array} manifest An array of objects to remove. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: {srcOrID:srcURIorID}
- * @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 in manifest was
- * successfully removed.
- * @static
- * @since 0.4.1
- * @deprecated
- */
- s.removeManifest = function (manifest, basePath) {
- try {
- console.log("createjs.Sound.removeManifest is deprecated, please use createjs.Sound.removeSounds.");
- } catch (error) {
-
- };
- return s.removeSounds(manifest, basePath);
- };
-
/**
* Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
@@ -4128,6 +4853,7 @@ this.createjs = this.createjs || {};
* @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; }
@@ -4137,17 +4863,19 @@ this.createjs = this.createjs || {};
} 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
+ * 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
+ * @private
+ * @static
*/
s._parsePath = function (value) {
if (typeof(value) != "string") {value = value.toString();}
@@ -4157,7 +4885,7 @@ this.createjs = this.createjs || {};
var name = match[4];
var ext = match[5];
- var c = s.getCapabilities();
+ var c = s.capabilities;
var i = 0;
while (!c[ext]) {
ext = s.alternateExtensions[i++];
@@ -4169,15 +4897,49 @@ this.createjs = this.createjs || {};
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.
+ * @private
+ * @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.
+ * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to
+ * play, an 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 the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
+ * method, 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 still be returned, which will
+ * not play any audio, but will not generate errors.
*
*
Example
*
@@ -4185,49 +4947,25 @@ this.createjs = this.createjs || {};
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* createjs.Sound.play("myID");
- * // we can pass in options we want to set inside of an object, and store off AbstractSoundInstance for controlling
+ * // store off AbstractSoundInstance for controlling
* var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
- * // alternately, we can pass full source path and specify each argument individually
- * var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", createjs.Sound.INTERRUPT_ANY, 0, 0, -1, 1, 0);
* }
*
- * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
+ * NOTE: To create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* @method play
* @param {String} src The src or ID of the audio.
- * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
- * 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}}.
- * OR
- * This parameter can be 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).
- * @param {Number} [delay=0] The amount of time to delay the start of audio playback, in milliseconds.
- * @param {Number} [offset=0] The offset from the start of the audio to begin playback, in milliseconds.
- * @param {Number} [loop=0] How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- * @param {Number} [volume=1] The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- * @param {Number} [pan=0] The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
+ * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
+ * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
+ * @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) {
- if (interrupt instanceof Object) {
- delay = interrupt.delay;
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- startTime = interrupt.startTime;
- duration = interrupt.duration;
- interrupt = interrupt.interrupt;
-
- }
- var instance = s.createInstance(src, startTime, duration);
- var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan);
+ s.play = function (src, props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
+ var instance = s.createInstance(src, playProps.startTime, playProps.duration);
+ var ok = s._playInstance(instance, playProps);
if (!ok) {instance._playFailed();}
return instance;
};
@@ -4256,114 +4994,35 @@ this.createjs = this.createjs || {};
* @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
- */
- s.createInstance = function (src, startTime, duration) {
- if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}
-
- 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);
- } else {
- instance = new createjs.DefaultSoundInstance(src, startTime, duration);;
- }
-
- instance.uniqueId = s._lastID++;
-
- return instance;
- };
-
- /**
- * 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/setVolume"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.setVolume(0.5);
- *
- * @method setVolume
- * @param {Number} value The master volume value. The acceptable range is 0-1.
- * @static
- */
- 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);
- }
- }
- };
-
- /**
- * Get the master volume of Sound. The master volume is multiplied against each sound's individual volume.
- * To get individual sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * var masterVolume = createjs.Sound.getVolume();
- *
- * @method getVolume
- * @return {Number} The master volume, in a range of 0-1.
- * @static
- */
- s.getVolume = function () {
- return s._masterVolume;
- };
-
- /**
- * 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/setMute"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.setMute(true);
- *
- * @method setMute
- * @param {Boolean} value Whether the audio should be muted or not.
- * @return {Boolean} If the mute was set.
- * @static
+ * Unsupported extensions will return the default AbstractSoundInstance.
* @since 0.4.0
+ * @static
*/
- s.setMute = function (value) {
- if (value == null) {return false;}
+ s.createInstance = function (src, startTime, duration) {
+ if (!s.initializeDefaultPlugins()) { return new createjs.DefaultSoundInstance(src, startTime, duration); }
- 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);
+ 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);
}
- return true;
- };
- /**
- * Returns the global mute value. To get the mute value of an individual instance, use AbstractSoundInstance
- * {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * var muted = createjs.Sound.getMute();
- *
- * @method getMute
- * @return {Boolean} The mute value of Sound.
- * @static
- * @since 0.4.0
- */
- s.getMute = function () {
- return this._masterMute;
+ instance.uniqueId = s._lastID++;
+
+ return instance;
};
/**
@@ -4384,6 +5043,34 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * 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
@@ -4393,49 +5080,30 @@ this.createjs = this.createjs || {};
* control delays.
* @method _playInstance
* @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
- * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
- * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior"}}{{/crossLink}}.
- * OR
- * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
- * delay, offset, loop, volume, and pan (see the above code sample).
- * @param {Number} [delay=0] Time in milliseconds before playback begins.
- * @param {Number} [offset=instance.offset] Time into the sound to begin playback in milliseconds. Defaults to the
- * current value on the instance.
- * @param {Number} [loop=0] The number of times to loop the audio. Use 0 for no loops, and -1 for an infinite loop.
- * @param {Number} [volume] The volume of the sound between 0 and 1. Defaults to current instance value.
- * @param {Number} [pan] The pan of the sound between -1 and 1. Defaults to current instance value.
+ * @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
+ * @private
* @static
*/
- s._playInstance = function (instance, interrupt, delay, offset, loop, volume, pan) {
- if (interrupt instanceof Object) {
- delay = interrupt.delay;
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- interrupt = interrupt.interrupt;
- }
-
- interrupt = interrupt || s.defaultInterruptBehavior;
- if (delay == null) {delay = 0;}
- if (offset == null) {offset = instance.getPosition();}
- if (loop == null) {loop = instance.loop;}
- if (volume == null) {volume = instance.volume;}
- if (pan == null) {pan = instance.pan;}
-
- if (delay == 0) {
- var ok = s._beginPlaying(instance, interrupt, offset, loop, volume, pan);
+ 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.position;}
+ 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, interrupt, offset, loop, volume, pan);
- }, delay);
+ s._beginPlaying(instance, playProps);
+ }, playProps.delay);
instance.delayTimeoutId = delayTimeoutId;
}
@@ -4448,24 +5116,17 @@ this.createjs = this.createjs || {};
* 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 {String} [interrupt=none] How this sound interrupts other instances with the same source. Defaults to
- * {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}. Interrupts are defined as INTERRUPT_TYPE
- * constants on Sound.
- * @param {Number} [offset] Time in milliseconds into the sound to begin playback. Defaults to the current value on
- * the instance.
- * @param {Number} [loop=0] The number of times to loop the audio. Use 0 for no loops, and -1 for an infinite loop.
- * @param {Number} [volume] The volume of the sound between 0 and 1. Defaults to the current value on the instance.
- * @param {Number} [pan=instance.pan] The pan of the sound between -1 and 1. Defaults to current instance value.
+ * @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
+ * @private
* @static
*/
- s._beginPlaying = function (instance, interrupt, offset, loop, volume, pan) {
- if (!SoundChannel.add(instance, interrupt)) {
+ s._beginPlaying = function (instance, playProps) {
+ if (!SoundChannel.add(instance, playProps.interrupt)) {
return false;
}
- var result = instance._beginPlaying(offset, loop, volume, pan);
+ var result = instance._beginPlaying(playProps);
if (!result) {
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);}
@@ -4480,7 +5141,7 @@ this.createjs = this.createjs || {};
* @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
+ * @private
* @static
*/
s._getSrcById = function (value) {
@@ -4493,7 +5154,7 @@ this.createjs = this.createjs || {};
* instances themselves.
* @method _playFinished
* @param {AbstractSoundInstance} instance The instance that finished playback.
- * @protected
+ * @private
* @static
*/
s._playFinished = function (instance) {
@@ -4757,8 +5418,8 @@ this.createjs = this.createjs || {};
}
// 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())) {
+ if ((interrupt == Sound.INTERRUPT_EARLY && target.position < replacement.position) ||
+ (interrupt == Sound.INTERRUPT_LATE && target.position > replacement.position)) {
replacement = target;
}
}
@@ -4873,62 +5534,59 @@ this.createjs = this.createjs || {};
// private properties
- /**
- * Audio sprite property used to determine the starting offset.
- * @type {Number}
- * @default null
- * @protected
- */
- this._startTime = Math.max(0, startTime || 0);
- //TODO add a getter / setter for startTime?
-
-
// 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.
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getVolume"}}{{/crossLink}}.
*
* The actual output volume of a sound can be calculated using:
- * myInstance.volume * createjs.Sound.getVolume();
+ * myInstance.volume * createjs.Sound._getVolume();
*
* @property volume
* @type {Number}
* @default 1
*/
this._volume = 1;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "volume", {
- get: this.getVolume,
- set: this.setVolume
- });
- }
+ Object.defineProperty(this, "volume", {
+ get: this._getVolume,
+ set: this._setVolume
+ });
+ this.getVolume = createjs.deprecate(this._getVolume, "AbstractSoundInstance.getVolume");
+ this.setVolume = createjs.deprecate(this._setVolume, "AbstractSoundInstance.setVolume");
/**
* The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPan"}}{{/crossLink}}.
- * Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D 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;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "pan", {
- get: this.getPan,
- set: this.setPan
- });
- }
+ Object.defineProperty(this, "pan", {
+ get: this._getPan,
+ set: this._setPan
+ });
+ this.getPan = createjs.deprecate(this._getPan, "AbstractSoundInstance.getPan");
+ this.setPan = createjs.deprecate(this._setPan, "AbstractSoundInstance.setPan");
/**
- * The length of the audio clip, in milliseconds.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setDuration"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getDuration"}}{{/crossLink}}.
+ * 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
+ });
+ this.getStartTime = createjs.deprecate(this._getStartTime, "AbstractSoundInstance.getStartTime");
+ this.setStartTime = createjs.deprecate(this._setStartTime, "AbstractSoundInstance.setStartTime");
+
+ /**
+ * Sets or gets the length of the audio clip, value is in milliseconds.
*
* @property duration
* @type {Number}
@@ -4936,12 +5594,12 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._duration = Math.max(0, duration || 0);
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "duration", {
- get: this.getDuration,
- set: this.setDuration
- });
- }
+ Object.defineProperty(this, "duration", {
+ get: this._getDuration,
+ set: this._setDuration
+ });
+ this.getDuration = createjs.deprecate(this._getDuration, "AbstractSoundInstance.getDuration");
+ this.setDuration = createjs.deprecate(this._setDuration, "AbstractSoundInstance.setDuration");
/**
* Object that holds plugin specific resource need for audio playback.
@@ -4953,39 +5611,33 @@ this.createjs = this.createjs || {};
* @default null
*/
this._playbackResource = null;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "playbackResource", {
- get: this.getPlaybackResource,
- set: this.setPlaybackResource
- });
- }
- if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
+ Object.defineProperty(this, "playbackResource", {
+ get: this._getPlaybackResource,
+ set: this._setPlaybackResource
+ });
+ if(playbackResource !== false && playbackResource !== true) { this._setPlaybackResource(playbackResource); }
+ this.getPlaybackResource = createjs.deprecate(this._getPlaybackResource, "AbstractSoundInstance.getPlaybackResource");
+ this.setPlaybackResource = createjs.deprecate(this._setPlaybackResource, "AbstractSoundInstance.setPlaybackResource");
/**
* The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPosition"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPosition"}}{{/crossLink}}.
- *
* @property position
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._position = 0;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "position", {
- get: this.getPosition,
- set: this.setPosition
- });
- }
+ Object.defineProperty(this, "position", {
+ get: this._getPosition,
+ set: this._setPosition
+ });
+ this.getPosition = createjs.deprecate(this._getPosition, "AbstractSoundInstance.getPosition");
+ this.setPosition = createjs.deprecate(this._setPosition, "AbstractSoundInstance.setPosition");
/**
* The number of play loops remaining. Negative values will loop infinitely.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setLoop"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getLoop"}}{{/crossLink}}.
- *
* @property loop
* @type {Number}
* @default 0
@@ -4993,18 +5645,15 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._loop = 0;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "loop", {
- get: this.getLoop,
- set: this.setLoop
- });
- }
+ Object.defineProperty(this, "loop", {
+ get: this._getLoop,
+ set: this._setLoop
+ });
+ this.getLoop = createjs.deprecate(this._getLoop, "AbstractSoundInstance.getLoop");
+ this.setLoop = createjs.deprecate(this._setLoop, "AbstractSoundInstance.setLoop");
/**
- * Determines if the audio is currently muted.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setMute"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}}.
+ * Mutes or unmutes the current audio instance.
*
* @property muted
* @type {Boolean}
@@ -5012,30 +5661,26 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._muted = false;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "muted", {
- get: this.getMuted,
- set: this.setMuted
- });
- }
+ Object.defineProperty(this, "muted", {
+ get: this._getMuted,
+ set: this._setMuted
+ });
+ this.getMuted = createjs.deprecate(this._getMuted, "AbstractSoundInstance.getMuted");
+ this.setMuted = createjs.deprecate(this._setMuted, "AbstractSoundInstance.setMuted");
/**
- * Tells you if the audio is currently paused.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower.
- * Use {{#crossLink "AbstractSoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/resume:method"}}{{/crossLink}} to set.
+ * Pauses or resumes the current audio instance.
*
* @property paused
* @type {Boolean}
*/
this._paused = false;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "paused", {
- get: this.getPaused,
- set: this.setPaused
- });
- }
+ Object.defineProperty(this, "paused", {
+ get: this._getPaused,
+ set: this._setPaused
+ });
+ this.getPaused = createjs.deprecate(this._getPaused, "AbstractSoundInstance.getPaused");
+ this.setPaused = createjs.deprecate(this._setPaused, "AbstractSoundInstance.setPaused");
// Events
@@ -5086,7 +5731,6 @@ this.createjs = this.createjs || {};
var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);
-
// Public Methods:
/**
* Play an instance. This method is intended to be called on SoundInstances that already exist (created
@@ -5095,75 +5739,35 @@ this.createjs = this.createjs || {};
*
Example
*
* var myInstance = createjs.Sound.createInstance(mySrc);
- * myInstance.play({offset:1, loop:2, pan:0.5}); // options as object properties
- * myInstance.play(createjs.Sound.INTERRUPT_ANY); // options as parameters
+ * myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
*
- * Note that if this sound is already playing, this call will do nothing.
+ * 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] 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 Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- * OR
- * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
- * delay, offset, loop, volume, and pan (see the above code sample).
- * @param {Number} [delay=0] The delay in milliseconds before the sound starts
- * @param {Number} [offset=0] How far into the sound to begin playback, in milliseconds.
- * @param {Number} [loop=0] The number of times to loop the audio. Use -1 for infinite loops.
- * @param {Number} [volume=1] The volume of the sound, between 0 and 1.
- * @param {Number} [pan=0] The pan of the sound between -1 (left) and 1 (right). Note that pan is not supported
- * for HTML Audio.
+ * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
+ * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
- p.play = function (interrupt, delay, offset, loop, volume, pan) {
+ p.play = function (props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- if (interrupt instanceof Object) {
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- }
- if (offset != null) { this.setPosition(offset) }
- if (loop != null) { this.setLoop(loop); }
- if (volume != null) { this.setVolume(volume); }
- if (pan != null) { this.setPan(pan); }
- if (this._paused) { this.setPaused(false); }
+ this.applyPlayProps(playProps);
+ if (this._paused) { this._setPaused(false); }
return;
}
this._cleanUp();
- createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); // make this an event dispatch??
+ createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
return this;
};
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead.
- *
- * @method pause
- * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing.
- * @deprecated
- */
- p.pause = function () {
- if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;}
- this.setPaused(true);
- return true;
- };
-
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead.
- *
- * @method resume
- * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused.
- * @deprecated
- */
- p.resume = function () {
- if (!this._paused) {return false;}
- this.setPaused(false);
- return true;
- };
-
/**
* 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}}.
+ * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
+ *
+ * If you don't want to lose your position use yourSoundInstance.paused = true instead. {{#crossLink "AbstractSoundInstance/paused"}}{{/crossLink}}.
*
*
Example
*
@@ -5194,37 +5798,50 @@ this.createjs = this.createjs || {};
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
/**
- * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property,
- * and getPaused remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Returns true if the instance is currently paused.
- *
- * @method getPaused
- * @returns {boolean} If the instance is currently paused
+ * Please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property.
+ * @method _getPaused
+ * @protected
+ * @return {boolean} If the instance is currently paused
* @since 0.6.0
*/
- p.getPaused = function() {
+ p._getPaused = function() {
return this._paused;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPaused remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Pause or resume the instance. Note you can also resume playback with {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
- *
+ * Please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
+ * @method _setPaused
+ * @protected
* @param {boolean} value
* @since 0.6.0
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
- p.setPaused = function (value) {
+ 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;
@@ -5238,23 +5855,13 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property,
- * setVolume remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the volume of the instance.
- *
- *
Example
- *
- * myInstance.setVolume(0.5);
- *
- * Note that the master volume set using the Sound API method {{#crossLink "Sound/setVolume"}}{{/crossLink}}
- * will be applied to the instance volume.
- *
- * @method setVolume
- * @param value The volume to set, between 0 and 1.
+ * Please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ * @method _setVolume
+ * @protected
+ * @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) {
+ p._setVolume = function (value) {
if (value == this._volume) { return this; }
this._volume = Math.max(0, Math.min(1, value));
if (!this._muted) {
@@ -5264,59 +5871,24 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property,
- * getVolume remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the volume of the instance. The actual output volume of a sound can be calculated using:
- * myInstance.getVolume() * createjs.Sound.getVolume();
- *
- * @method getVolume
- * @return The current volume of the sound instance.
+ * Please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ * @method _getVolume
+ * @protected
+ * @return {Number} The current volume of the sound instance.
*/
- p.getVolume = function () {
+ p._getVolume = function () {
return this._volume;
};
/**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * @method setMute
- * @param {Boolean} value If the sound should be muted.
- * @return {Boolean} If the mute call succeeds.
- * @deprecated
- */
- p.setMute = function (value) {
- this.setMuted(value);
- };
-
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * @method getMute
- * @return {Boolean} If the sound is muted.
- * @deprecated
- */
- p.getMute = function () {
- return this._muted;
- };
-
- /**
- * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property,
- * setMuted exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be
- * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound muted.
- *
- *
Example
- *
- * myInstance.setMuted(true);
- *
- * @method setMute
+ * Please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ * @method _setMuted
+ * @protected
* @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) {
+ p._setMuted = function (value) {
if (value !== true && value !== false) {return;}
this._muted = value;
this._updateVolume();
@@ -5324,40 +5896,24 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property,
- * getMuted remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the mute value of the instance.
- *
- *
Example
- *
- * var isMuted = myInstance.getMuted();
- *
- * @method getMute
+ * Please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ * @method _getMuted
+ * @protected
* @return {Boolean} If the sound is muted.
* @since 0.6.0
*/
- p.getMuted = function () {
+ p._getMuted = function () {
return this._muted;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPan remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not
- * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
- * The default pan value is 0 (center).
- *
- *
Example
- *
- * myInstance.setPan(-1); // to the left!
- *
- * @method setPan
+ * Please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ * @method _setPan
+ * @protected
* @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) {
+ p._setPan = function (value) {
if(value == this._pan) { return this; }
this._pan = Math.max(-1, Math.min(1, value));
this._updatePan();
@@ -5365,59 +5921,36 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPan remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is
- * actually 3D audio.
- *
- *
Example
- *
- * var myPan = myInstance.getPan();
- *
- * @method getPan
+ * Please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ * @method _getPan
+ * @protected
* @return {Number} The value of the pan, between -1 (left) and 1 (right).
*/
- p.getPan = function () {
+ p._getPan = function () {
return this._pan;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPosition remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the position of the playhead of the instance in milliseconds.
- *
- *
Example
- *
- * var currentOffset = myInstance.getPosition();
- *
- * @method getPosition
+ * Please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ * @method _getPosition
+ * @protected
* @return {Number} The position of the playhead in the sound, in milliseconds.
*/
- p.getPosition = function () {
+ p._getPosition = function () {
if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- return this._calculateCurrentPosition(); // sets this._position
+ this._position = this._calculateCurrentPosition();
}
return this._position;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPosition remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or
- * stopped.
- *
- *
Example
- *
- * myInstance.setPosition(myInstance.getDuration()/2); // set audio to its halfway point.
- *
- * @method setPosition
+ * Please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ * @method _setPosition
+ * @protected
* @param {Number} value The position to place the playhead, in milliseconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
- p.setPosition = function (value) {
+ p._setPosition = function (value) {
this._position = Math.max(0, value);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._updatePosition();
@@ -5426,35 +5959,48 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property,
- * getDuration exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the duration of the instance, in milliseconds.
- * Note a sound needs to be loaded before it will have duration, unless it was set manually to create an audio sprite.
- *
- *
Example
- *
- * var soundDur = myInstance.getDuration();
- *
- * @method getDuration
+ * Please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ * @method _getStartTime
+ * @protected
+ * @return {Number} The startTime of the sound instance in milliseconds.
+ */
+ p._getStartTime = function () {
+ return this._startTime;
+ };
+
+ /**
+ * Please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ * @method _setStartTime
+ * @protected
+ * @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;
+ };
+
+ /**
+ * Please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ * @method _getDuration
+ * @protected
* @return {Number} The duration of the sound instance in milliseconds.
*/
- p.getDuration = function () {
+ p._getDuration = function () {
return this._duration;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property,
- * setDuration exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the duration of the audio. Generally this is not called, but it can be used to create an audio sprite out of an existing AbstractSoundInstance.
- *
- * @method setDuration
+ * Please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ * @method _setDuration
+ * @protected
* @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) {
+ p._setDuration = function (value) {
if (value == this._duration) { return this; }
this._duration = Math.max(0, value || 0);
this._updateDuration();
@@ -5462,70 +6008,57 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPlaybackResource exists to allow support for IE8 with FlashAudioPlugin.
- *
- * An object containing any resources needed for audio playback, set by the plugin.
- * Only meant for use by advanced users.
- *
- * @method setPlayback
+ * Please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ * @method _setPlaybackResource
+ * @protected
* @param {Object} value The new playback resource.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
**/
- p.setPlaybackResource = function (value) {
+ p._setPlaybackResource = function (value) {
this._playbackResource = value;
- if (this._duration == 0) { this._setDurationFromSource(); }
+ if (this._duration == 0 && this._playbackResource) { this._setDurationFromSource(); }
return this;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPlaybackResource exists to allow support for IE8 with FlashAudioPlugin.
- *
- * An object containing any resources needed for audio playback, usually set by the plugin.
- *
- * @method setPlayback
+ * Please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ * @method _getPlaybackResource
+ * @protected
* @param {Object} value The new playback resource.
* @return {Object} playback resource used for playing audio
* @since 0.6.0
**/
- p.getPlaybackResource = function () {
+ p._getPlaybackResource = function () {
return this._playbackResource;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property,
- * getLoop exists to allow support for IE8 with FlashAudioPlugin.
- *
- * The number of play loops remaining. Negative values will loop infinitely.
- *
- * @method getLoop
+ * Please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ * @method _getLoop
+ * @protected
* @return {number}
* @since 0.6.0
**/
- p.getLoop = function () {
+ p._getLoop = function () {
return this._loop;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property,
- * setLoop exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the number of play loops remaining.
- *
- * @method setLoop
+ * Please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ * @method _setLoop
+ * @protected
* @param {number} value The number of times to loop after play.
* @since 0.6.0
*/
- p.setLoop = function (value) {
+ p._setLoop = function (value) {
if(this._playbackResource != null) {
// remove looping
if (this._loop != 0 && value == 0) {
this._removeLooping(value);
}
// add looping
- if (this._loop == 0 && value != 0) {
+ else if (this._loop == 0 && value != 0) {
this._addLooping(value);
}
}
@@ -5573,18 +6106,20 @@ this.createjs = this.createjs || {};
* 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 {Number} offset How far into the sound to begin playback, in milliseconds.
- * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops.
- * @param {Number} volume The volume of the sound, between 0 and 1.
- * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio.
+ * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If playback succeeded.
* @protected
*/
- p._beginPlaying = function (offset, loop, volume, pan) {
- this.setPosition(offset);
- this.setLoop(loop);
- this.setVolume(volume);
- this.setPan(pan);
+ // 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;
@@ -5661,6 +6196,16 @@ this.createjs = this.createjs || {};
// 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
@@ -5682,8 +6227,8 @@ this.createjs = this.createjs || {};
};
/**
- * Internal function that calculates the current position of the playhead and sets it on this._position
- * @method _updatePosition
+ * Internal function that calculates the current position of the playhead and sets this._position to that value
+ * @method _calculateCurrentPosition
* @protected
* @since 0.6.0
*/
@@ -5704,20 +6249,22 @@ this.createjs = this.createjs || {};
/**
* 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 () {
+ 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 () {
+ p._addLooping = function (value) {
// plugin specific code
};
@@ -5763,7 +6310,7 @@ this.createjs = this.createjs || {};
/**
* Internal function called when AbstractSoundInstance has played to end and is looping
- * @method _handleCleanUp
+ * @method _handleLoop
* @protected
* @since 0.6.0
*/
@@ -5831,6 +6378,15 @@ this.createjs = this.createjs || {};
*/
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}
@@ -5847,16 +6403,14 @@ this.createjs = this.createjs || {};
};
var p = AbstractPlugin.prototype;
-
// Static Properties:
// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
/**
- * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
- * method and is used internally.
+ * The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
- * @protected
+ * @private
* @static
*/
AbstractPlugin._capabilities = null;
@@ -5879,17 +6433,17 @@ this.createjs = this.createjs || {};
* can use to assist with preloading.
* @method register
* @param {String} loadItem An Object containing the source of the audio
- * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time.
* Note that not every plugin will manage this value.
* @return {Object} A result object, containing a "tag" for preloading purposes.
*/
- p.register = function (loadItem, instances) {
+ 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] = [];
- if(this._loaders[loadItem.src]) {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?
- var loader = new this._loaderClass(loadItem);
- loader.on("complete", createjs.proxy(this._handlePreloadComplete, this));
+ loader = new this._loaderClass(loadItem);
+ loader.on("complete", this._handlePreloadComplete, this);
this._loaders[loadItem.src] = loader;
return loader;
};
@@ -5901,7 +6455,7 @@ this.createjs = this.createjs || {};
* @param {Loader} loader The sound URI to load.
*/
p.preload = function (loader) {
- loader.on("error", createjs.proxy(this._handlePreloadError, this));
+ loader.on("error", this._handlePreloadError, this);
loader.load();
};
@@ -5967,12 +6521,18 @@ this.createjs = this.createjs || {};
this.preload(this.register(src));
}
var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
- this._soundInstances[src].push(si);
+ if(this._soundInstances[src]){
+ this._soundInstances[src].push(si);
+ }
+
+ // Plugins that don't have a setVolume should implement a setMasterVolune/setMasterMute
+ // So we have to check that here.
+ si.setMasterVolume && si.setMasterVolume(createjs.Sound.volume);
+ si.setMasterMute && si.setMasterMute(createjs.Sound.muted);
+
return si;
};
- // TODO Volume & mute Getter / Setter??
- // TODO change calls to return nothing or this for chaining??
// 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.
@@ -5990,7 +6550,7 @@ this.createjs = this.createjs || {};
/**
* Get the master volume of the plugin, which affects all SoundInstances.
* @method getVolume
- * @return The volume level, between 0 and 1.
+ * @return {Number} The volume level, between 0 and 1.
*/
p.getVolume = function () {
return this._volume;
@@ -6000,7 +6560,7 @@ this.createjs = this.createjs || {};
* 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.
+ * the mute value of Sound {{#crossLink "Sound/muted:property"}}{{/crossLink}}, so this property is not used here.
* @return {Boolean} If the mute call succeeds.
*/
p.setMute = function (value) {
@@ -6018,6 +6578,7 @@ this.createjs = this.createjs || {};
/**
* Handles internal preload completion.
* @method _handlePreloadComplete
+ * @param event
* @protected
*/
p._handlePreloadComplete = function (event) {
@@ -6025,13 +6586,14 @@ this.createjs = this.createjs || {};
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]);
+ item.playbackResource = this._audioSources[src];
// ToDo consider adding play call here if playstate == playfailed
+ this._soundInstances[src] = null;
}
};
/**
- * Handles internal preload erros
+ * Handles internal preload errors
* @method _handlePreloadError
* @param event
* @protected
@@ -6067,12 +6629,11 @@ this.createjs = this.createjs || {};
*
* @class WebAudioLoader
* @param {String} loadItem The item to be loaded
- * @param {Object} flash The flash instance that will do the preloading.
* @extends XHRRequest
* @protected
*/
function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
+ this.AbstractLoader_constructor(loadItem, true, createjs.Types.SOUND);
};
var p = createjs.extend(Loader, createjs.AbstractLoader);
@@ -6102,14 +6663,14 @@ this.createjs = this.createjs || {};
// 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._handleError, this));
+ createjs.proxy(this._sendError, this));
};
/**
* The audio has been decoded.
* @method handleAudioDecoded
- * @param decoded
+ * @param decoded
* @protected
*/
p._handleAudioDecoded = function (decodedAudio) {
@@ -6168,6 +6729,7 @@ this.createjs = this.createjs || {};
this.panNode = s.context.createPanner();
this.panNode.panningModel = s._panningModel;
this.panNode.connect(this.gainNode);
+ this._updatePan();
/**
* NOTE this is only intended for use by advanced users.
@@ -6231,6 +6793,16 @@ this.createjs = this.createjs || {};
*/
s.context = null;
+ /**
+ * Note this is only intended for use by advanced users.
+ * The scratch buffer that will be assigned to the buffer property of a source node on close.
+ * This is and should be the same scratch buffer referenced by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
+ * @property _scratchBuffer
+ * @type {AudioBufferSourceNode}
+ * @static
+ */
+ s._scratchBuffer = null;
+
/**
* Note this is only intended for use by advanced users.
* Audio node from WebAudioPlugin that sequences to context.destination
@@ -6273,11 +6845,11 @@ this.createjs = this.createjs || {};
// z need to be -0.5 otherwise the sound only plays in left, right, or center
};
- p._removeLooping = function() {
+ p._removeLooping = function(value) {
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
- p._addLooping = function() {
+ p._addLooping = function(value) {
if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
};
@@ -6297,7 +6869,7 @@ this.createjs = this.createjs || {};
clearTimeout(this._soundCompleteTimeout);
- this._playbackStartTime = 0; // This is used by getPosition
+ this._playbackStartTime = 0; // This is used by _getPosition
};
/**
@@ -6312,6 +6884,11 @@ this.createjs = this.createjs || {};
if(audioNode) {
audioNode.stop(0);
audioNode.disconnect(0);
+ // necessary to prevent leak on iOS Safari 7-9. will throw in almost all other
+ // browser implementations.
+ if ( createjs.BrowserDetect.isIOS ) {
+ try { audioNode.buffer = s._scratchBuffer; } catch(e) {}
+ }
audioNode = null;
}
return audioNode;
@@ -6320,8 +6897,8 @@ this.createjs = this.createjs || {};
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
- var dur = this._duration * 0.001;
- var pos = this._position * 0.001;
+ var dur = this._duration * 0.001,
+ pos = Math.min(Math.max(0, this._position) * 0.001, dur);
this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
@@ -6403,8 +6980,10 @@ this.createjs = this.createjs || {};
};
p._updateDuration = function () {
- this._pause();
- this._resume();
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._pause();
+ this._resume();
+ }
};
createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
@@ -6427,18 +7006,29 @@ this.createjs = this.createjs || {};
*
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.
- *
+ *
+ * mp3 audio files do not load properly on all windows machines, reported here.
+ * For this reason it is recommended to pass another FireFox-supported type (i.e. ogg) as the default
+ * extension, until this bug is resolved
+ *
+ *
* 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.
- *
+ *
+ * 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.
+ *
+ *
+ * Sound is initially muted and will only unmute through play being called inside a user initiated event
+ * (touch/click). Please read the mobile playback notes in the the {{#crossLink "Sound"}}{{/crossLink}}
+ * class for a full overview of the limitations, and how to get around them.
+ *
+ *
+ * A bug exists that will distort un-cached 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 sample rate.
+ *
*
* @class WebAudioPlugin
* @extends AbstractPlugin
@@ -6458,15 +7048,6 @@ this.createjs = this.createjs || {};
*/
this._panningModel = s._panningModel;;
- /**
- * The internal master volume value of the plugin.
- * @property _volume
- * @type {Number}
- * @default 1
- * @protected
- */
- this._volume = 1;
-
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
@@ -6506,7 +7087,6 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);
-
// Static Properties
var s = WebAudioPlugin;
/**
@@ -6515,7 +7095,7 @@ this.createjs = this.createjs || {};
* @property _capabilities
* @type {Object}
* @default null
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
@@ -6524,7 +7104,7 @@ this.createjs = this.createjs || {};
* 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
+ * @private
* @static
*/
s._panningModel = "equalpower";
@@ -6542,6 +7122,38 @@ this.createjs = this.createjs || {};
*/
s.context = null;
+ /**
+ * The scratch buffer that will be assigned to the buffer property of a source node on close.
+ * Works around an iOS Safari bug: https://github.com/CreateJS/SoundJS/issues/102
+ *
+ * Advanced users can set this to an existing source node, but must do so before they call
+ * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
+ *
+ * @property _scratchBuffer
+ * @type {AudioBuffer}
+ * @private
+ * @static
+ */
+ s._scratchBuffer = null;
+
+ /**
+ * Indicated whether audio on iOS has been unlocked, which requires a touchend/mousedown event that plays an
+ * empty sound.
+ * @property _unlocked
+ * @type {boolean}
+ * @since 0.6.2
+ * @private
+ */
+ s._unlocked = false;
+
+ /**
+ * The default sample rate used when checking for iOS compatibility. See {{#crossLink "WebAudioPlugin/_createAudioContext"}}{{/crossLink}}.
+ * @property DEFAULT_SAMPLE_REATE
+ * @type {number}
+ * @default 44100
+ * @static
+ */
+ s.DEFAULT_SAMPLE_RATE = 44100;
// Static Public Methods
/**
@@ -6577,8 +7189,9 @@ this.createjs = this.createjs || {};
* @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.buffer = s._scratchBuffer;
source.connect(s.context.destination);
source.start(0, 0, 0);
};
@@ -6590,7 +7203,7 @@ this.createjs = this.createjs || {};
* @method _isFileXHRSupported
* @return {Boolean} If XHR is supported.
* @since 0.4.2
- * @protected
+ * @private
* @static
*/
s._isFileXHRSupported = function() {
@@ -6619,11 +7232,11 @@ this.createjs = this.createjs || {};
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -6632,19 +7245,22 @@ this.createjs = this.createjs || {};
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.context = s._createAudioContext();
+ if (s.context == null) { return null; }
+ }
+ if (s._scratchBuffer == null) {
+ s._scratchBuffer = s.context.createBuffer(1, 1, 22050);
}
s._compatibilitySetUp();
- // playing this inside of a touch event will enable audio on iOS, which starts muted
- s.playEmptySound();
+ // Listen for document level clicks to unlock WebAudio on iOS. See the _unlock method.
+ if ("ontouchstart" in window && s.context.state != "running") {
+ s._unlock(); // When played inside of a touch event, this will enable audio on iOS immediately.
+ document.addEventListener("mousedown", s._unlock, true);
+ document.addEventListener("touchstart", s._unlock, true);
+ document.addEventListener("touchend", s._unlock, true);
+ }
s._capabilities = {
panning:true,
@@ -6668,6 +7284,43 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * Create an audio context for the sound.
+ *
+ * This method handles both vendor prefixes (specifically webkit support), as well as a case on iOS where
+ * audio played with a different sample rate may play garbled when first started. The default sample rate is
+ * 44,100, however it can be changed using the {{#crossLink "WebAudioPlugin/DEFAULT_SAMPLE_RATE:property"}}{{/crossLink}}.
+ * @method _createAudioContext
+ * @return {AudioContext | webkitAudioContext}
+ * @private
+ * @static
+ * @since 1.0.0
+ */
+ s._createAudioContext = function() {
+ // Slightly modified version of https://github.com/Jam3/ios-safe-audio-context
+ // Resolves issues with first-run contexts playing garbled on iOS.
+ var AudioCtor = (window.AudioContext || window.webkitAudioContext);
+ if (AudioCtor == null) { return null; }
+ var context = new AudioCtor();
+
+ // Check if hack is necessary. Only occurs in iOS6+ devices
+ // and only when you first boot the iPhone, or play a audio/video
+ // with a different sample rate
+ if (/(iPhone|iPad)/i.test(navigator.userAgent)
+ && context.sampleRate !== s.DEFAULT_SAMPLE_RATE) {
+ var buffer = context.createBuffer(1, 1, s.DEFAULT_SAMPLE_RATE),
+ dummy = context.createBufferSource();
+ dummy.buffer = buffer;
+ dummy.connect(context.destination);
+ dummy.start(0);
+ dummy.disconnect();
+ context.close() // dispose old context
+
+ context = new AudioCtor();
+ }
+ return context;
+ }
+
/**
* Set up compatibility if only deprecated web audio calls are supported.
* See http://www.w3.org/TR/webaudio/#DeprecationNotes
@@ -6676,7 +7329,7 @@ this.createjs = this.createjs || {};
*
* @method _compatibilitySetUp
* @static
- * @protected
+ * @private
* @since 0.4.2
*/
s._compatibilitySetUp = function() {
@@ -6696,6 +7349,29 @@ this.createjs = this.createjs || {};
s._panningModel = 0;
};
+ /**
+ * Try to unlock audio on iOS. This is triggered from either WebAudio plugin setup (which will work if inside of
+ * a `mousedown` or `touchend` event stack), or the first document touchend/mousedown event. If it fails (touchend
+ * will fail if the user presses for too long, indicating a scroll event instead of a click event.
+ *
+ * Note that earlier versions of iOS supported `touchstart` for this, but iOS9 removed this functionality. Adding
+ * a `touchstart` event to support older platforms may preclude a `mousedown` even from getting fired on iOS9, so we
+ * stick with `mousedown` and `touchend`.
+ * @method _unlock
+ * @since 0.6.2
+ * @private
+ */
+ s._unlock = function() {
+ if (s._unlocked) { return; }
+ s.playEmptySound();
+ if (s.context.state == "running") {
+ document.removeEventListener("mousedown", s._unlock, true);
+ document.removeEventListener("touchend", s._unlock, true);
+ document.removeEventListener("touchstart", s._unlock, true);
+ s._unlocked = true;
+ }
+ };
+
// Public Methods
p.toString = function () {
@@ -6714,6 +7390,7 @@ this.createjs = this.createjs || {};
p._addPropsToClasses = function() {
var c = this._soundInstanceClass;
c.context = this.context;
+ c._scratchBuffer = s._scratchBuffer;
c.destinationNode = this.gainNode;
c._panningModel = this._panningModel;
@@ -6742,192 +7419,158 @@ this.createjs = this.createjs || {};
this.createjs = this.createjs || {};
-//TODO verify that tags no longer need to be precreated (mac and pc)
-//TODO modify this now that tags do not need to be precreated
(function () {
"use strict";
/**
- * The TagPool is an object pool for HTMLAudio tag instances. In Chrome, we have to pre-create the number of HTML
- * audio tag instances that we are going to play before we load the data, otherwise the audio stalls.
- * (Note: This seems to be a bug in Chrome)
+ * HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
* @class HTMLAudioTagPool
* @param {String} src The source of the channel.
* @protected
*/
- function TagPool(src) {
-
-
-//Public Properties
- /**
- * The source of the tag pool.
- * #property src
- * @type {String}
- * @protected
- */
- this.src = src;
-
- /**
- * The total number of HTMLAudio tags in this pool. This is the maximum number of instance of a certain sound
- * that can play at one time.
- * #property length
- * @type {Number}
- * @default 0
- * @protected
- */
- this.length = 0;
-
- /**
- * The number of unused HTMLAudio tags.
- * #property available
- * @type {Number}
- * @default 0
- * @protected
- */
- this.available = 0;
-
- /**
- * A list of all available tags in the pool.
- * #property tags
- * @type {Array}
- * @protected
- */
- this.tags = [];
-
- /**
- * The duration property of all audio tags, converted to milliseconds, which originally is only available on the
- * last tag in the tags array because that is the one that is loaded.
- * #property
- * @type {Number}
- * @protected
- */
- this.duration = 0;
- };
-
- var p = TagPool.prototype;
- p.constructor = TagPool;
- var s = TagPool;
+ function HTMLAudioTagPool() {
+ throw "HTMLAudioTagPool cannot be instantiated";
+ }
+ var s = HTMLAudioTagPool;
// Static Properties
/**
- * A hash lookup of each sound channel, indexed by the audio source.
- * #property tags
+ * A hash lookup of each base audio tag, indexed by the audio source.
+ * @property _tags
+ * @type {{}}
* @static
- * @protected
+ * @private
*/
- s.tags = {};
+ s._tags = {};
+ /**
+ * An object pool for html audio tags
+ * @property _tagPool
+ * @type {TagPool}
+ * @static
+ * @private
+ */
+ s._tagPool = new TagPool();
-// Static Methods
/**
- * Get a tag pool. If the pool doesn't exist, create it.
- * #method get
- * @param {String} src The source file used by the audio tag.
+ * A hash lookup of if a base audio tag is available, indexed by the audio source
+ * @property _tagsUsed
+ * @type {{}}
+ * @private
* @static
- * @protected
*/
- s.get = function (src) {
- var channel = s.tags[src];
- if (channel == null) {
- channel = s.tags[src] = new TagPool(src);
- }
- return channel;
- };
+ s._tagUsed = {};
+// Static Methods
/**
- * Delete a TagPool and all related tags. Note that if the TagPool does not exist, this will fail.
- * #method remove
+ * 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 channel = s.tags[src];
- if (channel == null) {return false;}
- channel.removeAll();
- delete(s.tags[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;
};
- /**
- * Get a tag instance. This is a shortcut method.
- * #method getInstance
- * @param {String} src The source file used by the audio tag.
- * @static
- * @protected
- */
- s.getInstance = function (src) {
- var channel = s.tags[src];
- if (channel == null) {return null;}
- return channel.get();
- };
-
- /**
- * Return a tag instance. This is a shortcut method.
- * #method setInstance
- * @param {String} src The source file used by the audio tag.
- * @param {HTMLElement} tag Audio tag to set.
- * @static
- * @protected
- */
- s.setInstance = function (src, tag) {
- var channel = s.tags[src];
- if (channel == null) {return null;}
- return channel.set(tag);
- };
-
/**
* Gets the duration of the src audio in milliseconds
- * #method getDuration
+ * @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 channel = s.tags[src];
- if (channel == null) {return 0;}
- return channel.getDuration();
+ var t = s._tags[src];
+ if (t == null || !t.duration) {return 0;} // OJR duration is NaN if loading has not completed
+ return t.duration * 1000;
};
+ createjs.HTMLAudioTagPool = HTMLAudioTagPool;
-// Public Methods
- /**
- * Add an HTMLAudio tag into the pool.
- * #method add
- * @param {HTMLAudioElement} tag A tag to be used for playback.
- */
- p.add = function (tag) {
- this.tags.push(tag);
- this.length++;
- this.available++;
- };
+// ************************************************************************************************************
/**
- * Remove all tags from the channel. Usually in response to a delete call.
- * #method removeAll
+ * The TagPool is an object pool for HTMLAudio tag instances.
+ * #class TagPool
+ * @param {String} src The source of the channel.
+ * @protected
*/
- p.removeAll = function () {
- var tag;
- while(this.length--) {
- tag = this.tags[this.length];
- if(tag.parentNode) {
- tag.parentNode.removeChild(tag);
- }
- delete(this.tags[this.length]); // NOTE that the audio playback is already stopped by this point
- }
- this.src = null;
- this.tags.length = 0;
+ 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 () {
- if (this.tags.length == 0) {return null;}
- this.available = this.tags.length;
- var tag = this.tags.pop();
+ 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;
};
@@ -6938,27 +7581,35 @@ this.createjs = this.createjs || {};
* @param {HTMLAudioElement} tag HTML audio tag
*/
p.set = function (tag) {
- var index = createjs.indexOf(this.tags, tag);
- if (index == -1) {this.tags.push(tag);}
- this.available = this.tags.length;
+ // OJR this first step seems unnecessary
+ var index = createjs.indexOf(this._tags, tag);
+ if (index == -1) {
+ this._tags.src = null;
+ this._tags.push(tag);
+ }
};
- /**
- * Gets the duration for the src audio and on first call stores it to this.duration
- * #method getDuration
- * @return {Number} Duration of the src in milliseconds
- */
- p.getDuration = function () {
- // this will work because this will be only be run the first time a sound instance is created and before any tags are taken from the pool
- if (!this.duration) {this.duration = this.tags[this.tags.length - 1].duration * 1000;}
- return this.duration;
+ p.toString = function () {
+ return "[TagPool]";
};
- p.toString = function () {
- return "[HTMLAudioTagPool]";
+
+// 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;
};
- createjs.HTMLAudioTagPool = TagPool;
}());
//##############################################################################
@@ -6993,7 +7644,7 @@ this.createjs = this.createjs || {};
// 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._stalledHandler = createjs.proxy(this._playFailed, this);
this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
@@ -7005,7 +7656,6 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);
-
// Public Methods
/**
* Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
@@ -7059,14 +7709,14 @@ this.createjs = this.createjs || {};
tag.currentTime = this._startTime;
} catch (e) {
} // Reset Position
- createjs.HTMLAudioTagPool.setInstance(this.src, tag);
+ createjs.HTMLAudioTagPool.set(this.src, tag);
this._playbackResource = null;
}
};
- p._beginPlaying = function (offset, loop, volume, pan) {
- this._playbackResource = createjs.HTMLAudioTagPool.getInstance(this.src);
- return this.AbstractSoundInstance__beginPlaying(offset, loop, volume, pan);
+ p._beginPlaying = function (playProps) {
+ this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
+ return this.AbstractSoundInstance__beginPlaying(playProps);
};
p._handleSoundReady = function (event) {
@@ -7180,8 +7830,17 @@ this.createjs = this.createjs || {};
}
};
+ 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 = (startTime + duration) * 0.001;
+ 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);
@@ -7189,11 +7848,10 @@ this.createjs = this.createjs || {};
}
};
- /* This should never change
p._setDurationFromSource = function () {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
+ this._playbackResource = null;
};
- */
createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());
@@ -7216,9 +7874,8 @@ this.createjs = this.createjs || {};
*
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. This will be seen as soon as you register sounds, as
- * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as
- * a guide to how many total audio tags you can safely use in all browsers.
+ * 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
@@ -7227,7 +7884,7 @@ this.createjs = this.createjs || {};
*
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 and play at once, which appears to be determined by
+ *
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 requires Quicktime to be installed for audio playback.
*
* iOS 6 limitations
- *
Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)
- *
HTML Audio is disabled by default because
- *
can only have one <audio> tag
+ *
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
*
*
@@ -7265,19 +7921,6 @@ this.createjs = this.createjs || {};
// Public Properties
- /**
- * The default number of instances to allow. Used by {{#crossLink "Sound"}}{{/crossLink}} when a source
- * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method. This is only used if
- * a value is not provided.
- *
- * NOTE this property only exists as a limitation of HTML audio.
- * @property defaultNumChannels
- * @type {Number}
- * @default 2
- * @since 0.4.0
- */
- this.defaultNumChannels = 2;
-
this._capabilities = s._capabilities;
this._loaderClass = createjs.SoundLoader;
@@ -7287,10 +7930,9 @@ this.createjs = this.createjs || {};
var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
var s = HTMLAudioPlugin;
-
// Static Properties
/**
- * The maximum number of instances that can be loaded and played. This is a browser limitation, primarily limited to IE9.
+ * 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
@@ -7306,7 +7948,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default canplaythrough
* @static
- * @protected
+ * @private
*/
s._AUDIO_READY = "canplaythrough";
@@ -7316,7 +7958,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default ended
* @static
- * @protected
+ * @private
*/
s._AUDIO_ENDED = "ended";
@@ -7326,7 +7968,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default seeked
* @static
- * @protected
+ * @private
*/
s._AUDIO_SEEKED = "seeked";
@@ -7336,7 +7978,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default stalled
* @static
- * @protected
+ * @private
*/
s._AUDIO_STALLED = "stalled";
@@ -7347,35 +7989,21 @@ this.createjs = this.createjs || {};
* @type {String}
* @default timeupdate
* @static
- * @protected
+ * @private
*/
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
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
- /**
- * Deprecated now that we have audio sprite support. Audio sprites are strongly recommend on iOS for the following reasons:
- *
it 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
- *
- * @property enableIOS
- * @type {Boolean}
- * @default false
- * @deprecated
- */
- s.enableIOS = false;
-
// Static Methods
/**
@@ -7387,16 +8015,15 @@ this.createjs = this.createjs || {};
*/
s.isSupported = function () {
s._generateCapabilities();
- if (s._capabilities == null) {return false;}
- return true;
+ return (s._capabilities != null);
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -7404,7 +8031,7 @@ this.createjs = this.createjs || {};
if (t.canPlayType == null) {return null;}
s._capabilities = {
- panning:true,
+ panning:false,
volume:true,
tracks:-1
};
@@ -7421,15 +8048,9 @@ this.createjs = this.createjs || {};
// public methods
- p.register = function (loadItem, instances) {
- var channel = createjs.HTMLAudioTagPool.get(loadItem.src);
- var tag = null;
- for (var i = 0; i < instances; i++) {
- tag = this._createTag(loadItem.src);
- channel.add(tag);
- }
-
- var loader = this.AbstractPlugin_register(loadItem, instances);
+ p.register = function (loadItem) {
+ var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
+ var loader = this.AbstractPlugin_register(loadItem);
loader.setTag(tag);
return loader;
@@ -7442,7 +8063,7 @@ this.createjs = this.createjs || {};
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
- si.setPlaybackResource(null);
+ si.playbackResource = null;
return si;
};
@@ -7454,23 +8075,5 @@ this.createjs = this.createjs || {};
p.setVolume = p.getVolume = p.setMute = null;
-// 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
- */
- // TODO move this to tagpool when it changes to be a standard object pool
- 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.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
}());
\ No newline at end of file
diff --git a/lib/soundjs.min.js b/lib/soundjs.min.js
new file mode 100644
index 00000000..500295ab
--- /dev/null
+++ b/lib/soundjs.min.js
@@ -0,0 +1,19 @@
+/*!
+* @license SoundJS
+* Visit http://createjs.com/ for documentation, updates and examples.
+*
+* Copyright (c) 2011-2015 gskinner.com, inc.
+*
+* Distributed under the terms of the MIT license.
+* http://www.opensource.org/licenses/mit-license.html
+*
+* This notice shall be included in all copies or substantial portions of the Software.
+*/
+
+/**!
+ * SoundJS FlashAudioPlugin also includes swfobject (http://code.google.com/p/swfobject/)
+ */
+
+this.createjs=this.createjs||{},function(){var a=createjs.SoundJS=createjs.SoundJS||{};a.version="1.0.0",a.buildDate="Thu, 12 Oct 2017 16:34:05 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.deprecate=function(a,b){"use strict";return function(){var c="Deprecated property or method '"+b+"'. See docs for info.";return console&&(console.warn?console.warn(c):console.log(c)),a&&a.apply(this,arguments)}},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;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,b,c){if("string"==typeof a){var d=this._listeners;if(!(b||d&&d[a]))return!0;a=new createjs.Event(a,b,c)}else a.target&&a.clone&&(a=a.clone());try{a.target=this}catch(e){}if(a.bubbles&&this.parent){for(var f=this,g=[f];f.parent;)g.push(f=f.parent);var h,i=g.length;for(h=i-1;h>=0&&!a.propagationStopped;h--)g[h]._dispatchEvent(a,1+(0==h));for(h=1;i>h&&!a.propagationStopped;h++)g[h]._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,e=2>=b?this._captureListeners:this._listeners;if(a&&e&&(d=e[a.type])&&(c=d.length)){try{a.currentTarget=this}catch(f){}try{a.eventPhase=0|b}catch(f){}a.removed=!1,d=d.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=d[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}2===b&&this._dispatchEvent(a,2.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.Methods.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}(),this.createjs=this.createjs||{},function(){var a={};a.POST="POST",a.GET="GET",createjs.Methods=a}(),this.createjs=this.createjs||{},function(){var a={};a.BINARY="binary",a.CSS="css",a.FONT="font",a.FONTCSS="fontcss",a.IMAGE="image",a.JAVASCRIPT="javascript",a.JSON="json",a.JSONP="jsonp",a.MANIFEST="manifest",a.SOUND="sound",a.VIDEO="video",a.SPRITESHEET="spritesheet",a.SVG="svg",a.TEXT="text",a.XML="xml",createjs.Types=a}(),function(){var a={};a.a=function(){return a.el("a")},a.svg=function(){return a.el("svg")},a.object=function(){return a.el("object")},a.image=function(){return a.el("image")},a.img=function(){return a.el("img")},a.style=function(){return a.el("style")},a.link=function(){return a.el("link")},a.script=function(){return a.el("script")},a.audio=function(){return a.el("audio")},a.video=function(){return a.el("video")},a.text=function(a){return document.createTextNode(a)},a.el=function(a){return document.createElement(a)},createjs.Elements=a}(),function(){var a={container:null};a.appendToHead=function(b){a.getHead().appendChild(b)},a.appendToBody=function(b){if(null==a.container){a.container=document.createElement("div"),a.container.id="preloadjs-container";var c=a.container.style;c.visibility="hidden",c.position="absolute",c.width=a.container.style.height="10px",c.overflow="hidden",c.transform=c.msTransform=c.webkitTransform=c.oTransform="translate(-10px, -10px)",a.getBody().appendChild(a.container)}a.container.appendChild(b)},a.getHead=function(){return document.head||document.getElementsByTagName("head")[0]},a.getBody=function(){return document.body||document.getElementsByTagName("body")[0]},a.removeChild=function(a){a.parent&&a.parent.removeChild(a)},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},createjs.DomUtils=a}(),function(){var a={};a.isBinary=function(a){switch(a){case createjs.Types.IMAGE:case createjs.Types.BINARY:return!0;default:return!1}},a.isText=function(a){switch(a){case createjs.Types.TEXT:case createjs.Types.JSON:case createjs.Types.MANIFEST:case createjs.Types.XML:case createjs.Types.CSS:case createjs.Types.SVG:case createjs.Types.JAVASCRIPT:case createjs.Types.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.Types.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.Types.IMAGE;case"ogg":case"mp3":case"webm":return createjs.Types.SOUND;case"mp4":case"webm":case"ts":return createjs.Types.VIDEO;case"json":return createjs.Types.JSON;case"xml":return createjs.Types.XML;case"css":return createjs.Types.CSS;case"js":return createjs.Types.JAVASCRIPT;case"svg":return createjs.Types.SVG;default:return createjs.Types.TEXT}},createjs.RequestUtils=a}(),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,protocol:null,hostname:null,port:null,pathname:null,search:null,hash:null,host:null};if(null==b)return c;var d=createjs.Elements.a();d.href=b;for(var e in c)e in d&&(c[e]=d[e]);var f=b.indexOf("?");f>-1&&(b=b.substr(0,f));var g;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(g=b.match(a.EXTENSION_PATT))&&(c.extension=g[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildURI=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=createjs.Elements.a();b.href=a.src;var c=createjs.Elements.a();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=createjs.Elements.a();return b.href=a.src,""==b.hostname&&"file:"==b.protocol},createjs.URLUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;try{Object.defineProperties(b,{POST:{get:createjs.deprecate(function(){return createjs.Methods.POST},"AbstractLoader.POST")},GET:{get:createjs.deprecate(function(){return createjs.Methods.GET},"AbstractLoader.GET")},BINARY:{get:createjs.deprecate(function(){return createjs.Types.BINARY},"AbstractLoader.BINARY")},CSS:{get:createjs.deprecate(function(){return createjs.Types.CSS},"AbstractLoader.CSS")},FONT:{get:createjs.deprecate(function(){return createjs.Types.FONT},"AbstractLoader.FONT")},FONTCSS:{get:createjs.deprecate(function(){return createjs.Types.FONTCSS},"AbstractLoader.FONTCSS")},IMAGE:{get:createjs.deprecate(function(){return createjs.Types.IMAGE},"AbstractLoader.IMAGE")},JAVASCRIPT:{get:createjs.deprecate(function(){return createjs.Types.JAVASCRIPT},"AbstractLoader.JAVASCRIPT")},JSON:{get:createjs.deprecate(function(){return createjs.Types.JSON},"AbstractLoader.JSON")},JSONP:{get:createjs.deprecate(function(){return createjs.Types.JSONP},"AbstractLoader.JSONP")},MANIFEST:{get:createjs.deprecate(function(){return createjs.Types.MANIFEST},"AbstractLoader.MANIFEST")},SOUND:{get:createjs.deprecate(function(){return createjs.Types.SOUND},"AbstractLoader.SOUND")},VIDEO:{get:createjs.deprecate(function(){return createjs.Types.VIDEO},"AbstractLoader.VIDEO")},SPRITESHEET:{get:createjs.deprecate(function(){return createjs.Types.SPRITESHEET},"AbstractLoader.SPRITESHEET")},SVG:{get:createjs.deprecate(function(){return createjs.Types.SVG},"AbstractLoader.SVG")},TEXT:{get:createjs.deprecate(function(){return createjs.Types.TEXT},"AbstractLoader.TEXT")},XML:{get:createjs.deprecate(function(){return createjs.Types.XML},"AbstractLoader.XML")}})}catch(c){}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);b instanceof Function?b.call(this,createjs.proxy(this._resultFormatSuccess,this),createjs.proxy(this._resultFormatFailed,this)):(this._result=b||this._rawResult,this._sendComplete());break;case"progress":this._sendProgress(a);break;case"error":this._sendError(a);break;case"loadstart":this._sendLoadStart();break;case"abort":case"timeout":this._isCanceled()||this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_"+a.type.toUpperCase()+"_ERROR"))}},a._resultFormatSuccess=function(a){this._result=a,this._sendComplete()},a._resultFormatFailed=function(a){this._sendError(a)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",this.on("initialize",this._updateXHR,this)}var a=createjs.extend(AbstractMediaLoader,createjs.AbstractLoader);a.load=function(){this._tag||(this._tag=this._createTag(this._item.src)),this._tag.preload="auto",this._tag.load(),this.AbstractLoader_load()},a._createTag=function(){},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.MediaTagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._updateXHR=function(a){a.loader.setResponseType&&a.loader.setResponseType("blob")},a._formatResult=function(a){if(this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.onstalled=null,this._preferXHR){var b=window.URL||window.webkitURL,c=a.getResult(!0);a.getTag().src=b.createObjectURL(c)}return a.getTag()},createjs.AbstractMediaLoader=createjs.promote(AbstractMediaLoader,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractRequest=function(a){this._item=a},a=createjs.extend(AbstractRequest,createjs.EventDispatcher);a.load=function(){},a.destroy=function(){},a.cancel=function(){},createjs.AbstractRequest=createjs.promote(AbstractRequest,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function TagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this),this._addedToDOM=!1}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(createjs.DomUtils.appendToBody(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();null!=this._request.addEventListener?(this._request.addEventListener("loadstart",this._handleLoadStartProxy,!1),this._request.addEventListener("progress",this._handleProgressProxy,!1),this._request.addEventListener("abort",this._handleAbortProxy,!1),this._request.addEventListener("error",this._handleErrorProxy,!1),this._request.addEventListener("timeout",this._handleTimeoutProxy,!1),this._request.addEventListener("load",this._handleLoadProxy,!1),this._request.addEventListener("readystatechange",this._handleReadyStateChangeProxy,!1)):(this._request.onloadstart=this._handleLoadStartProxy,this._request.onprogress=this._handleProgressProxy,this._request.onabort=this._handleAbortProxy,this._request.onerror=this._handleErrorProxy,this._request.ontimeout=this._handleTimeoutProxy,this._request.onload=this._handleLoadProxy,this._request.onreadystatechange=this._handleReadyStateChangeProxy),1==this._xhrLevel&&(this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout));try{this._item.values?this._request.send(createjs.URLUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){"blob"===a&&(a=window.URL?"blob":"arraybuffer",this._responseType=a),this._request.responseType=a},a.getAllResponseHeaders=function(){return this._request.getAllResponseHeaders instanceof Function?this._request.getAllResponseHeaders():null},a.getResponseHeader=function(a){return this._request.getResponseHeader instanceof Function?this._request.getResponseHeader(a):null},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._handleLoadStart=function(){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);if(this._response=this._getResponse(),"arraybuffer"===this._responseType)try{this._response=new Blob([this._response])}catch(b){if(window.BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,"TypeError"===b.name&&window.BlobBuilder){var c=new BlobBuilder;c.append(this._response),this._response=c.getBlob()}}this._clean(),this.dispatchEvent(new createjs.Event("complete"))}},a._handleTimeout=function(a){this._clean(),this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT",null,a))},a._checkError=function(){var a=parseInt(this._request.status);return a>=400&&599>=a?new Error(a):0==a&&/^https?:/.test(location.protocol)?new Error(0):null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.URLUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){var g=s.ACTIVEX_VERSIONS[e];try{d=new ActiveXObject(g);break}catch(h){}}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 i=null;if(i=a.method==createjs.Methods.GET?createjs.URLUtils.buildURI(a.src,a.values):a.src,d.open(a.method||createjs.Methods.GET,i,!0),b&&d instanceof XMLHttpRequest&&1==this._xhrLevel&&(c.Origin=location.origin),a.values&&a.method==createjs.Methods.POST&&(c["Content-Type"]="application/x-www-form-urlencoded"),b||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest"),a.headers)for(var j in a.headers)c[j]=a.headers[j];for(j in c)d.setRequestHeader(j,c[j]);return d instanceof XMLHttpRequest&&void 0!==a.withCredentials&&(d.withCredentials=a.withCredentials),this._request=d,!0},a._clean=function(){clearTimeout(this._loadTimeout),null!=this._request.removeEventListener?(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)):(this._request.onloadstart=null,this._request.onprogress=null,this._request.onabort=null,this._request.onerror=null,this._request.ontimeout=null,this._request.onload=null,this._request.onreadystatechange=null)},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.Types.SOUND),createjs.DomUtils.isAudioTag(a)?this._tag=a:createjs.DomUtils.isAudioTag(a.src)?this._tag=a:createjs.DomUtils.isAudioTag(a.tag)&&(this._tag=createjs.DomUtils.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.Types.SOUND},a._createTag=function(a){var b=createjs.Elements.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("string"==typeof a)return console&&(console.warn||console.log)("Deprecated behaviour. Sound.play takes a configuration object instead of individual arguments. See docs for info."),(new createjs.PlayPropsConfig).set({interrupt:a});if(null==a||a instanceof b||a instanceof Object)return(new createjs.PlayPropsConfig).set(a);if(null==a)throw new Error("PlayProps configuration not recognized.")},a.set=function(a){if(null!=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,b._getMasterVolume=function(){return this._masterVolume},b.getVolume=createjs.deprecate(b._getMasterVolume,"Sound.getVolume"),b._setMasterVolume=function(a){if(null!=Number(a)&&(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.setVolume=createjs.deprecate(b._setMasterVolume,"Sound.setVolume"),b._masterMute=!1,b._getMute=function(){return this._masterMute},b.getMute=createjs.deprecate(b._getMute,"Sound.getMute"),b._setMute=function(a){if(null!=a&&(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)},b.setMute=createjs.deprecate(b._setMute,"Sound.setMute"),b._getCapabilities=function(){return null==b.activePlugin?null:b.activePlugin._capabilities},b.getCapabilities=createjs.deprecate(b._getCapabilities,"Sound.getCapabilities"),Object.defineProperties(b,{volume:{get:b._getMasterVolume,set:b._setMasterVolume},muted:{get:b._getMute,set:b._setMute},capabilities:{get:b._getCapabilities}}),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.initLoad=function(a){return"video"==a.type?!0: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+g.src);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",this._handleLoadComplete,this),h.on("error",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){var d=createjs.PlayPropsConfig.create(c),e=b.createInstance(a,d.startTime,d.duration),f=b._playInstance(e,d);return f||e._playFailed(),e},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),f=f||b._defaultPlayPropsHash[g.src],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.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.position),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){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.positionc.position)&&(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.getVolume=createjs.deprecate(this._getVolume,"AbstractSoundInstance.getVolume"),this.setVolume=createjs.deprecate(this._setVolume,"AbstractSoundInstance.setVolume"),this._pan=0,Object.defineProperty(this,"pan",{get:this._getPan,set:this._setPan}),this.getPan=createjs.deprecate(this._getPan,"AbstractSoundInstance.getPan"),this.setPan=createjs.deprecate(this._setPan,"AbstractSoundInstance.setPan"),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this._getStartTime,set:this._setStartTime}),this.getStartTime=createjs.deprecate(this._getStartTime,"AbstractSoundInstance.getStartTime"),this.setStartTime=createjs.deprecate(this._setStartTime,"AbstractSoundInstance.setStartTime"),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this._getDuration,set:this._setDuration}),this.getDuration=createjs.deprecate(this._getDuration,"AbstractSoundInstance.getDuration"),this.setDuration=createjs.deprecate(this._setDuration,"AbstractSoundInstance.setDuration"),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this._getPlaybackResource,set:this._setPlaybackResource}),d!==!1&&d!==!0&&this._setPlaybackResource(d),this.getPlaybackResource=createjs.deprecate(this._getPlaybackResource,"AbstractSoundInstance.getPlaybackResource"),this.setPlaybackResource=createjs.deprecate(this._setPlaybackResource,"AbstractSoundInstance.setPlaybackResource"),this._position=0,Object.defineProperty(this,"position",{get:this._getPosition,set:this._setPosition}),this.getPosition=createjs.deprecate(this._getPosition,"AbstractSoundInstance.getPosition"),this.setPosition=createjs.deprecate(this._setPosition,"AbstractSoundInstance.setPosition"),this._loop=0,Object.defineProperty(this,"loop",{get:this._getLoop,set:this._setLoop}),this.getLoop=createjs.deprecate(this._getLoop,"AbstractSoundInstance.getLoop"),this.setLoop=createjs.deprecate(this._setLoop,"AbstractSoundInstance.setLoop"),this._muted=!1,Object.defineProperty(this,"muted",{get:this._getMuted,set:this._setMuted}),this.getMuted=createjs.deprecate(this._getMuted,"AbstractSoundInstance.getMuted"),this.setMuted=createjs.deprecate(this._setMuted,"AbstractSoundInstance.setMuted"),this._paused=!1,Object.defineProperty(this,"paused",{get:this._getPaused,set:this._setPaused}),this.getPaused=createjs.deprecate(this._getPaused,"AbstractSoundInstance.getPaused"),this.setPaused=createjs.deprecate(this._setPaused,"AbstractSoundInstance.setPaused")},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a){var b=createjs.PlayPropsConfig.create(a);return this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(b),void(this._paused&&this._setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,b),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this._setPosition(a.offset),null!=a.loop&&this._setLoop(a.loop),null!=a.volume&&this._setVolume(a.volume),null!=a.pan&&this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),this},a.toString=function(){return"[AbstractSoundInstance]"},a._getPaused=function(){return this._paused},a._setPaused=function(a){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._playbackResource&&this._setDurationFromSource(),this},a._getPlaybackResource=function(){return this._playbackResource},a._getLoop=function(){return this._loop},a._setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this._setPosition(a.offset),this._setLoop(a.loop),this._setVolume(a.volume),this._setPan(a.pan),null!=a.startTime&&(this._setStartTime(a.startTime),this._setDuration(a.duration)),null!=this._playbackResource&&this._positionc;c++){var e=this._soundInstances[b][c];e.playbackResource=this._audioSources[b],this._soundInstances[b]=null}},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.Types.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._updatePan(),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._scratchBuffer=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){if(a){if(a.stop(0),a.disconnect(0),createjs.BrowserDetect.isIOS)try{a.buffer=b._scratchBuffer}catch(c){}a=null}return a},a._handleSoundReady=function(){this.gainNode.connect(b.destinationNode);var a=.001*this._duration,c=Math.min(.001*Math.max(0,this._position),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._scratchBuffer=null,b._unlocked=!1,b.DEFAULT_SAMPLE_RATE=44100,b.isSupported=function(){var a=createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry;return"file:"!=location.protocol||a||this._isFileXHRSupported()?(b._generateCapabilities(),null==b.context?!1:!0):!1},b.playEmptySound=function(){if(null!=b.context){var a=b.context.createBufferSource();a.buffer=b._scratchBuffer,a.connect(b.context.destination),a.start(0,0,0)}},b._isFileXHRSupported=function(){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&&(b.context=b._createAudioContext(),null==b.context))return null;null==b._scratchBuffer&&(b._scratchBuffer=b.context.createBuffer(1,1,22050)),b._compatibilitySetUp(),"ontouchstart"in window&&"running"!=b.context.state&&(b._unlock(),document.addEventListener("mousedown",b._unlock,!0),document.addEventListener("touchstart",b._unlock,!0),document.addEventListener("touchend",b._unlock,!0)),b._capabilities={panning:!0,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;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._createAudioContext=function(){var a=window.AudioContext||window.webkitAudioContext;if(null==a)return null;var c=new a;if(/(iPhone|iPad)/i.test(navigator.userAgent)&&c.sampleRate!==b.DEFAULT_SAMPLE_RATE){var d=c.createBuffer(1,1,b.DEFAULT_SAMPLE_RATE),e=c.createBufferSource();e.buffer=d,e.connect(c.destination),e.start(0),e.disconnect(),c.close(),c=new a}return c},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}},b._unlock=function(){b._unlocked||(b.playEmptySound(),"running"==b.context.state&&(document.removeEventListener("mousedown",b._unlock,!0),document.removeEventListener("touchend",b._unlock,!0),document.removeEventListener("touchstart",b._unlock,!0),b._unlocked=!0))},a.toString=function(){return"[WebAudioPlugin]"},a._addPropsToClasses=function(){var a=this._soundInstanceClass;a.context=this.context,a._scratchBuffer=b._scratchBuffer,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&&c.duration?1e3*c.duration:0},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))},a._setDurationFromSource=function(){this._duration=createjs.HTMLAudioTagPool.getDuration(this.src),this._playbackResource=null},createjs.HTMLAudioSoundInstance=createjs.promote(HTMLAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function HTMLAudioPlugin(){this.AbstractPlugin_constructor(),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.playbackResource=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
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..0ae873a7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "soundjs",
+ "npmName": "soundjs",
+ "version": "1.0.2",
+ "description": "A JavaScript library that provides a simple API, and powerful features to make working with audio a breeze. Easily ties in audio file loading to PreloadJS.",
+ "main": "lib/soundjs.js",
+ "jsdelivr": "lib/soundjs.min.js",
+ "directories": {
+ "doc": "docs",
+ "example": "examples",
+ "lib": "lib",
+ "test": "tests"
+ },
+ "npmFileMap": [
+ {
+ "basePath": "lib",
+ "files": [
+ "**/*"
+ ]
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/CreateJS/SoundJS.git"
+ },
+ "keywords": [
+ "createjs",
+ "soundjs",
+ "gskinner",
+ "javascript",
+ "audio",
+ "webaudio",
+ "html5",
+ "flash"
+ ],
+ "author": "gskinner.com, inc.",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/CreateJS/SoundJS/issues"
+ },
+ "homepage": "https://createjs.com/soundjs/"
+}
diff --git a/src/createjs/events/Event.js b/src/createjs/events/Event.js
index 70ac0e64..f010e269 100644
--- a/src/createjs/events/Event.js
+++ b/src/createjs/events/Event.js
@@ -28,7 +28,7 @@
/**
* A collection of Classes that are shared across all the CreateJS libraries. The classes are included in the minified
- * files of each library and are available on the createsjs namespace directly.
+ * files of each library and are available on the createjs namespace directly.
*
*
Example
*
@@ -53,8 +53,8 @@ this.createjs = this.createjs||{};
* 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.
+ * @param {Boolean} [bubbles=false] Indicates whether the event will bubble through the display list.
+ * @param {Boolean} [cancelable=false] Indicates whether the default behaviour of this event can be cancelled.
* @constructor
**/
function Event(type, bubbles, cancelable) {
@@ -171,24 +171,11 @@ this.createjs = this.createjs||{};
}
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.
+ * Sets {{#crossLink "Event/defaultPrevented:property"}}{{/crossLink}} to true if the event is cancelable.
+ * Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will
+ * cancel the default behaviour associated with the event.
* @method preventDefault
**/
p.preventDefault = function() {
@@ -196,7 +183,7 @@ this.createjs = this.createjs||{};
};
/**
- * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
+ * Sets {{#crossLink "Event/propagationStopped:property"}}{{/crossLink}} to true.
* Mirrors the DOM event standard.
* @method stopPropagation
**/
@@ -205,8 +192,8 @@ this.createjs = this.createjs||{};
};
/**
- * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
- * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
+ * Sets {{#crossLink "Event/propagationStopped:property"}}{{/crossLink}} and
+ * {{#crossLink "Event/immediatePropagationStopped:property"}}{{/crossLink}} to true.
* Mirrors the DOM event standard.
* @method stopImmediatePropagation
**/
diff --git a/src/createjs/events/EventDispatcher.js b/src/createjs/events/EventDispatcher.js
index 0ae47819..4526fa66 100644
--- a/src/createjs/events/EventDispatcher.js
+++ b/src/createjs/events/EventDispatcher.js
@@ -81,7 +81,12 @@ this.createjs = this.createjs||{};
* console.log(instance == this); // true, "on" uses dispatcher scope by default.
* });
*
- * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage scope.
+ * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
+ * scope.
+ *
+ * Browser support
+ * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
+ * requires modern browsers (IE9+).
*
*
* @class EventDispatcher
@@ -107,20 +112,6 @@ this.createjs = this.createjs||{};
}
var p = EventDispatcher.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 public methods:
/**
* Static initializer to mix EventDispatcher methods into a target object or prototype.
@@ -184,7 +175,11 @@ this.createjs = this.createjs||{};
* only run once, associate arbitrary data with the listener, and remove the listener.
*
* This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
- * The created anonymous function is returned for use with .removeEventListener (or .off).
+ * The wrapper function is returned for use with `removeEventListener` (or `off`).
+ *
+ * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
+ * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
+ * to `on` with the same params will create multiple listeners.
*
*
Example
*
@@ -254,6 +249,9 @@ this.createjs = this.createjs||{};
/**
* A shortcut to the removeEventListener method, with the same parameters and return value. This is a companion to the
* .on method.
+ *
+ * IMPORTANT: To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See
+ * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
*
* @method off
* @param {String} type The string type of the event.
@@ -299,19 +297,24 @@ this.createjs = this.createjs||{};
* @method dispatchEvent
* @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
* While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
- * dispatchEvent will construct an Event instance with the specified type.
- * @return {Boolean} Returns the value of eventObj.defaultPrevented.
+ * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
+ * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
+ * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
+ * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
+ * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
**/
- p.dispatchEvent = function(eventObj) {
+ p.dispatchEvent = function(eventObj, bubbles, cancelable) {
if (typeof eventObj == "string") {
- // won't bubble, so skip everything if there's no listeners:
+ // skip everything if there's no listeners and it doesn't bubble:
var listeners = this._listeners;
- if (!listeners || !listeners[eventObj]) { return false; }
- eventObj = new createjs.Event(eventObj);
+ if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
+ eventObj = new createjs.Event(eventObj, bubbles, cancelable);
} else if (eventObj.target && eventObj.clone) {
// redispatching an active event object, so clone it:
eventObj = eventObj.clone();
}
+
+ // TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
if (!eventObj.bubbles || !this.parent) {
@@ -330,7 +333,7 @@ this.createjs = this.createjs||{};
list[i]._dispatchEvent(eventObj, 3);
}
}
- return eventObj.defaultPrevented;
+ return !eventObj.defaultPrevented;
};
/**
@@ -376,17 +379,15 @@ this.createjs = this.createjs||{};
// private methods:
/**
* @method _dispatchEvent
- * @param {Object | String | Event} eventObj
+ * @param {Object | Event} eventObj
* @param {Object} eventPhase
* @protected
**/
p._dispatchEvent = function(eventObj, eventPhase) {
- var l, listeners = (eventPhase==1) ? this._captureListeners : this._listeners;
- if (eventObj && listeners) {
- var arr = listeners[eventObj.type];
- if (!arr||!(l=arr.length)) { return; }
+ var l, arr, listeners = (eventPhase <= 2) ? this._captureListeners : this._listeners;
+ if (eventObj && listeners && (arr = listeners[eventObj.type]) && (l=arr.length)) {
try { eventObj.currentTarget = this; } catch (e) {}
- try { eventObj.eventPhase = eventPhase; } catch (e) {}
+ try { eventObj.eventPhase = eventPhase|0; } catch (e) {}
eventObj.removed = false;
arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
@@ -400,6 +401,7 @@ this.createjs = this.createjs||{};
}
}
}
+ if (eventPhase === 2) { this._dispatchEvent(eventObj, 2.1); }
};
diff --git a/src/createjs/utils/deprecate.js b/src/createjs/utils/deprecate.js
new file mode 100644
index 00000000..6b16d04e
--- /dev/null
+++ b/src/createjs/utils/deprecate.js
@@ -0,0 +1,70 @@
+/*
+* extend
+* Visit http://createjs.com/ for documentation, updates and examples.
+*
+* Copyright (c) 2010 gskinner.com, inc.
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use,
+* copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following
+* conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/**
+ * @module CreateJS
+ */
+
+// namespace:
+this.createjs = this.createjs||{};
+
+/**
+ * @class Utility Methods
+ */
+
+/**
+ * Wraps deprecated methods so they still be used, but throw warnings to developers.
+ *
+ * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod);
+ *
+ * The recommended approach for deprecated properties is:
+ *
+ * try {
+ * Obj ect.defineProperties(object, {
+ * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) },
+ * readWriteProp: {
+ * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }),
+ * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; })
+ * });
+ * } catch (e) {}
+ *
+ * @method deprecate
+ * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how
+ * @param {String} [name=null] The name of the method or property to display in the console warning.
+ * to deprecate properties.
+ * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after
+ * logging the warning in the console.
+ */
+createjs.deprecate = function(fallbackMethod, name) {
+ "use strict";
+ return function() {
+ var msg = "Deprecated property or method '"+name+"'. See docs for info.";
+ console && (console.warn ? console.warn(msg) : console.log(msg));
+ return fallbackMethod && fallbackMethod.apply(this, arguments);
+ }
+};
\ No newline at end of file
diff --git a/src/createjs/utils/extend.js b/src/createjs/utils/extend.js
index 690de151..d6aa388f 100644
--- a/src/createjs/utils/extend.js
+++ b/src/createjs/utils/extend.js
@@ -44,7 +44,7 @@ this.createjs = this.createjs||{};
*
* function MySubClass() {}
* createjs.extend(MySubClass, MySuperClass);
- * ClassB.prototype.doSomething = function() { }
+ * MySubClass.prototype.doSomething = function() { }
*
* var foo = new MySubClass();
* console.log(foo instanceof MySuperClass); // true
diff --git a/src/preloadjs/data/LoadItem.js b/src/preloadjs/data/LoadItem.js
index 02d78c73..9c4b2289 100644
--- a/src/preloadjs/data/LoadItem.js
+++ b/src/preloadjs/data/LoadItem.js
@@ -49,7 +49,8 @@ this.createjs = this.createjs || {};
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.
+ * 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
@@ -57,9 +58,8 @@ this.createjs = this.createjs || {};
this.src = null;
/**
- * 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. See the {{#crossLink "AbstractLoader"}}{{/crossLink}}
- * class for the full list of supported types.
+ * 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
@@ -104,14 +104,14 @@ this.createjs = this.createjs || {};
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
+ * The request method used for HTTP calls. Both {{#crossLink "Methods/GET:property"}}{{/crossLink}} or
+ * {{#crossLink "Methods/POST:property"}}{{/crossLink}} request types are supported, and are defined as
* constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @property method
* @type {String}
- * @default get
+ * @default GET
*/
- this.method = createjs.LoadItem.GET;
+ this.method = createjs.Methods.GET;
/**
* An object hash of name/value pairs to send to the server.
@@ -154,7 +154,7 @@ this.createjs = this.createjs || {};
* @type {boolean}
* @default Anonymous
*/
- this.crossOrigin = "Anonymous";
+ this.crossOrigin = null;
/**
* The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
@@ -163,22 +163,31 @@ this.createjs = this.createjs || {};
* @type {Number}
* @default 8000 (8 seconds)
*/
- this.loadTimeout = 8000;
+ this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
};
var p = LoadItem.prototype = {};
var s = LoadItem;
/**
- * Create/validate a 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
- *
Objectss are returned as-is
+ *
Objects are returned with any needed properties added
*
* @method create
* @param {LoadItem|String|Object} value The load item value
- * @returns {Object|LoadItem}
+ * @returns {LoadItem|Object}
* @static
*/
s.create = function (value) {
@@ -188,8 +197,10 @@ this.createjs = this.createjs || {};
return item;
} else if (value instanceof s) {
return value;
- } else if (value instanceof Object) { // Don't modify object, allows users to attach random data to the item.
- // TODO: Disallow objects with no src?
+ } 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.");
diff --git a/src/preloadjs/data/Methods.js b/src/preloadjs/data/Methods.js
new file mode 100644
index 00000000..92fe4da0
--- /dev/null
+++ b/src/preloadjs/data/Methods.js
@@ -0,0 +1,69 @@
+/*
+ * Methods
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module PreloadJS
+ */
+
+// namespace:
+this.createjs = this.createjs || {};
+
+(function() {
+
+ "use strict";
+
+ /**
+ * Defines the method types for XHR requests. Currently, PreloadJS only supports "POST" and "GET", however
+ * any HTML method can be used with PreloadJS utilities.
+ *
+ * @class Methods
+ * @static
+ */
+ var s = {};
+
+ /**
+ * 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";
+
+ createjs.Methods = s;
+}());
diff --git a/src/preloadjs/data/Types.js b/src/preloadjs/data/Types.js
new file mode 100644
index 00000000..93b739ce
--- /dev/null
+++ b/src/preloadjs/data/Types.js
@@ -0,0 +1,229 @@
+/*
+ * Types
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module PreloadJS
+ */
+
+// namespace:
+this.createjs = this.createjs || {};
+
+(function() {
+
+ "use strict";
+
+ /**
+ * Defines the load types that PreloadJS supports by default. This is typically used when passing a type override to
+ * a {{#crossLink "LoadQueue"}}{{/crossLink}}.
+ *
+ *
Example
+ *
+ * queue.loadFile({src:"https://somecdn/wfossn3", type:createjs.Types.IMAGE});
+ *
+ * You can also use the string value:
+ *
+ * queue.loadFile({src:"https://somecdn/wfossn3", type:"image"});
+ *
+ * @class Types
+ * @static
+ */
+
+ var s = {};
+
+ /**
+ * 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 font files.
+ * @property FONT
+ * @type {String}
+ * @default font
+ * @static
+ * @since 0.9.0
+ */
+ s.FONT = "font";
+
+ /**
+ * The preload type for fonts specified with CSS (such as Google fonts)
+ * @property FONTCSS
+ * @type {String}
+ * @default fontcss
+ * @static
+ * @since 0.9.0
+ */
+ s.FONTCSS = "fontcss";
+
+ /**
+ * 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";
+
+ createjs.Types = s;
+}());
diff --git a/src/preloadjs/events/ProgressEvent.js b/src/preloadjs/events/ProgressEvent.js
index 48b831b1..69f61f81 100644
--- a/src/preloadjs/events/ProgressEvent.js
+++ b/src/preloadjs/events/ProgressEvent.js
@@ -39,11 +39,11 @@ this.createjs = this.createjs || {};
// constructor
/**
- * A createjs {{#crossLink "Event"}}{{/crossLink}} that is dispatched when progress changes.
+ * 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] The total amount that will load. This will default to 0, so does not need to be passed in,
- * as long as the loaded value is a progress value (between 0 and 1).
+ * @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
*/
diff --git a/src/preloadjs/loaders/AbstractLoader.js b/src/preloadjs/loaders/AbstractLoader.js
index 365b4565..a49adbc6 100644
--- a/src/preloadjs/loaders/AbstractLoader.js
+++ b/src/preloadjs/loaders/AbstractLoader.js
@@ -42,11 +42,11 @@ this.createjs = this.createjs || {};
* 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} The item to be loaded.
+ * @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.
- * @oaram {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
+ * @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
*/
@@ -70,6 +70,7 @@ this.createjs = this.createjs || {};
* @property canceled
* @type {Boolean}
* @default false
+ * @readonly
*/
this.canceled = false;
@@ -104,7 +105,18 @@ this.createjs = this.createjs || {};
* 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.
+ * asynchronous, such as creating a new image. The callback function is passed 2 parameters, which are callbacks
+ * to handle success and error conditions in the resultFormatter. Note that the resultFormatter method is
+ * called in the current scope, as well as the success and error callbacks.
+ *
+ *
Example asynchronous resultFormatter
+ *
+ * function _formatResult(loader) {
+ * return function(success, error) {
+ * if (errorCondition) { error(errorDetailEvent); }
+ * success(result);
+ * }
+ * }
* @property resultFormatter
* @type {Function}
* @default null
@@ -182,174 +194,29 @@ this.createjs = this.createjs || {};
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
- */
- s.POST = "POST";
-
- /**
- * Defines a GET request, use for a method value when loading data.
- * @property GET
- * @type {string}
- * @default get
- */
- 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";
+ // Remove these @deprecated properties after 1.0
+ try {
+ Object.defineProperties(s, {
+ POST: { get: createjs.deprecate(function() { return createjs.Methods.POST; }, "AbstractLoader.POST") },
+ GET: { get: createjs.deprecate(function() { return createjs.Methods.GET; }, "AbstractLoader.GET") },
+
+ BINARY: { get: createjs.deprecate(function() { return createjs.Types.BINARY; }, "AbstractLoader.BINARY") },
+ CSS: { get: createjs.deprecate(function() { return createjs.Types.CSS; }, "AbstractLoader.CSS") },
+ FONT: { get: createjs.deprecate(function() { return createjs.Types.FONT; }, "AbstractLoader.FONT") },
+ FONTCSS: { get: createjs.deprecate(function() { return createjs.Types.FONTCSS; }, "AbstractLoader.FONTCSS") },
+ IMAGE: { get: createjs.deprecate(function() { return createjs.Types.IMAGE; }, "AbstractLoader.IMAGE") },
+ JAVASCRIPT: { get: createjs.deprecate(function() { return createjs.Types.JAVASCRIPT; }, "AbstractLoader.JAVASCRIPT") },
+ JSON: { get: createjs.deprecate(function() { return createjs.Types.JSON; }, "AbstractLoader.JSON") },
+ JSONP: { get: createjs.deprecate(function() { return createjs.Types.JSONP; }, "AbstractLoader.JSONP") },
+ MANIFEST: { get: createjs.deprecate(function() { return createjs.Types.MANIFEST; }, "AbstractLoader.MANIFEST") },
+ SOUND: { get: createjs.deprecate(function() { return createjs.Types.SOUND; }, "AbstractLoader.SOUND") },
+ VIDEO: { get: createjs.deprecate(function() { return createjs.Types.VIDEO; }, "AbstractLoader.VIDEO") },
+ SPRITESHEET: { get: createjs.deprecate(function() { return createjs.Types.SPRITESHEET; }, "AbstractLoader.SPRITESHEET") },
+ SVG: { get: createjs.deprecate(function() { return createjs.Types.SVG; }, "AbstractLoader.SVG") },
+ TEXT: { get: createjs.deprecate(function() { return createjs.Types.TEXT; }, "AbstractLoader.TEXT") },
+ XML: { get: createjs.deprecate(function() { return createjs.Types.XML; }, "AbstractLoader.XML") }
+ });
+ } catch (e) {}
// Events
/**
@@ -388,7 +255,7 @@ this.createjs = this.createjs || {};
* 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 {String} type The event type ("fileerror")
* @param {LoadItem|object} The item that encountered the error
* @since 0.6.0
*/
@@ -641,10 +508,7 @@ this.createjs = this.createjs || {};
* @protected
*/
p._isCanceled = function () {
- if (window.createjs == null || this.canceled) {
- return true;
- }
- return false;
+ return this.canceled;
};
/**
@@ -656,13 +520,13 @@ this.createjs = this.createjs || {};
* @return {Object} The formatted result
* @since 0.6.0
*/
- p.resultFormatter = null; //TODO: Add support for async formatting.
+ p.resultFormatter = null;
/**
* 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} The event that the internal request dispatches.
+ * @param {Event} event The event that the internal request dispatches.
* @protected
* @since 0.6.0
*/
@@ -671,12 +535,13 @@ this.createjs = this.createjs || {};
case "complete":
this._rawResult = event.target._response;
var result = this.resultFormatter && this.resultFormatter(this);
- var _this = this;
+ // The resultFormatter is asynchronous
if (result instanceof Function) {
- result(function(result) {
- _this._result = result;
- _this._sendComplete();
- });
+ result.call(this,
+ createjs.proxy(this._resultFormatSuccess, this),
+ createjs.proxy(this._resultFormatFailed, this)
+ );
+ // The result formatter is synchronous
} else {
this._result = result || this._rawResult;
this._sendComplete();
@@ -694,20 +559,33 @@ this.createjs = this.createjs || {};
case "abort":
case "timeout":
if (!this._isCanceled()) {
- this.dispatchEvent(event.type);
+ this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_" + event.type.toUpperCase() + "_ERROR"));
}
break;
}
};
/**
- * @method buildPath
- * @protected
- * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
- * instead.
+ * The "success" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
+ * functions.
+ * @method _resultFormatSuccess
+ * @param {Object} result The formatted result
+ * @private
+ */
+ p._resultFormatSuccess = function (result) {
+ this._result = result;
+ this._sendComplete();
+ };
+
+ /**
+ * The "error" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
+ * functions.
+ * @method _resultFormatSuccess
+ * @param {Object} error The error event
+ * @private
*/
- p.buildPath = function (src, data) {
- return createjs.RequestUtils.buildPath(src, data);
+ p._resultFormatFailed = function (event) {
+ this._sendError(event);
};
/**
diff --git a/src/preloadjs/loaders/AbstractMediaLoader.js b/src/preloadjs/loaders/AbstractMediaLoader.js
index c8447613..a3a0298e 100644
--- a/src/preloadjs/loaders/AbstractMediaLoader.js
+++ b/src/preloadjs/loaders/AbstractMediaLoader.js
@@ -45,6 +45,7 @@ this.createjs = this.createjs || {};
* @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) {
@@ -55,6 +56,8 @@ this.createjs = this.createjs || {};
// protected properties
this._tagSrcAttribute = "src";
+
+ this.on("initialize", this._updateXHR, this);
};
var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
@@ -90,6 +93,20 @@ this.createjs = this.createjs || {};
}
};
+ // protected methods
+ /**
+ * Before the item loads, set its mimeType and responseType.
+ * @property _updateXHR
+ * @param {Event} event
+ * @private
+ */
+ p._updateXHR = function (event) {
+ // Only exists for XHR
+ if (event.loader.setResponseType) {
+ event.loader.setResponseType("blob");
+ }
+ };
+
/**
* The result formatter for media files.
* @method _formatResult
@@ -101,7 +118,10 @@ this.createjs = this.createjs || {};
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.onstalled = null;
if (this._preferXHR) {
- loader.getTag().src = loader.getResult(true);
+ var URL = window.URL || window.webkitURL;
+ var result = loader.getResult(true);
+
+ loader.getTag().src = URL.createObjectURL(result);
}
return loader.getTag();
};
diff --git a/src/preloadjs/loaders/SoundLoader.js b/src/preloadjs/loaders/SoundLoader.js
index a84a01b0..ddf98bce 100644
--- a/src/preloadjs/loaders/SoundLoader.js
+++ b/src/preloadjs/loaders/SoundLoader.js
@@ -46,18 +46,19 @@ this.createjs = this.createjs || {};
* @class SoundLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
+ * @extends AbstractMediaLoader
* @constructor
*/
function SoundLoader(loadItem, preferXHR) {
- this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
+ this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.Types.SOUND);
// protected properties
- if (createjs.RequestUtils.isAudioTag(loadItem)) {
+ if (createjs.DomUtils.isAudioTag(loadItem)) {
this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
+ } else if (createjs.DomUtils.isAudioTag(loadItem.src)) {
this._tag = loadItem;
- } else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
- this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
+ } else if (createjs.DomUtils.isAudioTag(loadItem.tag)) {
+ this._tag = createjs.DomUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
}
if (this._tag != null) {
@@ -71,19 +72,19 @@ this.createjs = this.createjs || {};
// 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}}.
+ * {{#crossLink "Types/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;
+ return item.type == createjs.Types.SOUND;
};
// protected methods
p._createTag = function (src) {
- var tag = document.createElement("audio");
+ var tag = createjs.Elements.audio();
tag.autoplay = false;
tag.preload = "none";
diff --git a/src/preloadjs/net/MediaTagRequest.js b/src/preloadjs/net/MediaTagRequest.js
index b33e0751..39c0fe02 100644
--- a/src/preloadjs/net/MediaTagRequest.js
+++ b/src/preloadjs/net/MediaTagRequest.js
@@ -60,8 +60,14 @@ this.createjs = this.createjs || {};
// public methods
p.load = function () {
- this._tag.onstalled = createjs.proxy(this._handleStalled, this);
- this._tag.onprogress = createjs.proxy(this._handleProgress, this);
+ 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.
@@ -104,8 +110,8 @@ this.createjs = this.createjs || {};
// protected methods
p._clean = function () {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
- this._tag.onstalled = null;
- this._tag.onprogress = null;
+ this._tag.removeEventListener("stalled", this._stalledCallback);
+ this._tag.removeEventListener("progress", this._progressCallback);
this.TagRequest__clean();
};
diff --git a/src/preloadjs/net/TagRequest.js b/src/preloadjs/net/TagRequest.js
index a7d4d3d2..dbdbed68 100644
--- a/src/preloadjs/net/TagRequest.js
+++ b/src/preloadjs/net/TagRequest.js
@@ -81,35 +81,30 @@ this.createjs = this.createjs || {};
*/
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 () {
- if (this._tag.parentNode == null) {
- window.document.body.appendChild(this._tag);
- this._addedToDOM = true;
- }
-
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) {
+ createjs.DomUtils.appendToBody(this._tag);
+ this._addedToDOM = true;
+ }
};
p.destroy = function() {
@@ -137,6 +132,16 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * 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
@@ -147,11 +152,21 @@ this.createjs = this.createjs || {};
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
@@ -160,18 +175,11 @@ this.createjs = this.createjs || {};
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);
}
- };
-
- p._hideTag = function() {
- this._startTagVisibility = this._tag.style.visibility;
- this._tag.style.visibility = "hidden";
- };
-
- p._showTag = function() {
- this._tag.style.visibility = this._startTagVisibility;
+ clearTimeout(this._loadTimeout);
};
/**
diff --git a/src/preloadjs/net/XHRRequest.js b/src/preloadjs/net/XHRRequest.js
index 2f946f20..01df75d4 100644
--- a/src/preloadjs/net/XHRRequest.js
+++ b/src/preloadjs/net/XHRRequest.js
@@ -50,7 +50,7 @@ this.createjs = this.createjs || {};
* for an overview of supported file properties.
* @extends AbstractLoader
*/
- function XHRRequest(item) {
+ function XHRRequest (item) {
this.AbstractRequest_constructor(item);
// protected properties
@@ -177,15 +177,28 @@ this.createjs = this.createjs || {};
}
//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);
+ if (this._request.addEventListener != null) {
+ 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);
+ } else {
+ // IE9 support
+ this._request.onloadstart = this._handleLoadStartProxy;
+ this._request.onprogress = this._handleProgressProxy;
+ this._request.onabort = this._handleAbortProxy;
+ this._request.onerror = this._handleErrorProxy;
+ this._request.ontimeout = this._handleTimeoutProxy;
+
+ // Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
+ this._request.onload = this._handleLoadProxy;
+ this._request.onreadystatechange = this._handleReadyStateChangeProxy;
+ }
// Set up a timeout if we don't have XHR2
if (this._xhrLevel == 1) {
@@ -194,10 +207,10 @@ this.createjs = this.createjs || {};
// 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) {
+ if (!this._item.values) {
this._request.send();
- } else if (this._item.method == createjs.AbstractLoader.POST) {
- this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
+ } else {
+ this._request.send(createjs.URLUtils.formatQueryString(this._item.values));
}
} catch (error) {
this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
@@ -205,6 +218,11 @@ this.createjs = this.createjs || {};
};
p.setResponseType = function (type) {
+ // Some old browsers doesn't support blob, so we convert arraybuffer to blob after response is downloaded
+ if (type === 'blob') {
+ type = window.URL ? 'blob' : 'arraybuffer';
+ this._responseType = type;
+ }
this._request.responseType = type;
};
@@ -310,6 +328,9 @@ this.createjs = this.createjs || {};
/**
* 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.
+ *
+ * Note that This method uses {{#crossLink "_checkError"}}{{/crossLink}} to determine if the server has returned an
+ * error code.
* @method _handleLoad
* @param {Object} event The XHR load event.
* @private
@@ -327,6 +348,21 @@ this.createjs = this.createjs || {};
}
this._response = this._getResponse();
+ // Convert arraybuffer back to blob
+ if (this._responseType === 'arraybuffer') {
+ try {
+ this._response = new Blob([this._response]);
+ } catch (e) {
+ // Fallback to use BlobBuilder if Blob constructor is not supported
+ // Tested on Android 2.3 ~ 4.2 and iOS5 safari
+ window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
+ if (e.name === 'TypeError' && window.BlobBuilder) {
+ var builder = new BlobBuilder();
+ builder.append(this._response);
+ this._response = builder.getBlob();
+ }
+ }
+ }
this._clean();
this.dispatchEvent(new createjs.Event("complete"));
@@ -341,30 +377,36 @@ this.createjs = this.createjs || {};
*/
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.
+ * Determine if there is an error in the current load.
+ * Currently this checks the status of the request for problem codes, and not actual response content:
+ *
+ *
Status codes between 400 and 599 (HTTP error range)
+ *
A status of 0, but *only when the application is running on a server*. If the application is running
+ * on `file:`, then it may incorrectly treat an error on local (or embedded applications) as a successful
+ * load.
+ *
* @method _checkError
- * @return {int} If the request status returns an error code.
+ * @return {Error} An error with the status code in the `message` argument.
* @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);
+ if (status >= 400 && status <= 599) {
+ return new Error(status);
+ } else if (status == 0) {
+ if ((/^https?:/).test(location.protocol)) { return new Error(0); }
+ return null; // Likely an embedded app.
+ } else {
+ return null;
}
- 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.
@@ -414,7 +456,7 @@ this.createjs = this.createjs || {};
*/
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 crossdomain = createjs.URLUtils.isCrossDomain(item);
var headers = {};
// Create the request. Fallback to whatever support we have.
@@ -429,11 +471,19 @@ this.createjs = this.createjs || {};
for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
var axVersion = s.ACTIVEX_VERSIONS[i];
try {
- req = new ActiveXObject(axVersions);
+ req = new ActiveXObject(axVersion);
break;
- } catch (e) {}
+ } catch (e) {
+ }
}
- if (req == null) { return false; }
+ 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.
@@ -445,21 +495,21 @@ this.createjs = this.createjs || {};
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);
+ if (item.method == createjs.Methods.GET) {
+ src = createjs.URLUtils.buildURI(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);
+ req.open(item.method || createjs.Methods.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) {
+ if (item.values && item.method == createjs.Methods.POST) {
headers["Content-Type"] = "application/x-www-form-urlencoded";
}
@@ -494,13 +544,23 @@ this.createjs = this.createjs || {};
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);
+ if (this._request.removeEventListener != null) {
+ 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);
+ } else {
+ this._request.onloadstart = null;
+ this._request.onprogress = null;
+ this._request.onabort = null;
+ this._request.onerror = null;
+ this._request.ontimeout = null;
+ this._request.onload = null;
+ this._request.onreadystatechange = null;
+ }
};
p.toString = function () {
diff --git a/src/preloadjs/utils/DomUtils.js b/src/preloadjs/utils/DomUtils.js
new file mode 100644
index 00000000..9ddff92d
--- /dev/null
+++ b/src/preloadjs/utils/DomUtils.js
@@ -0,0 +1,120 @@
+/*
+ * DomUtils
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module PreloadJS
+ */
+
+(function () {
+
+ /**
+ * A few utilities for interacting with the dom.
+ * @class DomUtils
+ */
+ var s = {
+ container: null
+ };
+
+ s.appendToHead = function (el) {
+ s.getHead().appendChild(el);
+ }
+
+ s.appendToBody = function (el) {
+ if (s.container == null) {
+ s.container = document.createElement("div");
+ s.container.id = "preloadjs-container";
+ var style = s.container.style;
+ style.visibility = "hidden";
+ style.position = "absolute";
+ style.width = s.container.style.height = "10px";
+ style.overflow = "hidden";
+ style.transform = style.msTransform = style.webkitTransform = style.oTransform = "translate(-10px, -10px)"; //LM: Not working
+ s.getBody().appendChild(s.container);
+ }
+ s.container.appendChild(el);
+ }
+
+ s.getHead = function () {
+ return document.head || document.getElementsByTagName("head")[0];
+ }
+
+ s.getBody = function () {
+ return document.body || document.getElementsByTagName("body")[0];
+ }
+
+ s.removeChild = function(el) {
+ if (el.parent) {
+ el.parent.removeChild(el);
+ }
+ }
+
+ /**
+ * 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;
+ }
+ };
+
+ createjs.DomUtils = s;
+
+}());
diff --git a/src/preloadjs/utils/Elements.js b/src/preloadjs/utils/Elements.js
new file mode 100644
index 00000000..04877dda
--- /dev/null
+++ b/src/preloadjs/utils/Elements.js
@@ -0,0 +1,94 @@
+/*
+ * Elements
+ *
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module PreloadJS
+ */
+
+(function () {
+
+ /**
+ * Convenience methods for creating various elements used by PrelaodJS.
+ *
+ * @class DomUtils
+ */
+ var s = {};
+
+ s.a = function() {
+ return s.el("a");
+ }
+
+ s.svg = function() {
+ return s.el("svg");
+ }
+
+ s.object = function() {
+ return s.el("object");
+ }
+
+ s.image = function() {
+ return s.el("image");
+ }
+
+ s.img = function() {
+ return s.el("img");
+ }
+
+ s.style = function() {
+ return s.el("style");
+ }
+
+ s.link = function() {
+ return s.el("link");
+ }
+
+ s.script = function() {
+ return s.el("script");
+ }
+
+ s.audio = function() {
+ return s.el("audio");
+ }
+
+ s.video = function() {
+ return s.el("video");
+ }
+
+ s.text = function(value) {
+ return document.createTextNode(value);
+ }
+
+ s.el = function(name) {
+ return document.createElement(name);
+ }
+
+ createjs.Elements = s;
+
+}());
diff --git a/src/preloadjs/utils/RequestUtils.js b/src/preloadjs/utils/RequestUtils.js
index ff3da676..69200c25 100644
--- a/src/preloadjs/utils/RequestUtils.js
+++ b/src/preloadjs/utils/RequestUtils.js
@@ -39,156 +39,6 @@
*/
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 an absolute 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
@@ -202,55 +52,14 @@
*/
s.isBinary = function (type) {
switch (type) {
- case createjs.AbstractLoader.IMAGE:
- case createjs.AbstractLoader.BINARY:
+ case createjs.Types.IMAGE:
+ case createjs.Types.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 {Objectitem
- * @returns {Boolean}
- * @static
- */
- s.isVideoTag = function(item) {
- if (window.HTMLVideoElement) {
- return item instanceof HTMLVideoElement;
- } else {
- false;
- }
- };
-
/**
* Determine if a specific type is a text-based asset, and should be loaded as UTF-8.
* @method isText
@@ -260,13 +69,14 @@
*/
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.Types.TEXT:
+ case createjs.Types.JSON:
+ case createjs.Types.MANIFEST:
+ case createjs.Types.XML:
+ case createjs.Types.CSS:
+ case createjs.Types.SVG:
+ case createjs.Types.JAVASCRIPT:
+ case createjs.Types.SPRITESHEET:
return true;
default:
return false;
@@ -278,13 +88,13 @@
* 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
+ * @return {String} The determined load type (for example, `createjs.Types.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;
+ return createjs.Types.TEXT;
}
switch (extension.toLowerCase()) {
@@ -294,30 +104,30 @@
case "png":
case "webp":
case "bmp":
- return createjs.AbstractLoader.IMAGE;
+ return createjs.Types.IMAGE;
case "ogg":
case "mp3":
case "webm":
- return createjs.AbstractLoader.SOUND;
+ return createjs.Types.SOUND;
case "mp4":
case "webm":
case "ts":
- return createjs.AbstractLoader.VIDEO;
+ return createjs.Types.VIDEO;
case "json":
- return createjs.AbstractLoader.JSON;
+ return createjs.Types.JSON;
case "xml":
- return createjs.AbstractLoader.XML;
+ return createjs.Types.XML;
case "css":
- return createjs.AbstractLoader.CSS;
+ return createjs.Types.CSS;
case "js":
- return createjs.AbstractLoader.JAVASCRIPT;
+ return createjs.Types.JAVASCRIPT;
case 'svg':
- return createjs.AbstractLoader.SVG;
+ return createjs.Types.SVG;
default:
- return createjs.AbstractLoader.TEXT;
+ return createjs.Types.TEXT;
}
};
createjs.RequestUtils = s;
-}());
\ No newline at end of file
+}());
diff --git a/src/preloadjs/utils/URLUtils.js b/src/preloadjs/utils/URLUtils.js
new file mode 100644
index 00000000..9aba8b40
--- /dev/null
+++ b/src/preloadjs/utils/URLUtils.js
@@ -0,0 +1,219 @@
+/*
+ * URLUtils
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module PreloadJS
+ */
+
+(function () {
+
+ /**
+ * Utilities that assist with parsing load items, and determining file types, etc.
+ * @class URLUtils
+ */
+ 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,
+ * the pieces of the path (protocol, hostname, port, pathname, search, hash, host)
+ * as well as an optional 'extension` property, which is the lowercase extension.
+ *
+ * @static
+ */
+ s.parseURI = function (path) {
+ var info = {
+ absolute: false,
+ relative: false,
+ protocol: null,
+ hostname: null,
+ port: null,
+ pathname: null,
+ search: null,
+ hash: null,
+ host: null
+ };
+
+ if (path == null) { return info; }
+
+ // Inject the path parts.
+ var parser = createjs.Elements.a();
+ parser.href = path;
+
+ for (var n in info) {
+ if (n in parser) {
+ info[n] = parser[n];
+ }
+ }
+
+ // 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 buildURI
+ * @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.buildURI = 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 = createjs.Elements.a();
+ target.href = item.src;
+
+ var host = createjs.Elements.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 = createjs.Elements.a();
+ target.href = item.src;
+ return target.hostname == "" && target.protocol == "file:";
+ };
+
+ createjs.URLUtils = s;
+
+}());
diff --git a/src/soundjs/AbstractPlugin.js b/src/soundjs/AbstractPlugin.js
index 9e299277..4fae3f5c 100644
--- a/src/soundjs/AbstractPlugin.js
+++ b/src/soundjs/AbstractPlugin.js
@@ -80,6 +80,15 @@ this.createjs = this.createjs || {};
*/
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}
@@ -96,29 +105,14 @@ this.createjs = this.createjs || {};
};
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 {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
- * method and is used internally.
+ * The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
- * @protected
+ * @private
* @static
*/
AbstractPlugin._capabilities = null;
@@ -141,17 +135,17 @@ this.createjs = this.createjs || {};
* can use to assist with preloading.
* @method register
* @param {String} loadItem An Object containing the source of the audio
- * @param {Number} instances The number of concurrently playing instances to allow for the channel at any time.
* Note that not every plugin will manage this value.
* @return {Object} A result object, containing a "tag" for preloading purposes.
*/
- p.register = function (loadItem, instances) {
+ 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] = [];
- if(this._loaders[loadItem.src]) {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?
- var loader = new this._loaderClass(loadItem);
- loader.on("complete", createjs.proxy(this._handlePreloadComplete, this));
+ loader = new this._loaderClass(loadItem);
+ loader.on("complete", this._handlePreloadComplete, this);
this._loaders[loadItem.src] = loader;
return loader;
};
@@ -163,7 +157,7 @@ this.createjs = this.createjs || {};
* @param {Loader} loader The sound URI to load.
*/
p.preload = function (loader) {
- loader.on("error", createjs.proxy(this._handlePreloadError, this));
+ loader.on("error", this._handlePreloadError, this);
loader.load();
};
@@ -229,12 +223,18 @@ this.createjs = this.createjs || {};
this.preload(this.register(src));
}
var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
- this._soundInstances[src].push(si);
+ if(this._soundInstances[src]){
+ this._soundInstances[src].push(si);
+ }
+
+ // Plugins that don't have a setVolume should implement a setMasterVolume/setMasterMute
+ // So we have to check that here.
+ si.setMasterVolume && si.setMasterVolume(createjs.Sound.volume);
+ si.setMasterMute && si.setMasterMute(createjs.Sound.muted);
+
return si;
};
- // TODO Volume & mute Getter / Setter??
- // TODO change calls to return nothing or this for chaining??
// 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.
@@ -252,7 +252,7 @@ this.createjs = this.createjs || {};
/**
* Get the master volume of the plugin, which affects all SoundInstances.
* @method getVolume
- * @return The volume level, between 0 and 1.
+ * @return {Number} The volume level, between 0 and 1.
*/
p.getVolume = function () {
return this._volume;
@@ -262,7 +262,7 @@ this.createjs = this.createjs || {};
* 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.
+ * the mute value of Sound {{#crossLink "Sound/muted:property"}}{{/crossLink}}, so this property is not used here.
* @return {Boolean} If the mute call succeeds.
*/
p.setMute = function (value) {
@@ -280,26 +280,33 @@ this.createjs = this.createjs || {};
/**
* Handles internal preload completion.
* @method _handlePreloadComplete
+ * @param event
* @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
+ var src = event.target.getItem().src,
+ result = event.result,
+ instances = this._soundInstances[src];
+ this._audioSources[src] = result;
+
+ if (instances != null && instances.length > 0) {
+ for (var i=0, l=instances.length; iNote this uses a getter setter, which is not supported by Firefox versions 3.6 or lower and Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setVolume"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getVolume"}}{{/crossLink}}.
*
* The actual output volume of a sound can be calculated using:
- * myInstance.volume * createjs.Sound.getVolume();
+ * myInstance.volume * createjs.Sound._getVolume();
*
* @property volume
* @type {Number}
* @default 1
*/
this._volume = 1;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "volume", {
- get: this.getVolume,
- set: this.setVolume
- });
- }
+ Object.defineProperty(this, "volume", {
+ get: this._getVolume,
+ set: this._setVolume
+ });
+ this.getVolume = createjs.deprecate(this._getVolume, "AbstractSoundInstance.getVolume");
+ this.setVolume = createjs.deprecate(this._setVolume, "AbstractSoundInstance.setVolume");
/**
* The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPan"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPan"}}{{/crossLink}}.
- * Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D 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;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "pan", {
- get: this.getPan,
- set: this.setPan
- });
- }
+ Object.defineProperty(this, "pan", {
+ get: this._getPan,
+ set: this._setPan
+ });
+ this.getPan = createjs.deprecate(this._getPan, "AbstractSoundInstance.getPan");
+ this.setPan = createjs.deprecate(this._setPan, "AbstractSoundInstance.setPan");
/**
- * The length of the audio clip, in milliseconds.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setDuration"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getDuration"}}{{/crossLink}}.
+ * 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
+ });
+ this.getStartTime = createjs.deprecate(this._getStartTime, "AbstractSoundInstance.getStartTime");
+ this.setStartTime = createjs.deprecate(this._setStartTime, "AbstractSoundInstance.setStartTime");
+
+ /**
+ * Sets or gets the length of the audio clip, value is in milliseconds.
*
* @property duration
* @type {Number}
@@ -182,12 +179,12 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._duration = Math.max(0, duration || 0);
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "duration", {
- get: this.getDuration,
- set: this.setDuration
- });
- }
+ Object.defineProperty(this, "duration", {
+ get: this._getDuration,
+ set: this._setDuration
+ });
+ this.getDuration = createjs.deprecate(this._getDuration, "AbstractSoundInstance.getDuration");
+ this.setDuration = createjs.deprecate(this._setDuration, "AbstractSoundInstance.setDuration");
/**
* Object that holds plugin specific resource need for audio playback.
@@ -199,39 +196,33 @@ this.createjs = this.createjs || {};
* @default null
*/
this._playbackResource = null;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "playbackResource", {
- get: this.getPlaybackResource,
- set: this.setPlaybackResource
- });
- }
- if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
+ Object.defineProperty(this, "playbackResource", {
+ get: this._getPlaybackResource,
+ set: this._setPlaybackResource
+ });
+ if(playbackResource !== false && playbackResource !== true) { this._setPlaybackResource(playbackResource); }
+ this.getPlaybackResource = createjs.deprecate(this._getPlaybackResource, "AbstractSoundInstance.getPlaybackResource");
+ this.setPlaybackResource = createjs.deprecate(this._setPlaybackResource, "AbstractSoundInstance.setPlaybackResource");
/**
* The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setPosition"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getPosition"}}{{/crossLink}}.
- *
* @property position
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._position = 0;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "position", {
- get: this.getPosition,
- set: this.setPosition
- });
- }
+ Object.defineProperty(this, "position", {
+ get: this._getPosition,
+ set: this._setPosition
+ });
+ this.getPosition = createjs.deprecate(this._getPosition, "AbstractSoundInstance.getPosition");
+ this.setPosition = createjs.deprecate(this._setPosition, "AbstractSoundInstance.setPosition");
/**
* The number of play loops remaining. Negative values will loop infinitely.
*
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setLoop"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getLoop"}}{{/crossLink}}.
- *
* @property loop
* @type {Number}
* @default 0
@@ -239,18 +230,15 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._loop = 0;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "loop", {
- get: this.getLoop,
- set: this.setLoop
- });
- }
+ Object.defineProperty(this, "loop", {
+ get: this._getLoop,
+ set: this._setLoop
+ });
+ this.getLoop = createjs.deprecate(this._getLoop, "AbstractSoundInstance.getLoop");
+ this.setLoop = createjs.deprecate(this._setLoop, "AbstractSoundInstance.setLoop");
/**
- * Determines if the audio is currently muted.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower. Instead use {{#crossLink "AbstractSoundInstance/setMute"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}}.
+ * Mutes or unmutes the current audio instance.
*
* @property muted
* @type {Boolean}
@@ -258,30 +246,26 @@ this.createjs = this.createjs || {};
* @since 0.6.0
*/
this._muted = false;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "muted", {
- get: this.getMuted,
- set: this.setMuted
- });
- }
+ Object.defineProperty(this, "muted", {
+ get: this._getMuted,
+ set: this._setMuted
+ });
+ this.getMuted = createjs.deprecate(this._getMuted, "AbstractSoundInstance.getMuted");
+ this.setMuted = createjs.deprecate(this._setMuted, "AbstractSoundInstance.setMuted");
/**
- * Tells you if the audio is currently paused.
- *
- * Note this uses a getter setter, which is not supported by Firefox versions 3.6 or lower, Opera versions 11.50 or lower,
- * and Internet Explorer 8 or lower.
- * Use {{#crossLink "AbstractSoundInstance/pause:method"}}{{/crossLink}} and {{#crossLink "AbstractSoundInstance/resume:method"}}{{/crossLink}} to set.
+ * Pauses or resumes the current audio instance.
*
* @property paused
* @type {Boolean}
*/
this._paused = false;
- if (createjs.definePropertySupported) {
- Object.defineProperty(this, "paused", {
- get: this.getPaused,
- set: this.setPaused
- });
- }
+ Object.defineProperty(this, "paused", {
+ get: this._getPaused,
+ set: this._setPaused
+ });
+ this.getPaused = createjs.deprecate(this._getPaused, "AbstractSoundInstance.getPaused");
+ this.setPaused = createjs.deprecate(this._setPaused, "AbstractSoundInstance.setPaused");
// Events
@@ -332,10 +316,6 @@ this.createjs = this.createjs || {};
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
@@ -344,75 +324,35 @@ this.createjs = this.createjs || {};
*
Example
*
* var myInstance = createjs.Sound.createInstance(mySrc);
- * myInstance.play({offset:1, loop:2, pan:0.5}); // options as object properties
- * myInstance.play(createjs.Sound.INTERRUPT_ANY); // options as parameters
+ * myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
*
- * Note that if this sound is already playing, this call will do nothing.
+ * 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] 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 Sound {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
- * OR
- * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
- * delay, offset, loop, volume, and pan (see the above code sample).
- * @param {Number} [delay=0] The delay in milliseconds before the sound starts
- * @param {Number} [offset=0] How far into the sound to begin playback, in milliseconds.
- * @param {Number} [loop=0] The number of times to loop the audio. Use -1 for infinite loops.
- * @param {Number} [volume=1] The volume of the sound, between 0 and 1.
- * @param {Number} [pan=0] The pan of the sound between -1 (left) and 1 (right). Note that pan is not supported
- * for HTML Audio.
+ * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
+ * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
- p.play = function (interrupt, delay, offset, loop, volume, pan) {
+ p.play = function (props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- if (interrupt instanceof Object) {
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- }
- if (offset != null) { this.setPosition(offset) }
- if (loop != null) { this.setLoop(loop); }
- if (volume != null) { this.setVolume(volume); }
- if (pan != null) { this.setPan(pan); }
- if (this._paused) { this.setPaused(false); }
+ this.applyPlayProps(playProps);
+ if (this._paused) { this._setPaused(false); }
return;
}
this._cleanUp();
- createjs.Sound._playInstance(this, interrupt, delay, offset, loop, volume, pan); // make this an event dispatch??
+ createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
return this;
};
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead.
- *
- * @method pause
- * @return {Boolean} If the pause call succeeds. This will return false if the sound isn't currently playing.
- * @deprecated
- */
- p.pause = function () {
- if (this._paused || this.playState != createjs.Sound.PLAY_SUCCEEDED) {return false;}
- this.setPaused(true);
- return true;
- };
-
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} instead.
- *
- * @method resume
- * @return {Boolean} If the resume call succeeds. This will return false if called on a sound that is not paused.
- * @deprecated
- */
- p.resume = function () {
- if (!this._paused) {return false;}
- this.setPaused(false);
- return true;
- };
-
/**
* 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}}.
+ * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
+ *
+ * If you don't want to lose your position use yourSoundInstance.paused = true instead. {{#crossLink "AbstractSoundInstance/paused"}}{{/crossLink}}.
*
*
Example
*
@@ -443,37 +383,50 @@ this.createjs = this.createjs || {};
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
/**
- * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property,
- * and getPaused remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Returns true if the instance is currently paused.
- *
- * @method getPaused
- * @returns {boolean} If the instance is currently paused
+ * Please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property.
+ * @method _getPaused
+ * @protected
+ * @return {boolean} If the instance is currently paused
* @since 0.6.0
*/
- p.getPaused = function() {
+ p._getPaused = function() {
return this._paused;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPaused remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Pause or resume the instance. Note you can also resume playback with {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
- *
+ * Please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
+ * @method _setPaused
+ * @protected
* @param {boolean} value
* @since 0.6.0
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
- p.setPaused = function (value) {
+ 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;
@@ -487,23 +440,13 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property,
- * setVolume remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the volume of the instance.
- *
- *
Example
- *
- * myInstance.setVolume(0.5);
- *
- * Note that the master volume set using the Sound API method {{#crossLink "Sound/setVolume"}}{{/crossLink}}
- * will be applied to the instance volume.
- *
- * @method setVolume
- * @param value The volume to set, between 0 and 1.
+ * Please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ * @method _setVolume
+ * @protected
+ * @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) {
+ p._setVolume = function (value) {
if (value == this._volume) { return this; }
this._volume = Math.max(0, Math.min(1, value));
if (!this._muted) {
@@ -513,59 +456,24 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} can be accessed directly as a property,
- * getVolume remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the volume of the instance. The actual output volume of a sound can be calculated using:
- * myInstance.getVolume() * createjs.Sound.getVolume();
- *
- * @method getVolume
- * @return The current volume of the sound instance.
+ * Please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
+ * @method _getVolume
+ * @protected
+ * @return {Number} The current volume of the sound instance.
*/
- p.getVolume = function () {
+ p._getVolume = function () {
return this._volume;
};
/**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * @method setMute
- * @param {Boolean} value If the sound should be muted.
- * @return {Boolean} If the mute call succeeds.
- * @deprecated
- */
- p.setMute = function (value) {
- this.setMuted(value);
- };
-
- /**
- * Deprecated, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
- *
- * @method getMute
- * @return {Boolean} If the sound is muted.
- * @deprecated
- */
- p.getMute = function () {
- return this._muted;
- };
-
- /**
- * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property,
- * setMuted exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Mute and unmute the sound. Muted sounds will still play at 0 volume. Note that an unmuted sound may still be
- * silent depending on {{#crossLink "Sound"}}{{/crossLink}} volume, instance volume, and Sound muted.
- *
- *
Example
- *
- * myInstance.setMuted(true);
- *
- * @method setMute
+ * Please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ * @method _setMuted
+ * @protected
* @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) {
+ p._setMuted = function (value) {
if (value !== true && value !== false) {return;}
this._muted = value;
this._updateVolume();
@@ -573,40 +481,24 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} can be accessed directly as a property,
- * getMuted remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the mute value of the instance.
- *
- *
Example
- *
- * var isMuted = myInstance.getMuted();
- *
- * @method getMute
+ * Please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
+ * @method _getMuted
+ * @protected
* @return {Boolean} If the sound is muted.
* @since 0.6.0
*/
- p.getMuted = function () {
+ p._getMuted = function () {
return this._muted;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPan remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the left(-1)/right(+1) pan of the instance. Note that {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}} does not
- * support panning, and only simple left/right panning has been implemented for {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
- * The default pan value is 0 (center).
- *
- *
Example
- *
- * myInstance.setPan(-1); // to the left!
- *
- * @method setPan
+ * Please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ * @method _setPan
+ * @protected
* @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) {
+ p._setPan = function (value) {
if(value == this._pan) { return this; }
this._pan = Math.max(-1, Math.min(1, value));
this._updatePan();
@@ -614,59 +506,36 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPan remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the left/right pan of the instance. Note in WebAudioPlugin this only gives us the "x" value of what is
- * actually 3D audio.
- *
- *
Example
- *
- * var myPan = myInstance.getPan();
- *
- * @method getPan
+ * Please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
+ * @method _getPan
+ * @protected
* @return {Number} The value of the pan, between -1 (left) and 1 (right).
*/
- p.getPan = function () {
+ p._getPan = function () {
return this._pan;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPosition remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the position of the playhead of the instance in milliseconds.
- *
- *
Example
- *
- * var currentOffset = myInstance.getPosition();
- *
- * @method getPosition
+ * Please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ * @method _getPosition
+ * @protected
* @return {Number} The position of the playhead in the sound, in milliseconds.
*/
- p.getPosition = function () {
+ p._getPosition = function () {
if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
- return this._calculateCurrentPosition(); // sets this._position
+ this._position = this._calculateCurrentPosition();
}
return this._position;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPosition remains to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the position of the playhead in the instance. This can be set while a sound is playing, paused, or
- * stopped.
- *
- *
Example
- *
- * myInstance.setPosition(myInstance.getDuration()/2); // set audio to its halfway point.
- *
- * @method setPosition
+ * Please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
+ * @method _setPosition
+ * @protected
* @param {Number} value The position to place the playhead, in milliseconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
- p.setPosition = function (value) {
+ p._setPosition = function (value) {
this._position = Math.max(0, value);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._updatePosition();
@@ -675,35 +544,48 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property,
- * getDuration exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Get the duration of the instance, in milliseconds.
- * Note a sound needs to be loaded before it will have duration, unless it was set manually to create an audio sprite.
- *
- *
Example
- *
- * var soundDur = myInstance.getDuration();
- *
- * @method getDuration
+ * Please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ * @method _getStartTime
+ * @protected
+ * @return {Number} The startTime of the sound instance in milliseconds.
+ */
+ p._getStartTime = function () {
+ return this._startTime;
+ };
+
+ /**
+ * Please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
+ * @method _setStartTime
+ * @protected
+ * @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;
+ };
+
+ /**
+ * Please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ * @method _getDuration
+ * @protected
* @return {Number} The duration of the sound instance in milliseconds.
*/
- p.getDuration = function () {
+ p._getDuration = function () {
return this._duration;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} can be accessed directly as a property,
- * setDuration exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the duration of the audio. Generally this is not called, but it can be used to create an audio sprite out of an existing AbstractSoundInstance.
- *
- * @method setDuration
+ * Please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
+ * @method _setDuration
+ * @protected
* @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) {
+ p._setDuration = function (value) {
if (value == this._duration) { return this; }
this._duration = Math.max(0, value || 0);
this._updateDuration();
@@ -711,70 +593,57 @@ this.createjs = this.createjs || {};
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property,
- * setPlaybackResource exists to allow support for IE8 with FlashAudioPlugin.
- *
- * An object containing any resources needed for audio playback, set by the plugin.
- * Only meant for use by advanced users.
- *
- * @method setPlayback
+ * Please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ * @method _setPlaybackResource
+ * @protected
* @param {Object} value The new playback resource.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
**/
- p.setPlaybackResource = function (value) {
+ p._setPlaybackResource = function (value) {
this._playbackResource = value;
- if (this._duration == 0) { this._setDurationFromSource(); }
+ if (this._duration == 0 && this._playbackResource) { this._setDurationFromSource(); }
return this;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} can be accessed directly as a property,
- * getPlaybackResource exists to allow support for IE8 with FlashAudioPlugin.
- *
- * An object containing any resources needed for audio playback, usually set by the plugin.
- *
- * @method setPlayback
+ * Please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
+ * @method _getPlaybackResource
+ * @protected
* @param {Object} value The new playback resource.
* @return {Object} playback resource used for playing audio
* @since 0.6.0
**/
- p.getPlaybackResource = function () {
+ p._getPlaybackResource = function () {
return this._playbackResource;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property,
- * getLoop exists to allow support for IE8 with FlashAudioPlugin.
- *
- * The number of play loops remaining. Negative values will loop infinitely.
- *
- * @method getLoop
+ * Please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ * @method _getLoop
+ * @protected
* @return {number}
* @since 0.6.0
**/
- p.getLoop = function () {
+ p._getLoop = function () {
return this._loop;
};
/**
- * NOTE {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} can be accessed directly as a property,
- * setLoop exists to allow support for IE8 with FlashAudioPlugin.
- *
- * Set the number of play loops remaining.
- *
- * @method setLoop
+ * Please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
+ * @method _setLoop
+ * @protected
* @param {number} value The number of times to loop after play.
* @since 0.6.0
*/
- p.setLoop = function (value) {
+ p._setLoop = function (value) {
if(this._playbackResource != null) {
// remove looping
if (this._loop != 0 && value == 0) {
this._removeLooping(value);
}
// add looping
- if (this._loop == 0 && value != 0) {
+ else if (this._loop == 0 && value != 0) {
this._addLooping(value);
}
}
@@ -822,18 +691,20 @@ this.createjs = this.createjs || {};
* 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 {Number} offset How far into the sound to begin playback, in milliseconds.
- * @param {Number} loop The number of times to loop the audio. Use -1 for infinite loops.
- * @param {Number} volume The volume of the sound, between 0 and 1.
- * @param {Number} pan The pan of the sound between -1 (left) and 1 (right). Note that pan does not work for HTML Audio.
+ * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If playback succeeded.
* @protected
*/
- p._beginPlaying = function (offset, loop, volume, pan) {
- this.setPosition(offset);
- this.setLoop(loop);
- this.setVolume(volume);
- this.setPan(pan);
+ // 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;
@@ -910,6 +781,16 @@ this.createjs = this.createjs || {};
// 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
@@ -931,8 +812,8 @@ this.createjs = this.createjs || {};
};
/**
- * Internal function that calculates the current position of the playhead and sets it on this._position
- * @method _updatePosition
+ * Internal function that calculates the current position of the playhead and sets this._position to that value
+ * @method _calculateCurrentPosition
* @protected
* @since 0.6.0
*/
@@ -953,20 +834,22 @@ this.createjs = this.createjs || {};
/**
* 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 () {
+ 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 () {
+ p._addLooping = function (value) {
// plugin specific code
};
@@ -1012,7 +895,7 @@ this.createjs = this.createjs || {};
/**
* Internal function called when AbstractSoundInstance has played to end and is looping
- * @method _handleCleanUp
+ * @method _handleLoop
* @protected
* @since 0.6.0
*/
diff --git a/src/soundjs/Sound.js b/src/soundjs/Sound.js
index 53c2c4c2..211f94be 100644
--- a/src/soundjs/Sound.js
+++ b/src/soundjs/Sound.js
@@ -36,20 +36,23 @@ this.createjs = this.createjs || {};
* to play sounds.
*
* To use SoundJS, use the public API on the {{#crossLink "Sound"}}{{/crossLink}} class. This API is for:
- *
Installing audio playback Plugins
+ *
+ *
Installing audio playback Plugins
*
Registering (and preloading) sounds
*
Creating and playing sounds
*
Master volume, mute, and stop controls for all sounds at once
*
*
* Controlling Sounds
- * Playing sounds creates {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances, which can be controlled individually.
- *
Pause, resume, seek, and stop sounds
+ * Playing sounds creates {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances, which can be controlled
+ * individually.
+ *
+ *
Pause, resume, seek, and stop sounds
*
Control a sound's volume, mute, and pan
*
Listen for events on sound instances to get notified when they finish, loop, or fail
- * Audio will work in browsers which support WebAudio (http://caniuse.com/audio-api)
- * or HTMLAudioElement (http://caniuse.com/audio). A Flash fallback can be added
- * as well, which will work in any browser that supports the Flash player.
+ * Audio will work in browsers which support Web Audio (http://caniuse.com/audio-api)
+ * or HTMLAudioElement (http://caniuse.com/audio).
+ * A Flash fallback can be used for any browser that supports the Flash player, and the Cordova plugin can be used in
+ * any webview that supports Cordova.Media.
+ * IE8 and earlier are not supported, even with the Flash fallback. To support earlier browsers, you can use an older
+ * version of SoundJS (version 0.5.2 and earlier).
+ *
* @module SoundJS
* @main SoundJS
*/
@@ -99,14 +106,15 @@ this.createjs = this.createjs || {};
*
*
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.on("fileload", 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.on("complete", this.handleComplete, this);
* instance.volume = 0.5;
* }
*
@@ -119,37 +127,20 @@ this.createjs = this.createjs || {};
*
* Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
* automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
- * load. As a result, it may 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.
+ * 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:event"}}{{/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 Audio Sprites, available as of version 0.6.0.
+ * 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.
*
- * 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 until now restricted how many
- * different sounds we could load 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 – 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.
- *
Audio sprites share the same core source, so if you have a sprite with 5 sounds and are limited to 2
- * concurrently playing instances, that means 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: {
@@ -158,7 +149,7 @@ this.createjs = this.createjs || {};
* {id:"sound2", startTime:1000, duration:400},
* {id:"sound3", startTime:1700, duration: 1000}
* ]}
- * }
+ * }
* ];
* createjs.Sound.alternateExtensions = ["mp3"];
* createjs.Sound.on("fileload", loadSound);
@@ -166,26 +157,60 @@ this.createjs = this.createjs || {};
* // 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.
+ * Mobile Playback
+ * Devices running iOS require the WebAudio context to be "unlocked" by playing at least one sound inside of a user-
+ * initiated event (such as touch/click). Earlier versions of SoundJS included a "MobileSafe" sample, but this is no
+ * longer necessary as of SoundJS 0.6.2.
+ *
+ *
+ * In SoundJS 0.4.1 and above, you can either initialize plugins or use the {{#crossLink "WebAudioPlugin/playEmptySound"}}{{/crossLink}}
+ * method in the call stack of a user input event to manually unlock the audio context.
+ *
+ *
+ * In SoundJS 0.6.2 and above, SoundJS will automatically listen for the first document-level "mousedown"
+ * and "touchend" event, and unlock WebAudio. This will continue to check these events until the WebAudio
+ * context becomes "unlocked" (changes from "suspended" to "running")
+ *
+ *
+ * Both the "mousedown" and "touchend" events can be used to unlock audio in iOS9+, the "touchstart" event
+ * will work in iOS8 and below. The "touchend" event will only work in iOS9 when the gesture is interpreted
+ * as a "click", so if the user long-presses the button, it will no longer work.
+ *
+ *
+ * When using the EaselJS Touch class,
+ * the "mousedown" event will not fire when a canvas is clicked, since MouseEvents are prevented, to ensure
+ * only touch events fire. To get around this, you can either rely on "touchend", or:
+ *
+ *
Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).
+ *
Set the `preventSelection` property on the EaselJS `Stage` to `false`.
+ *
+ * These settings may change how your application behaves, and are not recommended.
+ *
+ *
*
- * createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400});
+ * Loading Alternate Paths and Extension-less Files
+ * SoundJS supports loading alternate paths and extension-less files by passing an object instead of a string for
+ * the `src` property, which is a hash using the format `{extension:"path", extension2:"path2"}`. These labels are
+ * how SoundJS determines if the browser will support the sound. This also enables multiple formats to live in
+ * different folders, or on CDNs, which often has completely different filenames for each file.
*
- * 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.
+ * 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.
*
*
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
- * }
+ * var sounds = {path:"./audioPath/",
+ * manifest: [
+ * {id: "cool", src: {mp3:"mp3/awesome.mp3", ogg:"noExtensionOggFile"}}
+ * ]};
*
- *
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
@@ -194,22 +219,23 @@ this.createjs = this.createjs || {};
* 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.
+ * 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.
+ * 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.
- *
Note HTMLAudioPlugin is not supported on iOS by default. See {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
- * for more details.
+ *
Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
+ * Mobile Playback above.
+ *
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.
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 can not be used to avoid cross domain issues.
*
* @class Sound
* @static
@@ -327,10 +356,11 @@ this.createjs = this.createjs || {};
* 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", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
+ * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
* @since 0.4.0
+ * @static
*/
- s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
+ 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
@@ -342,6 +372,7 @@ this.createjs = this.createjs || {};
* @type {Object}
* @since 0.4.0
* @default {m4a:"mp4"}
+ * @static
*/
s.EXTENSION_MAP = {
m4a:"mp4"
@@ -353,7 +384,7 @@ this.createjs = this.createjs || {};
* @property FILE_PATTERN
* @type {RegExp}
* @static
- * @protected
+ * @private
*/
s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
@@ -395,6 +426,7 @@ this.createjs = this.createjs || {};
* @property alternateExtensions
* @type {Array}
* @since 0.5.2
+ * @static
*/
s.alternateExtensions = [];
@@ -409,60 +441,232 @@ this.createjs = this.createjs || {};
s.activePlugin = null;
-// 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;
+// class getter / setter properties
/**
- * Used internally to assign unique IDs to each AbstractSoundInstance.
- * @property _lastID
+ * 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
* @static
- * @protected
+ * @since 0.6.1
*/
- s._lastID = 0;
/**
- * The master volume value, which affects all sounds. Use {{#crossLink "Sound/getVolume"}}{{/crossLink}} and
- * {{#crossLink "Sound/setVolume"}}{{/crossLink}} to modify the volume of all audio.
+ * The internal volume level. Use {{#crossLink "Sound/volume:property"}}{{/crossLink}} to adjust the master volume.
* @property _masterVolume
- * @type {Number}
+ * @type {number}
* @default 1
- * @protected
- * @since 0.4.0
+ * @private
*/
s._masterVolume = 1;
/**
- * The master mute value, which affects all sounds. This is applies to all sound instances. This value can be set
- * through {{#crossLink "Sound/setMute"}}{{/crossLink}} and accessed via {{#crossLink "Sound/getMute"}}{{/crossLink}}.
- * @property _masterMute
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _getMasterVolume
+ * @private
+ * @static
+ * @return {Number}
+ **/
+ s._getMasterVolume = function() {
+ return this._masterVolume;
+ };
+
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method getMasterVolume
+ * @deprecated
+ */
+ // Sound.getMasterVolume is @deprecated. Remove for 1.1+
+ s.getVolume = createjs.deprecate(s._getMasterVolume, "Sound.getVolume");
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method _setMasterVolume
+ * @static
+ * @private
+ **/
+ s._setMasterVolume = function(value) {
+ if (Number(value) == null) { return; }
+ 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);
+ }
+ }
+ };
+
+ /**
+ * Use the {{#crossLink "Sound/volume:property"}}{{/crossLink}} property instead.
+ * @method setVolume
+ * @deprecated
+ */
+ // Sound.setVolume is @deprecated. Remove for 1.1+
+ s.setVolume = createjs.deprecate(s._setMasterVolume, "Sound.setVolume");
+
+ /**
+ * 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
- * @protected
* @static
- * @since 0.4.0
+ * @since 0.6.1
*/
s._masterMute = false;
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _getMute
+ * @returns {Boolean}
+ * @static
+ * @private
+ */
+ s._getMute = function () {
+ return this._masterMute;
+ };
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method getMute
+ * @deprecated
+ */
+ // Sound.getMute is @deprecated. Remove for 1.1+
+ s.getMute = createjs.deprecate(s._getMute, "Sound.getMute");
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method _setMute
+ * @param {Boolean} value The muted value
+ * @static
+ * @private
+ */
+ s._setMute = function (value) {
+ if (value == null) { return; }
+ 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);
+ }
+ }
+ };
+
+ /**
+ * Use the {{#crossLink "Sound/muted:property"}}{{/crossLink}} property instead.
+ * @method setMute
+ * @deprecated
+ */
+ // Sound.setMute is @deprecated. Remove for 1.1+
+ s.setMute = createjs.deprecate(s._setMute, "Sound.setMute");
+
+ /**
+ * 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
+ */
+
+ /**
+ * Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
+ * @returns {null}
+ * @private
+ */
+ s._getCapabilities = function() {
+ if (s.activePlugin == null) { return null; }
+ return s.activePlugin._capabilities;
+ };
+
+ /**
+ * Use the {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} property instead.
+ * @method getCapabilities
+ * @deprecated
+ */
+ // Sound.getCapabilities is @deprecated. Remove for 1.1+
+ s.getCapabilities = createjs.deprecate(s._getCapabilities, "Sound.getCapabilities");
+
+ Object.defineProperties(s, {
+ volume: { get: s._getMasterVolume, set: s._setMasterVolume },
+ muted: { get: s._getMute, set: s._setMute },
+ capabilities: { get: s._getCapabilities }
+ });
+
+
+// Class Private properties
+ /**
+ * Determines if the plugins have been registered. If false, the first call to {{#crossLink "play"}}{{/crossLink}} 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
+ * @private
+ */
+ s._pluginsRegistered = false;
+
+ /**
+ * Used internally to assign unique IDs to each AbstractSoundInstance.
+ * @property _lastID
+ * @type {Number}
+ * @static
+ * @private
+ */
+ 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}}.
+ * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/volume:property"}}{{/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
+ * @private
* @static
*/
s._instances = [];
@@ -471,7 +675,7 @@ this.createjs = this.createjs || {};
* An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
* @property _idHash
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._idHash = {};
@@ -482,11 +686,22 @@ this.createjs = this.createjs || {};
* and data.
* @property _preloadHash
* @type {Object}
- * @protected
+ * @private
* @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}
+ * @private
+ * @static
+ * @since 0.6.1
+ */
+ s._defaultPlayPropsHash = {};
+
// EventDispatcher methods:
s.addEventListener = null;
@@ -536,9 +751,9 @@ this.createjs = this.createjs || {};
*
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"}}{{/crossLink}}).
+ *
extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).
* @static
- * @protected
+ * @private
*/
s.getPreloadHandlers = function () {
return {
@@ -552,7 +767,7 @@ this.createjs = this.createjs || {};
* Used to dispatch fileload events from internal loading.
* @method _handleLoadComplete
* @param event A loader event.
- * @protected
+ * @private
* @static
* @since 0.6.0
*/
@@ -579,8 +794,9 @@ this.createjs = this.createjs || {};
/**
* Used to dispatch error events from internal preloading.
* @param event
- * @protected
+ * @private
* @since 0.6.0
+ * @static
*/
s._handleLoadError = function(event) {
var src = event.target.getItem().src;
@@ -655,6 +871,7 @@ this.createjs = this.createjs || {};
* @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;}
@@ -682,63 +899,17 @@ this.createjs = this.createjs || {};
return (s.activePlugin != null);
};
- /**
- * 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.
- *
- * @method getCapabilities
- * @return {Object} An object containing the capabilities of the active plugin.
- * @static
- */
- s.getCapabilities = function () {
- if (s.activePlugin == null) {return null;}
- return s.activePlugin._capabilities;
- };
-
- /**
- * Get a specific capability of the active plugin. See {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} for a
- * full list of capabilities.
- *
- *
Example
- *
- * var maxAudioInstances = createjs.Sound.getCapability("tracks");
- *
- * @method getCapability
- * @param {String} key The capability to retrieve
- * @return {Number|Boolean} The value of the capability.
- * @static
- * @see getCapabilities
- */
- 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
+ * @private
* @static
*/
s.initLoad = function (loadItem) {
+ if (loadItem.type == "video") { return true; } // Don't handle video. PreloadJS's plugin model is really aggressive.
return s._registerSound(loadItem);
};
@@ -758,13 +929,19 @@ this.createjs = this.createjs || {};
s._registerSound = function (loadItem) {
if (!s.initializeDefaultPlugins()) {return false;}
- var details = s._parsePath(loadItem.src);
+ 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 = s.activePlugin.defaultNumChannels || null;
+ var numChannels = null;
if (data != null) {
if (!isNaN(data.channels)) {
numChannels = parseInt(data.channels);
@@ -777,11 +954,15 @@ this.createjs = this.createjs || {};
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, numChannels); // Note only HTML audio uses numChannels
+ var loader = s.activePlugin.register(loadItem);
SoundChannel.create(loadItem.src, numChannels);
@@ -792,9 +973,11 @@ this.createjs = this.createjs || {};
loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
}
- //if (loader.onload) {details.completeHandler = loader.onload;} // used by preloadJS
if (loader.type) {loadItem.type = loader.type;}
+ if (loadItem.defaultPlayProps) {
+ s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
+ }
return loader;
};
@@ -808,34 +991,39 @@ this.createjs = this.createjs || {};
* 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
- * @param {String} [id] An id specified by the user to play the sound later.
+ * @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 audioSprite array of objects in the following format {id, startTime, duration}.
+ * 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) {
- var loadItem = {src: src, id: id, data:data};
- if (src instanceof Object) {
+ 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 = basePath + src;}
+ if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + loadItem.src;}
var loader = s._registerSound(loadItem);
if(!loader) {return false;}
@@ -844,8 +1032,8 @@ this.createjs = this.createjs || {};
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));
+ loader.on("complete", this._handleLoadComplete, this);
+ loader.on("error", this._handleLoadError, this);
s.activePlugin.preload(loader);
} else {
if (s._preloadHash[loadItem.src][0] == true) {return true;}
@@ -861,10 +1049,12 @@ this.createjs = this.createjs || {};
*
*
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
@@ -873,7 +1063,9 @@ this.createjs = this.createjs || {};
* @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 set an optional path property that will be prepended to the src of each object.
+ * 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.
@@ -890,37 +1082,15 @@ this.createjs = this.createjs || {};
} 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);
+ returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
}
return returnValues;
};
- /**
- * Deprecated. Please use {{#crossLink "Sound/registerSounds"}}{{/crossLink} instead.
- *
- * @method registerManifest
- * @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.
- * @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.
- * @since 0.4.0
- * @depreacted
- */
- s.registerManifest = function(manifest, basePath) {
- try {
- console.log("createjs.Sound.registerManifest is deprecated, please use createjs.Sound.registerSounds.")
- } catch (error) {
-
- };
- return this.registerSounds(manifest, basePath);
- };
-
/**
* Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
@@ -929,11 +1099,13 @@ this.createjs = this.createjs || {};
*
*
Example
*
- * createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
* 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
+ * @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
@@ -942,13 +1114,18 @@ this.createjs = this.createjs || {};
s.removeSound = function(src, basePath) {
if (s.activePlugin == null) {return false;}
- if (src instanceof Object) {src = src.src;}
- src = s._getSrcById(src).src;
- if (basePath != null) {src = basePath + src;}
+ if (src instanceof Object && src.src) {src = src.src;}
- var details = s._parsePath(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) {
@@ -974,6 +1151,7 @@ this.createjs = this.createjs || {};
*
*
Example
*
+ * assetPath = "./myPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
@@ -984,7 +1162,7 @@ this.createjs = this.createjs || {};
* @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 set an optional path property that will be prepended to the src of each object.
+ * 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.
@@ -999,6 +1177,7 @@ this.createjs = this.createjs || {};
} 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);
@@ -1006,28 +1185,6 @@ this.createjs = this.createjs || {};
return returnValues;
};
- /**
- * Deprecated. Please use {{#crossLink "Sound/removeSounds"}}{{/crossLink}} instead.
- *
- * @method removeManifest
- * @param {Array} manifest An array of objects to remove. Objects are expected to be in the format needed for
- * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: {srcOrID:srcURIorID}
- * @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 in manifest was
- * successfully removed.
- * @static
- * @since 0.4.1
- * @deprecated
- */
- s.removeManifest = function (manifest, basePath) {
- try {
- console.log("createjs.Sound.removeManifest is deprecated, please use createjs.Sound.removeSounds.");
- } catch (error) {
-
- };
- return s.removeSounds(manifest, basePath);
- };
-
/**
* Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
@@ -1063,6 +1220,7 @@ this.createjs = this.createjs || {};
* @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; }
@@ -1072,17 +1230,19 @@ this.createjs = this.createjs || {};
} 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
+ * 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
+ * @private
+ * @static
*/
s._parsePath = function (value) {
if (typeof(value) != "string") {value = value.toString();}
@@ -1092,7 +1252,7 @@ this.createjs = this.createjs || {};
var name = match[4];
var ext = match[5];
- var c = s.getCapabilities();
+ var c = s.capabilities;
var i = 0;
while (!c[ext]) {
ext = s.alternateExtensions[i++];
@@ -1104,15 +1264,49 @@ this.createjs = this.createjs || {};
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.
+ * @private
+ * @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.
+ * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to
+ * play, an 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 the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
+ * method, 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 still be returned, which will
+ * not play any audio, but will not generate errors.
*
*
Example
*
@@ -1120,49 +1314,25 @@ this.createjs = this.createjs || {};
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* createjs.Sound.play("myID");
- * // we can pass in options we want to set inside of an object, and store off AbstractSoundInstance for controlling
+ * // store off AbstractSoundInstance for controlling
* var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
- * // alternately, we can pass full source path and specify each argument individually
- * var myInstance = createjs.Sound.play("myAudioPath/mySound.mp3", createjs.Sound.INTERRUPT_ANY, 0, 0, -1, 1, 0);
* }
*
- * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
+ * NOTE: To create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* @method play
* @param {String} src The src or ID of the audio.
- * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
- * 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}}.
- * OR
- * This parameter can be 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).
- * @param {Number} [delay=0] The amount of time to delay the start of audio playback, in milliseconds.
- * @param {Number} [offset=0] The offset from the start of the audio to begin playback, in milliseconds.
- * @param {Number} [loop=0] How many times the audio loops when it reaches the end of playback. The default is 0 (no
- * loops), and -1 can be used for infinite playback.
- * @param {Number} [volume=1] The volume of the sound, between 0 and 1. Note that the master volume is applied
- * against the individual volume.
- * @param {Number} [pan=0] The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
- * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
- * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
- * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
+ * @param {Object | PlayPropsConfig} props A PlayPropsConfig instance, or an object that contains the parameters to
+ * play a sound. See the {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for more info.
+ * @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) {
- if (interrupt instanceof Object) {
- delay = interrupt.delay;
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- startTime = interrupt.startTime;
- duration = interrupt.duration;
- interrupt = interrupt.interrupt;
-
- }
- var instance = s.createInstance(src, startTime, duration);
- var ok = s._playInstance(instance, interrupt, delay, offset, loop, volume, pan);
+ s.play = function (src, props) {
+ var playProps = createjs.PlayPropsConfig.create(props);
+ var instance = s.createInstance(src, playProps.startTime, playProps.duration);
+ var ok = s._playInstance(instance, playProps);
if (!ok) {instance._playFailed();}
return instance;
};
@@ -1193,10 +1363,12 @@ this.createjs = this.createjs || {};
* @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);}
+ 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);
@@ -1204,10 +1376,15 @@ this.createjs = this.createjs || {};
var instance = null;
if (details != null && details.src != null) {
SoundChannel.create(details.src);
- if (startTime == null) {startTime = src.startTime;}
+ 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 = new createjs.DefaultSoundInstance(src, startTime, duration);
}
instance.uniqueId = s._lastID++;
@@ -1216,107 +1393,49 @@ this.createjs = this.createjs || {};
};
/**
- * 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/setVolume"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * createjs.Sound.setVolume(0.5);
- *
- * @method setVolume
- * @param {Number} value The master volume value. The acceptable range is 0-1.
- * @static
- */
- 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);
- }
- }
- };
-
- /**
- * Get the master volume of Sound. The master volume is multiplied against each sound's individual volume.
- * To get individual sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead.
- *
- *
Example
- *
- * var masterVolume = createjs.Sound.getVolume();
- *
- * @method getVolume
- * @return {Number} The master volume, in a range of 0-1.
- * @static
- */
- s.getVolume = function () {
- return s._masterVolume;
- };
-
- /**
- * 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/setMute"}}{{/crossLink}} instead.
+ * 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.setMute(true);
+ * createjs.Sound.stop();
*
- * @method setMute
- * @param {Boolean} value Whether the audio should be muted or not.
- * @return {Boolean} If the mute was set.
+ * @method stop
* @static
- * @since 0.4.0
*/
- 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);
- }
+ s.stop = function () {
+ var instances = this._instances;
+ for (var i = instances.length; i--; ) {
+ instances[i].stop(); // NOTE stop removes instance from this._instances
}
- return true;
};
/**
- * Returns the global mute value. To get the mute value of an individual instance, use AbstractSoundInstance
- * {{#crossLink "AbstractSoundInstance/getMute"}}{{/crossLink}} instead.
- *
- *
Example
+ * Set the default playback properties for all new SoundInstances of the passed in src or ID.
+ * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
*
- * var muted = createjs.Sound.getMute();
- *
- * @method getMute
- * @return {Boolean} The mute value of Sound.
- * @static
- * @since 0.4.0
+ * @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.getMute = function () {
- return this._masterMute;
+ s.setDefaultPlayProps = function(src, playProps) {
+ src = s._getSrcById(src);
+ s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
};
/**
- * 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();
+ * 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 stop
- * @static
+ * @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.stop = function () {
- var instances = this._instances;
- for (var i = instances.length; i--; ) {
- instances[i].stop(); // NOTE stop removes instance from this._instances
- }
+ s.getDefaultPlayProps = function(src) {
+ src = s._getSrcById(src);
+ return s._defaultPlayPropsHash[s._parsePath(src.src).src];
};
@@ -1328,49 +1447,30 @@ this.createjs = this.createjs || {};
* control delays.
* @method _playInstance
* @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
- * @param {String | Object} [interrupt="none"|options] How to interrupt any currently playing instances of audio with the same source,
- * if the maximum number of instances of the sound are already playing. Values are defined as INTERRUPT_TYPE
- * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior"}}{{/crossLink}}.
- * OR
- * This parameter can be an object that contains any or all optional properties by name, including: interrupt,
- * delay, offset, loop, volume, and pan (see the above code sample).
- * @param {Number} [delay=0] Time in milliseconds before playback begins.
- * @param {Number} [offset=instance.offset] Time into the sound to begin playback in milliseconds. Defaults to the
- * current value on the instance.
- * @param {Number} [loop=0] The number of times to loop the audio. Use 0 for no loops, and -1 for an infinite loop.
- * @param {Number} [volume] The volume of the sound between 0 and 1. Defaults to current instance value.
- * @param {Number} [pan] The pan of the sound between -1 and 1. Defaults to current instance value.
+ * @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
+ * @private
* @static
*/
- s._playInstance = function (instance, interrupt, delay, offset, loop, volume, pan) {
- if (interrupt instanceof Object) {
- delay = interrupt.delay;
- offset = interrupt.offset;
- loop = interrupt.loop;
- volume = interrupt.volume;
- pan = interrupt.pan;
- interrupt = interrupt.interrupt;
- }
-
- interrupt = interrupt || s.defaultInterruptBehavior;
- if (delay == null) {delay = 0;}
- if (offset == null) {offset = instance.getPosition();}
- if (loop == null) {loop = instance.loop;}
- if (volume == null) {volume = instance.volume;}
- if (pan == null) {pan = instance.pan;}
-
- if (delay == 0) {
- var ok = s._beginPlaying(instance, interrupt, offset, loop, volume, pan);
+ 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.position;}
+ 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, interrupt, offset, loop, volume, pan);
- }, delay);
+ s._beginPlaying(instance, playProps);
+ }, playProps.delay);
instance.delayTimeoutId = delayTimeoutId;
}
@@ -1383,24 +1483,17 @@ this.createjs = this.createjs || {};
* 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 {String} [interrupt=none] How this sound interrupts other instances with the same source. Defaults to
- * {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}. Interrupts are defined as INTERRUPT_TYPE
- * constants on Sound.
- * @param {Number} [offset] Time in milliseconds into the sound to begin playback. Defaults to the current value on
- * the instance.
- * @param {Number} [loop=0] The number of times to loop the audio. Use 0 for no loops, and -1 for an infinite loop.
- * @param {Number} [volume] The volume of the sound between 0 and 1. Defaults to the current value on the instance.
- * @param {Number} [pan=instance.pan] The pan of the sound between -1 and 1. Defaults to current instance value.
+ * @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
+ * @private
* @static
*/
- s._beginPlaying = function (instance, interrupt, offset, loop, volume, pan) {
- if (!SoundChannel.add(instance, interrupt)) {
+ s._beginPlaying = function (instance, playProps) {
+ if (!SoundChannel.add(instance, playProps.interrupt)) {
return false;
}
- var result = instance._beginPlaying(offset, loop, volume, pan);
+ var result = instance._beginPlaying(playProps);
if (!result) {
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);}
@@ -1415,7 +1508,7 @@ this.createjs = this.createjs || {};
* @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
+ * @private
* @static
*/
s._getSrcById = function (value) {
@@ -1428,7 +1521,7 @@ this.createjs = this.createjs || {};
* instances themselves.
* @method _playFinished
* @param {AbstractSoundInstance} instance The instance that finished playback.
- * @protected
+ * @private
* @static
*/
s._playFinished = function (instance) {
@@ -1559,20 +1652,6 @@ this.createjs = this.createjs || {};
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
@@ -1706,8 +1785,8 @@ this.createjs = this.createjs || {};
}
// 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())) {
+ if ((interrupt == Sound.INTERRUPT_EARLY && target.position < replacement.position) ||
+ (interrupt == Sound.INTERRUPT_LATE && target.position > replacement.position)) {
replacement = target;
}
}
diff --git a/src/soundjs/cordovaaudio/CordovaAudioLoader.js b/src/soundjs/cordovaaudio/CordovaAudioLoader.js
new file mode 100644
index 00000000..d7a632a2
--- /dev/null
+++ b/src/soundjs/cordovaaudio/CordovaAudioLoader.js
@@ -0,0 +1,127 @@
+/*
+ * CordovaAudioLoader
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module SoundJS
+ */
+
+// namespace:
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * Loader provides a mechanism to preload Cordova 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.
+ * Currently files are assumed to be local and no loading actually takes place. This class exists to more easily support
+ * the existing architecture.
+ *
+ * @class CordovaAudioLoader
+ * @param {String} loadItem The item to be loaded
+ * @extends XHRRequest
+ * @protected
+ */
+ function Loader(loadItem) {
+ this.AbstractLoader_constructor(loadItem, true, createjs.Types.SOUND);
+
+ /**
+ * A Media object used to determine if src exists and to get duration
+ * @property _media
+ * @type {Media}
+ * @protected
+ */
+ this._media = null;
+
+ /**
+ * A time counter that triggers timeout if loading takes too long
+ * @property _loadTime
+ * @type {number}
+ * @protected
+ */
+ this._loadTime = 0;
+
+ /**
+ * The frequency to fire the loading timer until duration can be retrieved
+ * @property _TIMER_FREQUENCY
+ * @type {number}
+ * @protected
+ */
+ this._TIMER_FREQUENCY = 100;
+ };
+ var p = createjs.extend(Loader, createjs.AbstractLoader);
+
+
+// public methods
+ p.load = function() {
+ this._media = new Media(this._item.src, null, createjs.proxy(this._mediaErrorHandler,this));
+ this._media.seekTo(0); // needed to get duration
+
+ this._getMediaDuration();
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioLoader]";
+ };
+
+
+// private methods
+ /**
+ * Fires if audio cannot seek, indicating that src does not exist.
+ * @method _mediaErrorHandler
+ * @param error
+ * @protected
+ */
+ p._mediaErrorHandler = function(error) {
+ this._media.release();
+ this._sendError();
+ };
+
+ /**
+ * will attempt to get duration of audio until successful or time passes this._item.loadTimeout
+ * @method _getMediaDuration
+ * @protected
+ */
+ p._getMediaDuration = function() {
+ this._result = this._media.getDuration() * 1000;
+ if (this._result < 0) {
+ this._loadTime += this._TIMER_FREQUENCY;
+ if (this._loadTime > this._item.loadTimeout) {
+ this.handleEvent({type:"timeout"});
+ } else {
+ setTimeout(createjs.proxy(this._getMediaDuration, this), this._TIMER_FREQUENCY);
+ }
+ } else {
+ this._media.release();
+ this._sendComplete();
+ }
+ };
+
+ createjs.CordovaAudioLoader = createjs.promote(Loader, "AbstractLoader");
+}());
diff --git a/src/soundjs/cordovaaudio/CordovaAudioPlugin.js b/src/soundjs/cordovaaudio/CordovaAudioPlugin.js
new file mode 100644
index 00000000..d9b3f8c0
--- /dev/null
+++ b/src/soundjs/cordovaaudio/CordovaAudioPlugin.js
@@ -0,0 +1,184 @@
+/*
+ * CordovaAudioPlugin
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module SoundJS
+ */
+
+// namespace:
+this.createjs = this.createjs || {};
+
+(function () {
+
+ "use strict";
+
+ /**
+ * Play sounds using Cordova Media plugin, which will work with a Cordova app and tools that utilize Cordova such as PhoneGap or Ionic.
+ * This plugin is not used by default, and must be registered manually in {{#crossLink "Sound"}}{{/crossLink}}
+ * using the {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
+ * This plugin is recommended when building a Cordova based app, but is not required.
+ *
+ * NOTE the Cordova Media plugin is required
+ *
+ * cordova plugin add org.apache.cordova.media
+ *
+ *
Known Issues
+ * Audio Position
+ *
Audio position is calculated asynchronusly by Media. The SoundJS solution to this problem is two-fold:
+ *
Provide {{#crossLink "CordovaAudioSoundInstance/getCurrentPosition"}}{{/crossLink}} that maps directly to media.getCurrentPosition.
+ *
Provide a best guess position based on elapsed time since playback started, which is synchronized with actual position when the audio is paused or stopped.
+ * Testing showed this to be fairly reliable within 200ms.
+ *
+ *
+ * @class CordovaAudioPlugin
+ * @extends AbstractPlugin
+ * @constructor
+ */
+ function CordovaAudioPlugin() {
+ this.AbstractPlugin_constructor();
+
+ this._capabilities = s._capabilities;
+
+ this._loaderClass = createjs.CordovaAudioLoader;
+ this._soundInstanceClass = createjs.CordovaAudioSoundInstance;
+
+ this._srcDurationHash = {};
+ }
+
+ var p = createjs.extend(CordovaAudioPlugin, createjs.AbstractPlugin);
+ var s = CordovaAudioPlugin;
+
+
+// Static Properties
+ /**
+ * Sets a default playAudioWhenScreenIsLocked property for play calls on iOS devices.
+ * Individual SoundInstances can alter the default with {{#crossLink "CordovaAudioSoundInstance/playWhenScreenLocked"}}{{/crossLink}}.
+ * @property playWhenScreenLocked
+ * @type {boolean}
+ * @static
+ */
+ s.playWhenScreenLocked = false;
+
+ /**
+ * The capabilities of the plugin. This is generated via the {{#crossLink "CordovaAudioPlugin/_generateCapabilities"}}{{/crossLink}}
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for an overview of all
+ * of the available properties.
+ * @property _capabilities
+ * @type {Object}
+ * @private
+ * @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/capabilities:property"}}{{/crossLink}}
+ * method for an overview of plugin capabilities.
+ * @method _generateCapabilities
+ * @static
+ * @private
+ */
+ s._generateCapabilities = function () {
+ if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) {return;}
+
+ // OJR my best guess is that Cordova will have the same limits on playback that the audio tag has, but this could be wrong
+ 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.create = function (src, startTime, duration) {
+ var si = this.AbstractPlugin_create(src, startTime, duration);
+ si.playWhenScreenLocked = this.playWhenScreenLocked;
+ return si;
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioPlugin]";
+ };
+
+ // plugin does not support these
+ p.setVolume = p.getVolume = p.setMute = null;
+
+ /**
+ * Get the duration for a src. Intended for internal use by CordovaAudioSoundInstance.
+ * @method getSrcDuration
+ * @param src
+ * @returns {Number} The duration of the src or null if it does not exist
+ */
+ p.getSrcDuration = function(src) {
+ return this._srcDurationHash[src];
+ };
+
+// Private Methods
+ p._handlePreloadComplete = function (event) {
+ var src = event.target.getItem().src;
+ this._srcDurationHash[src] = event.result;
+ this._audioSources[src] = event.result;
+ //this.AbstractPlugin__handlePreloadComplete(event); // we don't want to do the rest of this
+ };
+
+ p.removeSound = function (src) {
+ delete(this._srcDurationHash[src]);
+ this.AbstractPlugin_removeSound(src);
+ };
+
+ createjs.CordovaAudioPlugin = createjs.promote(CordovaAudioPlugin, "AbstractPlugin");
+}());
diff --git a/src/soundjs/cordovaaudio/CordovaAudioSoundInstance.js b/src/soundjs/cordovaaudio/CordovaAudioSoundInstance.js
new file mode 100644
index 00000000..1541d059
--- /dev/null
+++ b/src/soundjs/cordovaaudio/CordovaAudioSoundInstance.js
@@ -0,0 +1,289 @@
+/*
+ * CordovaAudioSoundInstance
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module SoundJS
+ */
+
+// namespace:
+this.createjs = this.createjs || {};
+
+(function () {
+ "use strict";
+
+ /**
+ * CordovaAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
+ * {{#crossLink "CordovaAudioPlugin"}}{{/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 CordovaAudioSoundInstance
+ * @extends AbstractSoundInstance
+ * @constructor
+ */
+ function CordovaAudioSoundInstance(src, startTime, duration, playbackResource) {
+ this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
+
+// Public Properties
+ /**
+ * Sets the playAudioWhenScreenIsLocked property for play calls on iOS devices.
+ * @property playWhenScreenLocked
+ * @type {boolean}
+ */
+ this.playWhenScreenLocked = null;
+
+// Private Properties
+ /**
+ * Used to approximate the playback position by storing the number of milliseconds elapsed since
+ * 1 January 1970 00:00:00 UTC when playing
+ * Note that if js clock is out of sync with Media playback, this will become increasingly inaccurate.
+ * @property _playStartTime
+ * @type {Number}
+ * @protected
+ */
+ this._playStartTime = null;
+
+ /**
+ * A TimeOut used to trigger the end and possible loop of audio sprites.
+ * @property _audioSpriteTimeout
+ * @type {null}
+ * @protected
+ */
+ this._audioSpriteTimeout = null;
+
+ /**
+ * Boolean value that indicates if we are using an audioSprite
+ * @property _audioSprite
+ * @type {boolean}
+ * @protected
+ */
+ this._audioSprite = false;
+
+ // Proxies, make removing listeners easier.
+ 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(src, this._mediaPlayFinishedHandler, this._mediaErrorHandler, this._mediaProgressHandler);
+
+ if (duration) {
+ this._audioSprite = true;
+ } else {
+ this._setDurationFromSource();
+ }
+ }
+ var p = createjs.extend(CordovaAudioSoundInstance, createjs.AbstractSoundInstance);
+
+
+// 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.destroy = function() {
+ // pause and release the playback resource, then call parent function
+ this._playbackResource.pause();
+ this._playbackResource.release();
+ this.AbstractSoundInstance_destroy();
+ };
+
+ /**
+ * Maps to Media.getCurrentPosition,
+ * which is curiously asynchronus and requires a callback.
+ * @method getCurrentPosition
+ * @param {Method} mediaSuccess The callback that is passed the current position in seconds.
+ * @param {Method} [mediaError=null] (Optional) The callback to execute if an error occurs.
+ */
+ p.getCurrentPosition = function (mediaSuccess, mediaError) {
+ this._playbackResource.getCurrentPosition(mediaSuccess, mediaError);
+ };
+
+ p.toString = function () {
+ return "[CordovaAudioSoundInstance]";
+ };
+
+//Private Methods
+ /**
+ * media object has failed and likely will never work
+ * @method _handleMediaError
+ * @param error
+ * @private
+ */
+ p._handleMediaError = function(error) {
+ clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
+
+ this.playState = createjs.Sound.PLAY_FAILED;
+ this._sendEvent("failed");
+ };
+
+ p._handleMediaProgress = function(state) {
+ // do nothing
+ };
+
+ p._handleAudioSpriteComplete = function() {
+ this._playbackResource.pause();
+ this._handleSoundComplete();
+ };
+ /* don't need these for current looping approach
+ p._removeLooping = function() {
+ };
+
+ p._addLooping = function() {
+ };
+ */
+
+ p._handleCleanUp = function () {
+ clearTimeout(this._audioSpriteTimeout);
+ // OJR cannot use .stop as it prevents .seekTo from working
+ // todo consider media.release
+ };
+
+ p._handleSoundReady = function (event) {
+ this._playbackResource.seekTo(this._startTime + this._position);
+
+ if (this._audioSprite) {
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this._position)
+ }
+
+ this._playbackResource.play({playAudioWhenScreenIsLocked: this.playWhenScreenLocked});
+ this._playStartTime = Date.now();
+ };
+
+ p._pause = function () {
+ clearTimeout(this._audioSpriteTimeout);
+ this._playbackResource.pause();
+ if (this._playStartTime) {
+ this._position = Date.now() - this._playStartTime;
+ this._playStartTime = null;
+ }
+ this._playbackResource.getCurrentPosition(createjs.proxy(this._updatePausePos, this));
+ };
+
+ /**
+ * Synchronizes the best guess position with the actual current position.
+ * @method _updatePausePos
+ * @param {Number} pos The current position in seconds
+ * @private
+ */
+ p._updatePausePos = function (pos) {
+ this._position = pos * 1000 - this._startTime;
+ if(this._playStartTime) {
+ this._playStartTime = Date.now();
+ }
+ };
+
+ p._resume = function () {
+ if (this._audioSprite) {
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this._position)
+ }
+
+ this._playbackResource.play({playAudioWhenScreenIsLocked: this.playWhenScreenLocked});
+ this._playStartTime = Date.now();
+ };
+
+ p._handleStop = function() {
+ clearTimeout(this._audioSpriteTimeout);
+ this._playbackResource.pause(); // cannot use .stop because it prevents .seekTo from working
+ this._playbackResource.seekTo(this._startTime);
+ if (this._playStartTime) {
+ this._position = 0;
+ this._playStartTime = null;
+ }
+ };
+
+ p._updateVolume = function () {
+ var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
+ this._playbackResource.setVolume(newVolume);
+ };
+
+ p._calculateCurrentPosition = function() {
+ // return best guess position.
+ // Note if Media and js clock are out of sync, this value will become increasingly inaccurate over time
+ if (this._playStartTime) {
+ this._position = Date.now() - this._playStartTime + this._position;
+ this._playStartTime = Date.now();
+ }
+ return this._position;
+ };
+
+ p._updatePosition = function() {
+ this._playbackResource.seekTo(this._startTime + this._position);
+ this._playStartTime = Date.now();
+ if (this._audioSprite) {
+ clearTimeout(this._audioSpriteTimeout);
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this._position)
+ }
+ };
+
+ p._handleLoop = function (event) {
+ this._handleSoundReady();
+ };
+
+ p._updateStartTime = function () {
+ this._audioSprite = true;
+
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ // do nothing
+ }
+ };
+
+ p._updateDuration = function () {
+ this._audioSprite
+
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ clearTimeout(this._audioSpriteTimeout);
+ this._audioSpriteTimeout = setTimeout(this._audioSpriteEndHandler, this._duration - this.position)
+ }
+ };
+
+ p._setDurationFromSource = function () {
+ this._duration = createjs.Sound.activePlugin.getSrcDuration(this.src); // TODO find a better way to do this that does not break flow
+ };
+
+ createjs.CordovaAudioSoundInstance = createjs.promote(CordovaAudioSoundInstance, "AbstractSoundInstance");
+}());
diff --git a/src/soundjs/data/AudioSprite.js b/src/soundjs/data/AudioSprite.js
new file mode 100644
index 00000000..a060e2d0
--- /dev/null
+++ b/src/soundjs/data/AudioSprite.js
@@ -0,0 +1,67 @@
+// NOTE this is "Class" is purely to document audioSprite Setup and usage.
+/**
+ * @module SoundJS
+ */
+
+/**
+ * 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
+ */
diff --git a/src/soundjs/data/PlayPropsConfig.js b/src/soundjs/data/PlayPropsConfig.js
new file mode 100644
index 00000000..a55ef25e
--- /dev/null
+++ b/src/soundjs/data/PlayPropsConfig.js
@@ -0,0 +1,192 @@
+/*
+ * PlayPropsConfig
+ * Visit http://createjs.com/ for documentation, updates and examples.
+ *
+ *
+ * Copyright (c) 2012 gskinner.com, inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @module SoundJS
+ */
+
+// namespace:
+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 props = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
+ * createjs.Sound.play("mySound", props);
+ * // OR
+ * mySoundInstance.play(props);
+ *
+ * @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 (typeof(value) === "string") {
+ // Handle the old API gracefully.
+ console && (console.warn || console.log)("Deprecated behaviour. Sound.play takes a configuration object instead of individual arguments. See docs for info.");
+ return new createjs.PlayPropsConfig().set({interrupt:value});
+ } else if (value == null || value instanceof s || value instanceof Object) {
+ return new createjs.PlayPropsConfig().set(value);
+ } else if (value == null) {
+ throw new Error("PlayProps configuration 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) {
+ if (props != null) {
+ for (var n in props) { this[n] = props[n]; }
+ }
+ return this;
+ };
+
+ p.toString = function() {
+ return "[PlayPropsConfig]";
+ };
+
+ createjs.PlayPropsConfig = s;
+
+}());
diff --git a/src/soundjs/flashaudio/FlashAudioLoader.js b/src/soundjs/flashaudio/FlashAudioLoader.js
index 0b096df9..b2c1a4c3 100644
--- a/src/soundjs/flashaudio/FlashAudioLoader.js
+++ b/src/soundjs/flashaudio/FlashAudioLoader.js
@@ -48,7 +48,7 @@ this.createjs = this.createjs || {};
* @protected
*/
function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, false, createjs.AbstractLoader.SOUND);
+ this.AbstractLoader_constructor(loadItem, false, createjs.Types.SOUND);
// Public properties
@@ -63,17 +63,13 @@ this.createjs = this.createjs || {};
}
var p = createjs.extend(Loader, createjs.AbstractLoader);
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
// Static Properties
var s = Loader;
/**
* A reference to the Flash instance that gets created.
* @property flash
* @type {Object | Embed}
- * @protected
+ * @private
*/
s._flash = null;
@@ -81,7 +77,7 @@ this.createjs = this.createjs || {};
* A list of loader instances that tried to load before _flash was set
* @property _preloadInstances
* @type {Array}
- * @protected
+ * @private
*/
s._preloadInstances = [];
diff --git a/src/soundjs/flashaudio/FlashAudioPlugin.js b/src/soundjs/flashaudio/FlashAudioPlugin.js
index 25c7ba17..77aca3a6 100644
--- a/src/soundjs/flashaudio/FlashAudioPlugin.js
+++ b/src/soundjs/flashaudio/FlashAudioPlugin.js
@@ -38,12 +38,6 @@ this.createjs = this.createjs || {};
"use strict";
- /**
- * FlashPlugin has been renamed to {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
- * @class FlashPlugin
- * @deprecated
- */
-
/**
* Play sounds using a Flash instance. This plugin is not used by default, and must be registered manually in
* {{#crossLink "Sound"}}{{/crossLink}} using the {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method. This
@@ -153,17 +147,6 @@ this.createjs = this.createjs || {};
this._flashPreloadInstances = {};
//TODO consider combining _flashInstances and _flashPreloadInstances into a single hash
- // TODO remove _queuedInstances
- /**
- * An array of Sound Preload instances that are waiting to preload. Once Flash is initialized, the queued
- * instances are preloaded.
- * @property _queuedInstances
- * @type {Object}
- * @protected
- */
- this._queuedInstances = [];
-
-
this._capabilities = s._capabilities;
this._loaderClass = createjs.FlashAudioLoader;
@@ -193,10 +176,6 @@ this.createjs = this.createjs || {};
var p = createjs.extend(FlashAudioPlugin, createjs.AbstractPlugin);
var s = FlashAudioPlugin;
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
// Static properties
/**
* Event constant for the "registerFlashID" event for cleaner code.
@@ -204,7 +183,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default registerflashid
* @static
- * @protected
+ * @private
*/
s._REG_FLASHID = "registerflashid";
@@ -214,17 +193,17 @@ this.createjs = this.createjs || {};
* @type {String}
* @default unregisterflashid
* @static
- * @protected
+ * @private
*/
s._UNREG_FLASHID = "unregisterflashid";
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities"}}{{/crossLink}}
- * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for a list of available
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for a list of available
* capabilities.
* @property _capabilities
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
@@ -257,11 +236,11 @@ this.createjs = this.createjs || {};
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -285,25 +264,13 @@ this.createjs = this.createjs || {};
//public methods
p.register = function (src, instances) {
- if (!this.flashReady) {
- this._queuedInstances.push(src);
- }
var loader = this.AbstractPlugin_register(src, instances);
loader.addEventListener(s._REG_FLASHID, createjs.proxy(this.registerPreloadInstance, this));
loader.addEventListener(s._UNREG_FLASHID, createjs.proxy(this.unregisterPreloadInstance, this));
return loader;
};
- p.removeSound = function (src) {
- var i = createjs.indexOf(this._queuedInstances, src);
- if(i != -1) {this._queuedInstances.splice(i,1);}
- // NOTE sound cannot be removed from a swf
-
- this.AbstractPlugin_removeSound(src);
- };
-
p.removeAllSounds = function () {
- this._queuedInstances.length = 0;
this._flashInstances = {};
this._flashPreloadInstances = {};
// NOTE sound cannot be removed from a swf
@@ -345,13 +312,6 @@ this.createjs = this.createjs || {};
this._loaderClass.setFlash(this._flash);
this._soundInstanceClass.setFlash(this._flash);
-
- // Anything that needed to be preloaded, can now do so.
- for (var i = 0, l = this._queuedInstances.length; i < l; i++) {
- this._flash.register(this._queuedInstances[i]); // NOTE this flash function currently does nothing
- }
- this._queuedInstances.length = 0;
-
};
/**
@@ -496,5 +456,4 @@ this.createjs = this.createjs || {};
};
createjs.FlashAudioPlugin = createjs.promote(FlashAudioPlugin, "AbstractPlugin");
- createjs.FlashPlugin = createjs.FlashAudioPlugin; // TODO remove deprecated
}());
diff --git a/src/soundjs/flashaudio/FlashAudioPlugin.swf b/src/soundjs/flashaudio/FlashAudioPlugin.swf
index 1b85ccdd..60fbfa25 100644
Binary files a/src/soundjs/flashaudio/FlashAudioPlugin.swf and b/src/soundjs/flashaudio/FlashAudioPlugin.swf differ
diff --git a/src/soundjs/flashaudio/FlashAudioSoundInstance.js b/src/soundjs/flashaudio/FlashAudioSoundInstance.js
index 923b4aae..13bbcfb6 100644
--- a/src/soundjs/flashaudio/FlashAudioSoundInstance.js
+++ b/src/soundjs/flashaudio/FlashAudioSoundInstance.js
@@ -68,10 +68,6 @@ this.createjs = this.createjs || {};
};
var p = createjs.extend(FlashAudioSoundInstance, createjs.AbstractSoundInstance);
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
-
-
// Static Propeties
var s = FlashAudioSoundInstance;
/**
@@ -105,23 +101,20 @@ this.createjs = this.createjs || {};
// Public Methods
+ // TODO change flash.setLoop to mimic remove and add??
+ p.setLoop = function (value) {
+ if(this.flashId!= null) {
+ s._flash.setLoop(this.flashId, value);
+ }
+ this._loop = value;
+ };
+
p.toString = function () {
return "[FlashAudioSoundInstance]"
};
// Private Methods
- // TODO change flash.setLoop to mimic remove and add??
- p._removeLooping = function () {
- if (this.flashId == null) { return; }
- s._flash.setLoop(this.flashId, this._loop);
- };
-
- p._addLooping = function () {
- if (this.flashId == null) { return; }
- s._flash.setLoop(this.flashId, this._loop);
- };
-
p._updateVolume = function() {
if (this.flashId == null) { return; }
s._flash.setVolume(this.flashId, this._volume)
@@ -149,13 +142,17 @@ this.createjs = this.createjs || {};
this.flashId = null;
};
- p._beginPlaying = function (offset, loop, volume, pan) {
+ p._beginPlaying = function (playProps) {
if (s._flash == null) { return false; }
- this.setPosition(offset);
- this.setLoop(loop);
- this.setVolume(volume);
- this.setPan(pan);
+ this.position = playProps.offset;
+ this.loop = playProps.loop;
+ this.volume = playProps.volume;
+ this.pan = playProps.pan;
+ if (playProps.startTime != null) {
+ this.startTime = playProps.startTime;
+ this.duration = playProps.duration;
+ }
this._paused = false;
this.flashId = s._flash.playSound(this.src, this._position, this._loop, this._volume, this._pan, this._startTime, this._duration);
diff --git a/src/soundjs/htmlaudio/HTMLAudioPlugin.js b/src/soundjs/htmlaudio/HTMLAudioPlugin.js
index 596d924c..7dab17be 100644
--- a/src/soundjs/htmlaudio/HTMLAudioPlugin.js
+++ b/src/soundjs/htmlaudio/HTMLAudioPlugin.js
@@ -46,9 +46,8 @@ this.createjs = this.createjs || {};
*
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. This will be seen as soon as you register sounds, as
- * tags are precreated to allow Chrome to load them. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as
- * a guide to how many total audio tags you can safely use in all browsers.
+ * 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
@@ -57,7 +56,7 @@ this.createjs = this.createjs || {};
*
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 and play at once, which appears to be determined by
+ *
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 requires Quicktime to be installed for audio playback.
*
* iOS 6 limitations
- *
Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)
- *
HTML Audio is disabled by default because
- *
can only have one <audio> tag
+ *
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
*
*
@@ -95,19 +93,6 @@ this.createjs = this.createjs || {};
// Public Properties
- /**
- * The default number of instances to allow. Used by {{#crossLink "Sound"}}{{/crossLink}} when a source
- * is registered using the {{#crossLink "Sound/register"}}{{/crossLink}} method. This is only used if
- * a value is not provided.
- *
- * NOTE this property only exists as a limitation of HTML audio.
- * @property defaultNumChannels
- * @type {Number}
- * @default 2
- * @since 0.4.0
- */
- this.defaultNumChannels = 2;
-
this._capabilities = s._capabilities;
this._loaderClass = createjs.SoundLoader;
@@ -117,13 +102,9 @@ this.createjs = this.createjs || {};
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 and played. This is a browser limitation, primarily limited to IE9.
+ * 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
@@ -139,7 +120,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default canplaythrough
* @static
- * @protected
+ * @private
*/
s._AUDIO_READY = "canplaythrough";
@@ -149,7 +130,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default ended
* @static
- * @protected
+ * @private
*/
s._AUDIO_ENDED = "ended";
@@ -159,7 +140,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default seeked
* @static
- * @protected
+ * @private
*/
s._AUDIO_SEEKED = "seeked";
@@ -169,7 +150,7 @@ this.createjs = this.createjs || {};
* @type {String}
* @default stalled
* @static
- * @protected
+ * @private
*/
s._AUDIO_STALLED = "stalled";
@@ -180,35 +161,21 @@ this.createjs = this.createjs || {};
* @type {String}
* @default timeupdate
* @static
- * @protected
+ * @private
*/
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
+ * method. Please see the Sound {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
- /**
- * Deprecated now that we have audio sprite support. Audio sprites are strongly recommend on iOS for the following reasons:
- *
it 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
- *
- * @property enableIOS
- * @type {Boolean}
- * @default false
- * @deprecated
- */
- s.enableIOS = false;
-
// Static Methods
/**
@@ -220,16 +187,15 @@ this.createjs = this.createjs || {};
*/
s.isSupported = function () {
s._generateCapabilities();
- if (s._capabilities == null) {return false;}
- return true;
+ return (s._capabilities != null);
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -237,7 +203,7 @@ this.createjs = this.createjs || {};
if (t.canPlayType == null) {return null;}
s._capabilities = {
- panning:true,
+ panning:false,
volume:true,
tracks:-1
};
@@ -254,15 +220,9 @@ this.createjs = this.createjs || {};
// public methods
- p.register = function (loadItem, instances) {
- var channel = createjs.HTMLAudioTagPool.get(loadItem.src);
- var tag = null;
- for (var i = 0; i < instances; i++) {
- tag = this._createTag(loadItem.src);
- channel.add(tag);
- }
-
- var loader = this.AbstractPlugin_register(loadItem, instances);
+ p.register = function (loadItem) {
+ var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
+ var loader = this.AbstractPlugin_register(loadItem);
loader.setTag(tag);
return loader;
@@ -275,7 +235,7 @@ this.createjs = this.createjs || {};
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
- si.setPlaybackResource(null);
+ si.playbackResource = null;
return si;
};
@@ -287,23 +247,5 @@ this.createjs = this.createjs || {};
p.setVolume = p.getVolume = p.setMute = null;
-// 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
- */
- // TODO move this to tagpool when it changes to be a standard object pool
- 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.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
}());
diff --git a/src/soundjs/htmlaudio/HTMLAudioSoundInstance.js b/src/soundjs/htmlaudio/HTMLAudioSoundInstance.js
index c23522d6..79d669a0 100644
--- a/src/soundjs/htmlaudio/HTMLAudioSoundInstance.js
+++ b/src/soundjs/htmlaudio/HTMLAudioSoundInstance.js
@@ -60,7 +60,7 @@ this.createjs = this.createjs || {};
// 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._stalledHandler = createjs.proxy(this._playFailed, this);
this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
@@ -72,10 +72,6 @@ this.createjs = this.createjs || {};
}
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.
@@ -129,14 +125,14 @@ this.createjs = this.createjs || {};
tag.currentTime = this._startTime;
} catch (e) {
} // Reset Position
- createjs.HTMLAudioTagPool.setInstance(this.src, tag);
+ createjs.HTMLAudioTagPool.set(this.src, tag);
this._playbackResource = null;
}
};
- p._beginPlaying = function (offset, loop, volume, pan) {
- this._playbackResource = createjs.HTMLAudioTagPool.getInstance(this.src);
- return this.AbstractSoundInstance__beginPlaying(offset, loop, volume, pan);
+ p._beginPlaying = function (playProps) {
+ this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
+ return this.AbstractSoundInstance__beginPlaying(playProps);
};
p._handleSoundReady = function (event) {
@@ -250,8 +246,17 @@ this.createjs = this.createjs || {};
}
};
+ 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 = (startTime + duration) * 0.001;
+ 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);
@@ -259,11 +264,10 @@ this.createjs = this.createjs || {};
}
};
- /* This should never change
p._setDurationFromSource = function () {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
+ this._playbackResource = null;
};
- */
createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());
diff --git a/src/soundjs/htmlaudio/HTMLAudioTagPool.js b/src/soundjs/htmlaudio/HTMLAudioTagPool.js
index b3332c1a..fdf63dfa 100644
--- a/src/soundjs/htmlaudio/HTMLAudioTagPool.js
+++ b/src/soundjs/htmlaudio/HTMLAudioTagPool.js
@@ -34,195 +34,158 @@
// namespace:
this.createjs = this.createjs || {};
-//TODO verify that tags no longer need to be precreated (mac and pc)
-//TODO modify this now that tags do not need to be precreated
(function () {
"use strict";
/**
- * The TagPool is an object pool for HTMLAudio tag instances. In Chrome, we have to pre-create the number of HTML
- * audio tag instances that we are going to play before we load the data, otherwise the audio stalls.
- * (Note: This seems to be a bug in Chrome)
+ * HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
* @class HTMLAudioTagPool
* @param {String} src The source of the channel.
* @protected
*/
- function TagPool(src) {
-
-
-//Public Properties
- /**
- * The source of the tag pool.
- * #property src
- * @type {String}
- * @protected
- */
- this.src = src;
-
- /**
- * The total number of HTMLAudio tags in this pool. This is the maximum number of instance of a certain sound
- * that can play at one time.
- * #property length
- * @type {Number}
- * @default 0
- * @protected
- */
- this.length = 0;
-
- /**
- * The number of unused HTMLAudio tags.
- * #property available
- * @type {Number}
- * @default 0
- * @protected
- */
- this.available = 0;
-
- /**
- * A list of all available tags in the pool.
- * #property tags
- * @type {Array}
- * @protected
- */
- this.tags = [];
-
- /**
- * The duration property of all audio tags, converted to milliseconds, which originally is only available on the
- * last tag in the tags array because that is the one that is loaded.
- * #property
- * @type {Number}
- * @protected
- */
- this.duration = 0;
- };
-
- var p = TagPool.prototype;
- p.constructor = TagPool;
- var s = TagPool;
-
- // TODO: deprecated
- // p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
+ function HTMLAudioTagPool() {
+ throw "HTMLAudioTagPool cannot be instantiated";
+ }
+ var s = HTMLAudioTagPool;
// Static Properties
/**
- * A hash lookup of each sound channel, indexed by the audio source.
- * #property tags
+ * A hash lookup of each base audio tag, indexed by the audio source.
+ * @property _tags
+ * @type {{}}
* @static
- * @protected
+ * @private
*/
- s.tags = {};
-
+ s._tags = {};
-// Static Methods
/**
- * Get a tag pool. If the pool doesn't exist, create it.
- * #method get
- * @param {String} src The source file used by the audio tag.
+ * An object pool for html audio tags
+ * @property _tagPool
+ * @type {TagPool}
* @static
- * @protected
+ * @private
*/
- s.get = function (src) {
- var channel = s.tags[src];
- if (channel == null) {
- channel = s.tags[src] = new TagPool(src);
- }
- return channel;
- };
+ s._tagPool = new TagPool();
/**
- * Delete a TagPool and all related tags. Note that if the TagPool does not exist, this will fail.
- * #method remove
- * @param {String} src The source for the tag
- * @return {Boolean} If the TagPool was deleted.
+ * A hash lookup of if a base audio tag is available, indexed by the audio source
+ * @property _tagsUsed
+ * @type {{}}
+ * @private
* @static
*/
- s.remove = function (src) {
- var channel = s.tags[src];
- if (channel == null) {return false;}
- channel.removeAll();
- delete(s.tags[src]);
- return true;
- };
+ s._tagUsed = {};
+// Static Methods
/**
- * Get a tag instance. This is a shortcut method.
- * #method getInstance
- * @param {String} src The source file used by the audio tag.
- * @static
- * @protected
- */
- s.getInstance = function (src) {
- var channel = s.tags[src];
- if (channel == null) {return null;}
- return channel.get();
- };
+ * 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);
+ }
+ };
/**
- * Return a tag instance. This is a shortcut method.
- * #method setInstance
- * @param {String} src The source file used by the audio tag.
- * @param {HTMLElement} tag Audio tag to set.
+ * 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
- * @protected
*/
- s.setInstance = function (src, tag) {
- var channel = s.tags[src];
- if (channel == null) {return null;}
- return channel.set(tag);
+ 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
+ * @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 channel = s.tags[src];
- if (channel == null) {return 0;}
- return channel.getDuration();
+ var t = s._tags[src];
+ if (t == null || !t.duration) {return 0;} // OJR duration is NaN if loading has not completed
+ return t.duration * 1000;
};
+ createjs.HTMLAudioTagPool = HTMLAudioTagPool;
-// Public Methods
- /**
- * Add an HTMLAudio tag into the pool.
- * #method add
- * @param {HTMLAudioElement} tag A tag to be used for playback.
- */
- p.add = function (tag) {
- this.tags.push(tag);
- this.length++;
- this.available++;
- };
+// ************************************************************************************************************
/**
- * Remove all tags from the channel. Usually in response to a delete call.
- * #method removeAll
+ * The TagPool is an object pool for HTMLAudio tag instances.
+ * #class TagPool
+ * @param {String} src The source of the channel.
+ * @protected
*/
- p.removeAll = function () {
- var tag;
- while(this.length--) {
- tag = this.tags[this.length];
- if(tag.parentNode) {
- tag.parentNode.removeChild(tag);
- }
- delete(this.tags[this.length]); // NOTE that the audio playback is already stopped by this point
- }
- this.src = null;
- this.tags.length = 0;
+ 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 () {
- if (this.tags.length == 0) {return null;}
- this.available = this.tags.length;
- var tag = this.tags.pop();
+ 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;
};
@@ -233,25 +196,33 @@ this.createjs = this.createjs || {};
* @param {HTMLAudioElement} tag HTML audio tag
*/
p.set = function (tag) {
- var index = createjs.indexOf(this.tags, tag);
- if (index == -1) {this.tags.push(tag);}
- this.available = this.tags.length;
+ // OJR this first step seems unnecessary
+ var index = createjs.indexOf(this._tags, tag);
+ if (index == -1) {
+ this._tags.src = null;
+ this._tags.push(tag);
+ }
};
- /**
- * Gets the duration for the src audio and on first call stores it to this.duration
- * #method getDuration
- * @return {Number} Duration of the src in milliseconds
- */
- p.getDuration = function () {
- // this will work because this will be only be run the first time a sound instance is created and before any tags are taken from the pool
- if (!this.duration) {this.duration = this.tags[this.tags.length - 1].duration * 1000;}
- return this.duration;
+ p.toString = function () {
+ return "[TagPool]";
};
- p.toString = function () {
- return "[HTMLAudioTagPool]";
+
+// 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;
};
- createjs.HTMLAudioTagPool = TagPool;
}());
diff --git a/src/soundjs/version_cordovaplugin.js b/src/soundjs/version_cordovaplugin.js
new file mode 100644
index 00000000..4956e781
--- /dev/null
+++ b/src/soundjs/version_cordovaplugin.js
@@ -0,0 +1,28 @@
+/**
+ * @module SoundJS
+ */
+this.createjs = this.createjs || {};
+
+(function () {
+
+ var s = createjs.CordovaAudioPlugin = createjs.CordovaAudioPlugin || {};
+
+ /**
+ * The version string for this release.
+ * @for CordovaAudioPlugin
+ * @property version
+ * @type String
+ * @static
+ **/
+ s.version = /*=version*/""; // injected by build process
+
+ /**
+ * The build date for this release in UTC format.
+ * @for CordovaAudioPlugin
+ * @property buildDate
+ * @type String
+ * @static
+ **/
+ s.buildDate = /*=date*/""; // injected by build process
+
+})();
diff --git a/src/soundjs/version_flashplugin.js b/src/soundjs/version_flashplugin.js
index 06842418..f5381286 100644
--- a/src/soundjs/version_flashplugin.js
+++ b/src/soundjs/version_flashplugin.js
@@ -9,7 +9,7 @@ this.createjs = this.createjs || {};
/**
* The version string for this release.
- * @for FlashPlugin
+ * @for FlashAudioPlugin
* @property version
* @type String
* @static
@@ -18,7 +18,7 @@ this.createjs = this.createjs || {};
/**
* The build date for this release in UTC format.
- * @for FlashPlugin
+ * @for FlashAudioPlugin
* @property buildDate
* @type String
* @static
diff --git a/src/soundjs/webaudio/WebAudioLoader.js b/src/soundjs/webaudio/WebAudioLoader.js
index c5ef919a..df80eaa7 100644
--- a/src/soundjs/webaudio/WebAudioLoader.js
+++ b/src/soundjs/webaudio/WebAudioLoader.js
@@ -43,20 +43,15 @@ this.createjs = this.createjs || {};
*
* @class WebAudioLoader
* @param {String} loadItem The item to be loaded
- * @param {Object} flash The flash instance that will do the preloading.
* @extends XHRRequest
* @protected
*/
function Loader(loadItem) {
- this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
+ this.AbstractLoader_constructor(loadItem, true, createjs.Types.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
@@ -82,14 +77,14 @@ this.createjs = this.createjs || {};
// 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._handleError, this));
+ createjs.proxy(this._sendError, this));
};
/**
* The audio has been decoded.
* @method handleAudioDecoded
- * @param decoded
+ * @param decoded
* @protected
*/
p._handleAudioDecoded = function (decodedAudio) {
diff --git a/src/soundjs/webaudio/WebAudioPlugin.js b/src/soundjs/webaudio/WebAudioPlugin.js
index fee5605d..4c65ff4d 100644
--- a/src/soundjs/webaudio/WebAudioPlugin.js
+++ b/src/soundjs/webaudio/WebAudioPlugin.js
@@ -45,18 +45,29 @@ this.createjs = this.createjs || {};
*
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.
- *
+ *
+ * mp3 audio files do not load properly on all windows machines, reported here.
+ * For this reason it is recommended to pass another FireFox-supported type (i.e. ogg) as the default
+ * extension, until this bug is resolved
+ *
+ *
* 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.
- *
+ *
+ * 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.
+ *
+ *
+ * Sound is initially muted and will only unmute through play being called inside a user initiated event
+ * (touch/click). Please read the mobile playback notes in the the {{#crossLink "Sound"}}{{/crossLink}}
+ * class for a full overview of the limitations, and how to get around them.
+ *
+ *
+ * A bug exists that will distort un-cached 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 sample rate.
+ *
*
* @class WebAudioPlugin
* @extends AbstractPlugin
@@ -76,15 +87,6 @@ this.createjs = this.createjs || {};
*/
this._panningModel = s._panningModel;;
- /**
- * The internal master volume value of the plugin.
- * @property _volume
- * @type {Number}
- * @default 1
- * @protected
- */
- this._volume = 1;
-
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
@@ -124,10 +126,6 @@ this.createjs = this.createjs || {};
}
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;
/**
@@ -136,7 +134,7 @@ this.createjs = this.createjs || {};
* @property _capabilities
* @type {Object}
* @default null
- * @protected
+ * @private
* @static
*/
s._capabilities = null;
@@ -145,7 +143,7 @@ this.createjs = this.createjs || {};
* 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
+ * @private
* @static
*/
s._panningModel = "equalpower";
@@ -163,6 +161,38 @@ this.createjs = this.createjs || {};
*/
s.context = null;
+ /**
+ * The scratch buffer that will be assigned to the buffer property of a source node on close.
+ * Works around an iOS Safari bug: https://github.com/CreateJS/SoundJS/issues/102
+ *
+ * Advanced users can set this to an existing source node, but must do so before they call
+ * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
+ *
+ * @property _scratchBuffer
+ * @type {AudioBuffer}
+ * @private
+ * @static
+ */
+ s._scratchBuffer = null;
+
+ /**
+ * Indicated whether audio on iOS has been unlocked, which requires a touchend/mousedown event that plays an
+ * empty sound.
+ * @property _unlocked
+ * @type {boolean}
+ * @since 0.6.2
+ * @private
+ */
+ s._unlocked = false;
+
+ /**
+ * The default sample rate used when checking for iOS compatibility. See {{#crossLink "WebAudioPlugin/_createAudioContext"}}{{/crossLink}}.
+ * @property DEFAULT_SAMPLE_REATE
+ * @type {number}
+ * @default 44100
+ * @static
+ */
+ s.DEFAULT_SAMPLE_RATE = 44100;
// Static Public Methods
/**
@@ -198,8 +228,9 @@ this.createjs = this.createjs || {};
* @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.buffer = s._scratchBuffer;
source.connect(s.context.destination);
source.start(0, 0, 0);
};
@@ -211,40 +242,51 @@ this.createjs = this.createjs || {};
* @method _isFileXHRSupported
* @return {Boolean} If XHR is supported.
* @since 0.4.2
- * @protected
+ * @private
* @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;
- }
+
+ // CHANGE - Dan Zen 3/27/21
+ // The commented code was giving error locally finding test file
+ // XHR is supported as far as I can tell in all browsers now
+ // The line below will turn it off for local files as that is what the commented test seemed to do
+ // but not sure if this is needed
+ // maybe we can just set supported to true as that seems to work for sound - did not test, though
+
+ var supported = document.location.host;
+
+ // // it's much easier to detect when something goes wrong, so let's start optimistically
+ // var supported = true;
+
+ // var xhr = new XMLHttpRequest();
+ // 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.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;
+ // }
+ // try {
+ // xhr.send();
+ // } catch (error) {
+ // // catch errors in cases where the onerror is passed by
+ // supported = true;
+ // }
return supported;
};
/**
- * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
+ * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/capabilities:property"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
- * @protected
+ * @private
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
@@ -253,19 +295,22 @@ this.createjs = this.createjs || {};
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.context = s._createAudioContext();
+ if (s.context == null) { return null; }
+ }
+ if (s._scratchBuffer == null) {
+ s._scratchBuffer = s.context.createBuffer(1, 1, 22050);
}
s._compatibilitySetUp();
- // playing this inside of a touch event will enable audio on iOS, which starts muted
- s.playEmptySound();
+ // Listen for document level clicks to unlock WebAudio on iOS. See the _unlock method.
+ if ("ontouchstart" in window && s.context.state != "running") {
+ s._unlock(); // When played inside of a touch event, this will enable audio on iOS immediately.
+ document.addEventListener("mousedown", s._unlock, true);
+ document.addEventListener("touchstart", s._unlock, true);
+ document.addEventListener("touchend", s._unlock, true);
+ }
s._capabilities = {
panning:true,
@@ -289,6 +334,44 @@ this.createjs = this.createjs || {};
}
};
+ /**
+ * Create an audio context for the sound.
+ *
+ * This method handles both vendor prefixes (specifically webkit support), as well as a case on iOS where
+ * audio played with a different sample rate may play garbled when first started. The default sample rate is
+ * 44,100, however it can be changed using the {{#crossLink "WebAudioPlugin/DEFAULT_SAMPLE_RATE:property"}}{{/crossLink}}.
+ * @method _createAudioContext
+ * @return {AudioContext | webkitAudioContext}
+ * @private
+ * @static
+ * @since 1.0.0
+ */
+ s._createAudioContext = function() {
+ // Slightly modified version of https://github.com/Jam3/ios-safe-audio-context
+ // Resolves issues with first-run contexts playing garbled on iOS.
+ var AudioCtor = (window.AudioContext || window.webkitAudioContext);
+ if (AudioCtor == null) { return null; }
+ var context = new AudioCtor();
+ console.warn("The AudioContext is ready"); // Dan Zen 3/27/21
+
+ // Check if hack is necessary. Only occurs in iOS6+ devices
+ // and only when you first boot the iPhone, or play a audio/video
+ // with a different sample rate
+ if (/(iPhone|iPad)/i.test(navigator.userAgent)
+ && context.sampleRate !== s.DEFAULT_SAMPLE_RATE) {
+ var buffer = context.createBuffer(1, 1, s.DEFAULT_SAMPLE_RATE),
+ dummy = context.createBufferSource();
+ dummy.buffer = buffer;
+ dummy.connect(context.destination);
+ dummy.start(0);
+ dummy.disconnect();
+ context.close() // dispose old context
+
+ context = new AudioCtor();
+ }
+ return context;
+ }
+
/**
* Set up compatibility if only deprecated web audio calls are supported.
* See http://www.w3.org/TR/webaudio/#DeprecationNotes
@@ -297,7 +380,7 @@ this.createjs = this.createjs || {};
*
* @method _compatibilitySetUp
* @static
- * @protected
+ * @private
* @since 0.4.2
*/
s._compatibilitySetUp = function() {
@@ -317,6 +400,29 @@ this.createjs = this.createjs || {};
s._panningModel = 0;
};
+ /**
+ * Try to unlock audio on iOS. This is triggered from either WebAudio plugin setup (which will work if inside of
+ * a `mousedown` or `touchend` event stack), or the first document touchend/mousedown event. If it fails (touchend
+ * will fail if the user presses for too long, indicating a scroll event instead of a click event.
+ *
+ * Note that earlier versions of iOS supported `touchstart` for this, but iOS9 removed this functionality. Adding
+ * a `touchstart` event to support older platforms may preclude a `mousedown` even from getting fired on iOS9, so we
+ * stick with `mousedown` and `touchend`.
+ * @method _unlock
+ * @since 0.6.2
+ * @private
+ */
+ s._unlock = function() {
+ if (s._unlocked) { return; }
+ s.playEmptySound();
+ if (s.context.state == "running") {
+ document.removeEventListener("mousedown", s._unlock, true);
+ document.removeEventListener("touchend", s._unlock, true);
+ document.removeEventListener("touchstart", s._unlock, true);
+ s._unlocked = true;
+ }
+ };
+
// Public Methods
p.toString = function () {
@@ -335,6 +441,7 @@ this.createjs = this.createjs || {};
p._addPropsToClasses = function() {
var c = this._soundInstanceClass;
c.context = this.context;
+ c._scratchBuffer = s._scratchBuffer;
c.destinationNode = this.gainNode;
c._panningModel = this._panningModel;
diff --git a/src/soundjs/webaudio/WebAudioSoundInstance.js b/src/soundjs/webaudio/WebAudioSoundInstance.js
index 7a1011ac..29d30b5d 100644
--- a/src/soundjs/webaudio/WebAudioSoundInstance.js
+++ b/src/soundjs/webaudio/WebAudioSoundInstance.js
@@ -76,6 +76,7 @@ this.createjs = this.createjs || {};
this.panNode = s.context.createPanner();
this.panNode.panningModel = s._panningModel;
this.panNode.connect(this.gainNode);
+ this._updatePan();
/**
* NOTE this is only intended for use by advanced users.
@@ -129,10 +130,6 @@ this.createjs = this.createjs || {};
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}}.
@@ -143,6 +140,16 @@ this.createjs = this.createjs || {};
*/
s.context = null;
+ /**
+ * Note this is only intended for use by advanced users.
+ * The scratch buffer that will be assigned to the buffer property of a source node on close.
+ * This is and should be the same scratch buffer referenced by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
+ * @property _scratchBuffer
+ * @type {AudioBufferSourceNode}
+ * @static
+ */
+ s._scratchBuffer = null;
+
/**
* Note this is only intended for use by advanced users.
* Audio node from WebAudioPlugin that sequences to context.destination
@@ -185,11 +192,11 @@ this.createjs = this.createjs || {};
// z need to be -0.5 otherwise the sound only plays in left, right, or center
};
- p._removeLooping = function() {
+ p._removeLooping = function(value) {
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
- p._addLooping = function() {
+ p._addLooping = function(value) {
if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
};
@@ -209,7 +216,7 @@ this.createjs = this.createjs || {};
clearTimeout(this._soundCompleteTimeout);
- this._playbackStartTime = 0; // This is used by getPosition
+ this._playbackStartTime = 0; // This is used by _getPosition
};
/**
@@ -224,6 +231,11 @@ this.createjs = this.createjs || {};
if(audioNode) {
audioNode.stop(0);
audioNode.disconnect(0);
+ // necessary to prevent leak on iOS Safari 7-9. will throw in almost all other
+ // browser implementations.
+ if ( createjs.BrowserDetect.isIOS ) {
+ try { audioNode.buffer = s._scratchBuffer; } catch(e) {}
+ }
audioNode = null;
}
return audioNode;
@@ -232,8 +244,8 @@ this.createjs = this.createjs || {};
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
- var dur = this._duration * 0.001;
- var pos = this._position * 0.001;
+ var dur = this._duration * 0.001,
+ pos = Math.min(Math.max(0, this._position) * 0.001, dur);
this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
@@ -315,8 +327,10 @@ this.createjs = this.createjs || {};
};
p._updateDuration = function () {
- this._pause();
- this._resume();
+ if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
+ this._pause();
+ this._resume();
+ }
};
createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
diff --git a/tests/Gruntfile.js b/tests/Gruntfile.js
new file mode 100644
index 00000000..321455c2
--- /dev/null
+++ b/tests/Gruntfile.js
@@ -0,0 +1,67 @@
+module.exports = function (grunt) {
+ grunt.initConfig(
+ {
+ pkg: grunt.file.readJSON('package.json'),
+
+ jasmine: {
+ run: {
+ src: [
+ '../lib/soundjs-NEXT.js'
+ ],
+
+ options: {
+ specs: 'spec/*Spec.js',
+ helpers: [
+ 'spec/Helpers.js'
+ ],
+ vendor: [],
+ host: 'http://127.0.0.1:<%=connect.serve.options.port%>/'
+ }
+ }
+ },
+
+ connect: {
+ serve: {
+ options: {
+ keepalive: true,
+ base: [
+ {
+ path: __dirname,
+ options: {
+ index: '_SpecRunner.html'
+ }
+ }, '..', '../_assets/', '../lib/', './'
+ ],
+ useAvailablePort: true,
+ port: 8000,
+ open: true,
+ }
+ }
+ },
+
+ listips: {
+ run: {
+ options: {
+ label: "Normal"
+ }
+ }
+ }
+ }
+ );
+
+ grunt.registerTask('configureConnectHeadless', function () {
+ grunt.config('connect.serve.options.keepalive', false);
+ grunt.config('connect.serve.options.open', false);
+ });
+
+ // Load all the tasks we need
+ grunt.loadNpmTasks('grunt-contrib-jasmine');
+ grunt.loadNpmTasks('grunt-contrib-connect');
+ grunt.loadTasks('tasks/');
+
+ grunt.registerTask("default", "Launches browser-based tests", "serve");
+ grunt.registerTask("serve", "Launches browser-based tests", ["jasmine:run:build", "listips", "connect"]);
+
+ grunt.registerTask("headless", "phantom");
+ grunt.registerTask("phantom", "Launches phantom-based tests", ["configureConnectHeadless", "connect", "jasmine"]);
+};
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 00000000..7424d686
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,6 @@
+## Setup and run tests ##
+* Run via Grunt
+ * Install dependencies; npm install;
+ * Run tests in browser: grunt;
+ * Run headless: grunt headless;
+
diff --git a/tests/lib/jasmine-2.0.2/MIT.LICENSE b/tests/lib/jasmine-2.0.2/MIT.LICENSE
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/lib/jasmine-2.0.2/boot.js b/tests/lib/jasmine-2.0.2/boot.js
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/lib/jasmine-2.0.2/console.js b/tests/lib/jasmine-2.0.2/console.js
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/lib/jasmine-2.0.2/jasmine-html.js b/tests/lib/jasmine-2.0.2/jasmine-html.js
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/lib/jasmine-2.0.2/jasmine.css b/tests/lib/jasmine-2.0.2/jasmine.css
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/lib/jasmine-2.0.2/jasmine.js b/tests/lib/jasmine-2.0.2/jasmine.js
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/lib/jasmine-2.0.2/jasmine_favicon.png b/tests/lib/jasmine-2.0.2/jasmine_favicon.png
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/package.json b/tests/package.json
new file mode 100644
index 00000000..789263b5
--- /dev/null
+++ b/tests/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "SoundJS-UnitTests",
+ "version": "0.0.1",
+ "description": "SoundJS unit testing.",
+ "url": "http://www.createjs.com/#!/SoundJS",
+ "logo": "assets/docs-icon-SoundJS.png",
+ "devDependencies": {
+ "body-parser": "^1.9.2",
+ "grunt": "~0.4.5",
+ "grunt-contrib-connect": "^0.9.0",
+ "grunt-contrib-jasmine": "^0.8.2"
+ },
+ "engine": "node >= 0.10.22"
+}
diff --git a/tests/spec/Helpers.js b/tests/spec/Helpers.js
new file mode 100644
index 00000000..059f8147
--- /dev/null
+++ b/tests/spec/Helpers.js
@@ -0,0 +1,45 @@
+beforeEach(function () {
+ this.baseAssetsPath = "../_assets/";
+
+ this.getFilePath = function (fileObj) {
+ if (typeof fileObj == "string") {
+ return this.baseAssetsPath + fileObj;
+ } else {
+ return this.baseAssetsPath + fileObj.src;
+ }
+ }
+
+ this.findClass = function (selector) {
+ // search backwards because the last match is more likely the right one
+ for (var i = document.styleSheets.length - 1; i >= 0; i--) {
+ var cssRules = document.styleSheets[i].cssRules ||
+ document.styleSheets[i].rules || []; // IE support
+ for (var c = 0; c < cssRules.length; c++) {
+ if (cssRules[c].selectorText === selector) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ var customMatchers = {
+ toBeInRange: function (util, customEqualityTesters) {
+ return {
+ compare: function (actual, excpected, range) {
+ var result = {};
+ range = range || 0;
+
+ if (actual <= (excpected + range) && actual >= (excpected - range)) {
+ result.pass = true;
+ } else {
+ result.pass = false;
+ }
+ return result;
+ }
+ };
+ }
+ };
+
+ jasmine.addMatchers(customMatchers);
+});
diff --git a/tests/spec/SoundSpec.js b/tests/spec/SoundSpec.js
new file mode 100644
index 00000000..c4d3e21e
--- /dev/null
+++ b/tests/spec/SoundSpec.js
@@ -0,0 +1,208 @@
+describe("SoundJS", function () {
+ beforeEach(function () {
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;
+
+ this.mp3File = "audio/Thunder1.mp3";
+ this.oggFile = "audio/Thunder1.ogg";
+ this.sound = createjs.Sound;
+ });
+
+ afterEach(function () {
+ this.sound.removeAllSounds();
+ this.sound.removeAllEventListeners("fileload");
+ });
+
+ it("should play mp3s", function (done) {
+ var _this = this;
+ this.sound.registerSound(this.mp3File, "thunder");
+ this.sound.on("fileload", function (evt) {
+ expect(evt.src).toBe(_this.mp3File);
+ var s = createjs.Sound.play("thunder");
+ expect(s.playState).toBe("playSucceeded");
+ done();
+ });
+ });
+
+ it("should play oggs", function (done) {
+ var _this = this;
+ this.sound.registerSound(this.oggFile, "thunder");
+ this.sound.on("fileload", function (evt) {
+ expect(evt.src).toBe(_this.oggFile);
+ var s = createjs.Sound.play("thunder");
+ expect(s.playState).toBe("playSucceeded");
+ done();
+ });
+
+ });
+
+ it("removeSound() should work", function (done) {
+ this.sound.registerSound(this.mp3File, "thunder");
+ this.sound.on("fileload", function (evt) {
+ createjs.Sound.removeSound("thunder");
+ var s = createjs.Sound.play("thunder");
+ expect(s.playState).toBe("playFailed");
+ done();
+ });
+
+ });
+
+ it("removeAllSounds() should work", function (done) {
+ this.sound.registerSound(this.mp3File, "thunder");
+ this.sound.on("fileload", function (evt) {
+ createjs.Sound.removeAllSounds();
+ var s = createjs.Sound.play("thunder");
+ expect(s.playState).toBe("playFailed");
+ done();
+ });
+
+ });
+
+ describe("Capabilities", function () {
+ beforeEach(function () {
+ this.capabilities = this.sound.getCapabilities();
+ this.availableCapabilities = ["panning", "volume", "tracks", "mp3", "ogg", "wav", "mpeg", "m4a", "mp4", "aiff", "wma", "mid"];
+ });
+
+ it("getCapabilities() should contain the correct properties.", function () {
+ var containsAll = true;
+ var _this = this;
+ this.availableCapabilities.forEach(function (item, index, arr) {
+ if (!(item in _this.capabilities)) {
+ containsAll = false;
+ }
+ });
+
+ expect(containsAll).toBe(true);
+ });
+
+ it("getCapability() should match getCapabilities().", function () {
+ for (var n in this.capabilities) {
+ expect(this.capabilities[n]).toBe(this.sound.getCapability(n));
+ }
+ });
+ });
+
+ it("setMute() should work.", function () {
+ this.sound.setMute(true);
+ expect(this.sound.getMute()).toBe(true);
+ });
+
+ it("setVolume() should work.", function () {
+ this.sound.setVolume(.5);
+ expect(this.sound.getVolume()).toBe(.5);
+ });
+
+ it("initializeDefaultPlugins() should work", function () {
+ expect(this.sound.initializeDefaultPlugins()).toBe(true);
+ });
+
+ it("isReady() should work", function () {
+ expect(this.sound.isReady()).toBe(true);
+ });
+
+ it("loadComplete() should be true", function (done) {
+ this.sound.registerSound(this.mp3File, "thunder");
+ this.sound.on("fileload", function (evt) {
+ expect(createjs.Sound.loadComplete("thunder")).toBe(true);
+ createjs.Sound.removeAllSounds();
+ expect(createjs.Sound.loadComplete("thunder")).toBe(false);
+ done();
+ });
+ });
+
+ it("loadComplete() should be false", function (done) {
+ this.sound.registerSound(this.mp3File, "thunder");
+ this.sound.on("fileload", function (evt) {
+ createjs.Sound.removeAllSounds();
+ expect(createjs.Sound.loadComplete("thunder")).toBe(false);
+ done();
+ });
+ });
+
+ it("registerSounds() should work", function (done) {
+ var sounds = [
+ {src: "Game-Shot.mp3", id: "shot"},
+ {src: "Game-Spawn.mp3", id: "spawn"},
+ {src: "Humm.mp3", id: "humm"}
+ ];
+ createjs.Sound.registerSounds(sounds, "audio/");
+
+ var loadCount = 0;
+
+ this.sound.on("fileload", function (evt) {
+ if (++loadCount == sounds.length) {
+ for (var i = 0; i < sounds.length; i++) {
+ var s = createjs.Sound.play(sounds[i].id);
+ expect(s.playState).toBe("playSucceeded");
+ }
+ done();
+ }
+ });
+ });
+
+ it("defaultInterruptBehavior should be INTERRUPT_NONE", function () {
+ expect(this.sound.defaultInterruptBehavior).toBe(this.sound.INTERRUPT_NONE);
+ });
+
+ it("EXTENSION_MAP should contain mp4", function () {
+ expect(this.sound.EXTENSION_MAP.m4a).toBe("mp4");
+ });
+
+ it("Default constants should be be correct", function () {
+ var defaults = {
+ INTERRUPT_ANY: "any",
+ INTERRUPT_EARLY: "early",
+ INTERRUPT_LATE: "late",
+ INTERRUPT_NONE: "none",
+ PLAY_FAILED: "playFailed",
+ PLAY_FINISHED: "playFinished",
+ PLAY_INITED: "playInited",
+ PLAY_INTERRUPTED: "playInterrupted",
+ PLAY_SUCCEEDED: "playSucceeded"
+ };
+
+ for (var n in defaults) {
+ expect(this.sound[n]).toBe(defaults[n]);
+ }
+ });
+
+ it("Default SUPPORTED_EXTENSIONS should match defaults", function () {
+ var correctCount = 0;
+ var defaults = ["mp3", "ogg", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
+ for (var i = 0; i < this.sound.SUPPORTED_EXTENSIONS.length; i++) {
+ if (defaults.indexOf(this.sound.SUPPORTED_EXTENSIONS[i]) > -1) {
+ correctCount++;
+ }
+ }
+
+ expect(correctCount).toBe(defaults.length);
+ });
+
+ it("stop() should stop all playing sounds.", function (done) {
+ var sounds = [
+ {src: "Game-Shot.mp3", id: "shot"},
+ {src: "Game-Spawn.mp3", id: "spawn"},
+ {src: "Humm.mp3", id: "humm"}
+ ];
+ createjs.Sound.registerSounds(sounds, "audio/");
+
+ var loadCount = 0;
+ var i;
+
+ this.sound.on("fileload", function (evt) {
+ if (++loadCount == sounds.length) {
+ var playingSounds = [];
+ for (i = 0; i < sounds.length; i++) {
+ var s = createjs.Sound.play(sounds[i].id);
+ playingSounds.push(s);
+ }
+
+ createjs.Sound.stop();
+ for (i = 0; i < playingSounds.length; i++) {
+ expect(playingSounds[i].playState).toBe("playFinished");
+ }
+ done();
+ }
+ });
+ });
+});
diff --git a/tests/tasks/findopenport.js b/tests/tasks/findopenport.js
new file mode 100644
index 00000000..521c766d
--- /dev/null
+++ b/tests/tasks/findopenport.js
@@ -0,0 +1,74 @@
+/*
+* Copyright (c) 2014 gskinner.com, inc.
+*
+* Permission is hereby granted, free of charge, to any person
+* obtaining a copy of this software and associated documentation
+* files (the "Software"), to deal in the Software without
+* restriction, including without limitation the rights to use,
+* copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the
+* Software is furnished to do so, subject to the following
+* conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+* OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+module.exports = function (grunt) {
+ var net = require('net');
+ var _callback;
+ var _ports;
+ var _opts;
+ var _done;
+
+ grunt.registerMultiTask('findopenport', 'Prints a list of active ips.', function() {
+ _opts = this.options();
+
+ _done = this.async();
+ _ports = _opts['ports'] || [80, 8888, 9000, 9999, 9001];
+ checkNext();
+ });
+
+ function checkNext() {
+ if (!_ports.length) {
+ grunt.option(_portName, -1);
+ _done();
+ return;
+ }
+
+ check(_ports.shift(), function(success, port) {
+ if (!success) {
+ checkNext();
+ } else {
+ //grunt.option(_portName, port);
+ var configNames = Array.isArray(_opts.configName)?_opts.configName:[_opts.configName];
+
+ configNames.forEach(function(item) {
+ grunt.config.set(item, port);
+ });
+ _done();
+ }
+ });
+ }
+
+ function check(port, callback) {
+ var server = net.createServer();
+ server.on('error', function(e) {
+ callback(false, port);
+ });
+
+ server.listen(port, function() {
+ callback(true, port);
+ server.close();
+ });
+ }
+};
diff --git a/src/createjs/utils/definePropertySupported.js b/tests/tasks/listips.js
similarity index 54%
rename from src/createjs/utils/definePropertySupported.js
rename to tests/tasks/listips.js
index 04bb0cd8..7f9eb45a 100644
--- a/src/createjs/utils/definePropertySupported.js
+++ b/tests/tasks/listips.js
@@ -1,8 +1,5 @@
/*
-* defineProperty
-* Visit http://createjs.com/ for documentation, updates and examples.
-*
-* Copyright (c) 2010 gskinner.com, inc.
+* Copyright (c) 2014 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -26,46 +23,33 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
-/**
- * @module CreateJS
- */
+module.exports = function (grunt) {
+ var os = require('os');
-// namespace:
-this.createjs = this.createjs||{};
+ grunt.registerMultiTask('listips', 'Prints a list of active ips.', function() {
+ var opts = this.options({"port": 80});
-/**
- * @class Utility Methods
- */
-(function() {
- "use strict";
+ var port = opts.port;
+ var label = opts.label?'('+opts.label+') ':'';
- /**
- * Boolean value indicating if Object.defineProperty is supported.
- *
- *
Example
- *
- * if (createjs.definePropertySupported) { // add getter / setter}
- *
- * @property definePropertySupported
- * @type {Boolean}
- * @default true
- */
- var t = Object.defineProperty ? true : false;
+ if (port == 80) {
+ port = '';
+ } else {
+ port = ':'+port;
+ }
- // IE8 has Object.defineProperty, but only for DOM objects, so check if fails to suppress errors
- var foo = {};
- try {
- Object.defineProperty(foo, "bar", {
- get: function () {
- return this._bar;
- },
- set: function (value) {
- this._bar = value;
+ var interfaces = os.networkInterfaces();
+ var addresses = [];
+ for (var n in interfaces) {
+ for (var n2 in interfaces[n]) {
+ var address = interfaces[n][n2];
+ if (address.family == 'IPv4' && !address.internal) {
+ addresses.push('http://'+address.address+port);
+ }
}
- });
- } catch (e) {
- t = false;
- }
+ }
- createjs.definePropertySupported = t;
-}());
+ addresses.push('http://localhost'+port);
+ grunt.log.subhead('\n'+label+'Listening on:\n\t', addresses.join('\n\t '));
+ });
+}
diff --git a/tutorials/Basics and Best Practices/sample.html b/tutorials/Basics and Best Practices/sample.html
index a32e05c6..a2e3408f 100644
--- a/tutorials/Basics and Best Practices/sample.html
+++ b/tutorials/Basics and Best Practices/sample.html
@@ -4,7 +4,7 @@
-
+
+
+
-
+
+