scratch

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 72  →  ?path2? @ 73
/bower_components/SoundJS/.bower.json
@@ -0,0 +1,48 @@
{
"name": "SoundJS",
"version": "0.6.2",
"homepage": "https://github.com/CreateJS/SoundJS",
"authors": [
"lannymcnie",
"OJayRobinson",
"gskinner",
"wdamien"
],
"description": "A Javascript library for working with Audio. Features a simple interface as the front end to multiple audio APIs via a plugin model. Currently supports WebAudio, HTML5 Audio, and a Flash fallback. Part of the CreateJS suite of libraries.",
"main": "lib/soundjs-0.6.2.combined.js",
"keywords": [
"sound",
"audio",
"webaudio",
"html5",
"createjs"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"_assets",
"bower_components",
".bower.json",
"build",
"dev",
"docs",
"examples",
"icon.png",
"LICENSE.txt",
"README.md",
"src",
"tutorials",
"VERSIONS.txt"
],
"_release": "0.6.2",
"_resolution": {
"type": "version",
"tag": "0.6.2",
"commit": "0750aad3fc07a3a50ec4a4be99537750a1787191"
},
"_source": "https://github.com/CreateJS/SoundJS.git",
"_target": "^0.6.2",
"_originalSource": "SoundJS",
"_direct": true
}
/bower_components/SoundJS/README_CREATEJS_NAMESPACE.txt
@@ -0,0 +1,24 @@
In this version of SoundJS, class definitions reside in a "createjs" namespace by default.
 
For example, instead of playing a sound like this:
var foo = SoundJS.play(id);
 
You will need to reach into the createjs namespace:
var bar = createjs.SoundJS.play(id);
 
This functionality is configurable though. You can easily shortcut the namespace or get rid of it completely.
 
To shortcut the namespace, just point a different variable at createjs it is loaded:
<script src="easeljs.js"></script>
<script>
var c = createjs; // creates a reference to the createjs namespace in "c"
var foo = new c.Shape();
</script>
 
To remove the namespace, just point the createjs variable at the window before loading the libraries:
<script>
var createjs = window; // sets window as the createjs namespace (the object the classes will be defined in)
</script>
<script src="easeljs.js"></script>
 
This will also make CreateJS libraries compatible with old content that did not use a namespace, such as the output from the Flash Pro Toolkit for CreateJS v1.0.
/bower_components/SoundJS/bower.json
@@ -0,0 +1,38 @@
{
"name": "SoundJS",
"version": "0.6.2",
"homepage": "https://github.com/CreateJS/SoundJS",
"authors": [
"lannymcnie",
"OJayRobinson",
"gskinner",
"wdamien"
],
"description": "A Javascript library for working with Audio. Features a simple interface as the front end to multiple audio APIs via a plugin model. Currently supports WebAudio, HTML5 Audio, and a Flash fallback. Part of the CreateJS suite of libraries.",
"main": "lib/soundjs-0.6.2.combined.js",
"keywords": [
"sound",
"audio",
"webaudio",
"html5",
"createjs"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"_assets",
"bower_components",
".bower.json",
"build",
"dev",
"docs",
"examples",
"icon.png",
"LICENSE.txt",
"README.md",
"src",
"tutorials",
"VERSIONS.txt"
]
}
/bower_components/SoundJS/lib/README.txt
@@ -0,0 +1,24 @@
# lib directory
This directory contains a compressed version of the SoundJS library, including the most recent tagged release and the
in-progress NEXT release.
 
Both combined and minified versions of the library are included. The former being useful for debugging, and the latter
for deployment.
 
You can also link to the libraries on the [CreateJS CDN](http://code.createjs.com/), to benefit from faster load times
and shared caching across sites.
 
# libraries
**soundjs-VERSION.min.js** contains minified versions of all of the SoundJS classes (comments and white space stripped)
except for the Flash and Cordova audio plugins.
**flashaudioplugin-VERSION.min.js** contains minified versions of the Flash audio plugin.
**cordovaaudioplugin-VERSION.min.js** contains minified versions of the Cordova audio plugin.
**soundjs-VERSION.combined.js** contains all the SoundJS classes (except Flash and Cordova plugin classes) including
whitespace and comments.
**flashaudioplugin-VERSION.combined.js** contains all the Flash audio plugin classes.
**cordovaaudioplugin-VERSION.combined.js** contains all the Cordova audio plugin classes.
 
 
# license
The libraries are ©2010 gskinner.com, inc., and made available under the highly permissive MIT open source software
license. See the source file header for the full license text.
/bower_components/SoundJS/lib/cordovaaudioplugin-0.6.2.combined.js
@@ -0,0 +1,570 @@
/*!
* 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.AbstractLoader.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() {
// call parent function, then release
this.AbstractSoundInstance_destroy();
this._playbackResource.release();
};
 
/**
* Maps to <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Media.getCurrentPosition</a>,
* 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);
this._playbackResource.pause(); // 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.
*
* <b>NOTE the <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Cordova Media plugin</a> is required</b>
*
* cordova plugin add org.apache.cordova.media
*
* <h4>Known Issues</h4>
* <b>Audio Position</b>
* <ul>Audio position is calculated asynchronusly by Media. The SoundJS solution to this problem is two-fold:
* <li>Provide {{#crossLink "CordovaAudioSoundInstance/getCurrentPosition"}}{{/crossLink}} that maps directly to media.getCurrentPosition.</li>
* <li>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.</li></ul>
* <b>Cordova Media Docs</b>
* <ul><li>See the <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Cordova Media Docs</a> for various known OS issues.</li></ul>
* <br />
*
* @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/getCapabilities"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
* @protected
* @static
*/
s._capabilities = null;
 
 
// Static Methods
/**
* Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
* browsers, but is disabled in iOS because of its limitations.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
s._generateCapabilities();
return (s._capabilities != null);
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null || !(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*/"0.6.2"; // injected by build process
 
/**
* The build date for this release in UTC format.
* @for CordovaAudioPlugin
* @property buildDate
* @type String
* @static
**/
s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:31 GMT"; // injected by build process
 
})();
/bower_components/SoundJS/lib/cordovaaudioplugin-0.6.2.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.AbstractLoader.SOUND),this._media=null,this._loadTime=0,this._TIMER_FREQUENCY=100}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._media=new Media(this._item.src,null,createjs.proxy(this._mediaErrorHandler,this)),this._media.seekTo(0),this._getMediaDuration()},b.toString=function(){return"[CordovaAudioLoader]"},b._mediaErrorHandler=function(){this._media.release(),this._sendError()},b._getMediaDuration=function(){this._result=1e3*this._media.getDuration(),this._result<0?(this._loadTime+=this._TIMER_FREQUENCY,this._loadTime>this._item.loadTimeout?this.handleEvent({type:"timeout"}):setTimeout(createjs.proxy(this._getMediaDuration,this),this._TIMER_FREQUENCY)):(this._media.release(),this._sendComplete())},createjs.CordovaAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function CordovaAudioSoundInstance(a,b,c,d){this.AbstractSoundInstance_constructor(a,b,c,d),this.playWhenScreenLocked=null,this._playStartTime=null,this._audioSpriteTimeout=null,this._audioSprite=!1,this._audioSpriteEndHandler=createjs.proxy(this._handleAudioSpriteComplete,this),this._mediaPlayFinishedHandler=createjs.proxy(this._handleSoundComplete,this),this._mediaErrorHandler=createjs.proxy(this._handleMediaError,this),this._mediaProgressHandler=createjs.proxy(this._handleMediaProgress,this),this._playbackResource=new Media(a,this._mediaPlayFinishedHandler,this._mediaErrorHandler,this._mediaProgressHandler),c?this._audioSprite=!0:this._setDurationFromSource()}var a=createjs.extend(CordovaAudioSoundInstance,createjs.AbstractSoundInstance);a.setMasterVolume=function(){this._updateVolume()},a.setMasterMute=function(){this._updateVolume()},a.destroy=function(){this.AbstractSoundInstance_destroy(),this._playbackResource.release()},a.getCurrentPosition=function(a,b){this._playbackResource.getCurrentPosition(a,b)},a.toString=function(){return"[CordovaAudioSoundInstance]"},a._handleMediaError=function(){clearTimeout(this.delayTimeoutId),this.playState=createjs.Sound.PLAY_FAILED,this._sendEvent("failed")},a._handleMediaProgress=function(){},a._handleAudioSpriteComplete=function(){this._playbackResource.pause(),this._handleSoundComplete()},a._handleCleanUp=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause()},a._handleSoundReady=function(){this._playbackResource.seekTo(this._startTime+this._position),this._audioSprite&&(this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position)),this._playbackResource.play({playAudioWhenScreenIsLocked:this.playWhenScreenLocked}),this._playStartTime=Date.now()},a._pause=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause(),this._playStartTime&&(this._position=Date.now()-this._playStartTime,this._playStartTime=null),this._playbackResource.getCurrentPosition(createjs.proxy(this._updatePausePos,this))},a._updatePausePos=function(a){this._position=1e3*a-this._startTime,this._playStartTime&&(this._playStartTime=Date.now())},a._resume=function(){this._audioSprite&&(this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position)),this._playbackResource.play({playAudioWhenScreenIsLocked:this.playWhenScreenLocked}),this._playStartTime=Date.now()},a._handleStop=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause(),this._playbackResource.seekTo(this._startTime),this._playStartTime&&(this._position=0,this._playStartTime=null)},a._updateVolume=function(){var a=this._muted||createjs.Sound._masterMute?0:this._volume*createjs.Sound._masterVolume;this._playbackResource.setVolume(a)},a._calculateCurrentPosition=function(){return this._playStartTime&&(this._position=Date.now()-this._playStartTime+this._position,this._playStartTime=Date.now()),this._position},a._updatePosition=function(){this._playbackResource.seekTo(this._startTime+this._position),this._playStartTime=Date.now(),this._audioSprite&&(clearTimeout(this._audioSpriteTimeout),this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this._position))},a._handleLoop=function(){this._handleSoundReady()},a._updateStartTime=function(){this._audioSprite=!0,this.playState==createjs.Sound.PLAY_SUCCEEDED},a._updateDuration=function(){this._audioSprite,this.playState==createjs.Sound.PLAY_SUCCEEDED&&(clearTimeout(this._audioSpriteTimeout),this._audioSpriteTimeout=setTimeout(this._audioSpriteEndHandler,this._duration-this.position))},a._setDurationFromSource=function(){this._duration=createjs.Sound.activePlugin.getSrcDuration(this.src)},createjs.CordovaAudioSoundInstance=createjs.promote(CordovaAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function CordovaAudioPlugin(){this.AbstractPlugin_constructor(),this._capabilities=b._capabilities,this._loaderClass=createjs.CordovaAudioLoader,this._soundInstanceClass=createjs.CordovaAudioSoundInstance,this._srcDurationHash={}}var a=createjs.extend(CordovaAudioPlugin,createjs.AbstractPlugin),b=CordovaAudioPlugin;b.playWhenScreenLocked=!1,b._capabilities=null,b.isSupported=function(){return b._generateCapabilities(),null!=b._capabilities},b._generateCapabilities=function(){if(null==b._capabilities&&(window.cordova||window.PhoneGap||window.phonegap)&&window.Media){var a=document.createElement("audio");if(null==a.canPlayType)return null;b._capabilities={panning:!1,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},a.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.playWhenScreenLocked=this.playWhenScreenLocked,d},a.toString=function(){return"[CordovaAudioPlugin]"},a.setVolume=a.getVolume=a.setMute=null,a.getSrcDuration=function(a){return this._srcDurationHash[a]},a._handlePreloadComplete=function(a){var b=a.target.getItem().src;this._srcDurationHash[b]=a.result,this._audioSources[b]=a.result},a.removeSound=function(a){delete this._srcDurationHash[a],this.AbstractPlugin_removeSound(a)},createjs.CordovaAudioPlugin=createjs.promote(CordovaAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){var a=createjs.CordovaAudioPlugin=createjs.CordovaAudioPlugin||{};a.version="0.6.2",a.buildDate="Thu, 26 Nov 2015 20:44:31 GMT"}();
/bower_components/SoundJS/lib/cordovaaudioplugin-NEXT.combined.js
@@ -0,0 +1,570 @@
/*!
* 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.AbstractLoader.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() {
// call parent function, then release
this.AbstractSoundInstance_destroy();
this._playbackResource.release();
};
 
/**
* Maps to <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Media.getCurrentPosition</a>,
* 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);
this._playbackResource.pause(); // 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.
*
* <b>NOTE the <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Cordova Media plugin</a> is required</b>
*
* cordova plugin add org.apache.cordova.media
*
* <h4>Known Issues</h4>
* <b>Audio Position</b>
* <ul>Audio position is calculated asynchronusly by Media. The SoundJS solution to this problem is two-fold:
* <li>Provide {{#crossLink "CordovaAudioSoundInstance/getCurrentPosition"}}{{/crossLink}} that maps directly to media.getCurrentPosition.</li>
* <li>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.</li></ul>
* <b>Cordova Media Docs</b>
* <ul><li>See the <a href="http://plugins.cordova.io/#/package/org.apache.cordova.media" target="_blank">Cordova Media Docs</a> for various known OS issues.</li></ul>
* <br />
*
* @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/getCapabilities"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
* @protected
* @static
*/
s._capabilities = null;
 
 
// Static Methods
/**
* Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
* browsers, but is disabled in iOS because of its limitations.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
s._generateCapabilities();
return (s._capabilities != null);
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null || !(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*/"Mon, 14 Sep 2015 19:11:47 GMT"; // injected by build process
 
})();
/bower_components/SoundJS/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.AbstractLoader.SOUND),this._media=null,this._loadTime=0,this._TIMER_FREQUENCY=100}var b=createjs.extend(a,createjs.AbstractLoader);b.load=function(){this._media=new Media(this._item.src,null,createjs.proxy(this._mediaErrorHandler,this)),this._media.seekTo(0),this._getMediaDuration()},b.toString=function(){return"[CordovaAudioLoader]"},b._mediaErrorHandler=function(a){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(a){this._updateVolume()},a.setMasterMute=function(a){this._updateVolume()},a.destroy=function(){this.AbstractSoundInstance_destroy(),this._playbackResource.release()},a.getCurrentPosition=function(a,b){this._playbackResource.getCurrentPosition(a,b)},a.toString=function(){return"[CordovaAudioSoundInstance]"},a._handleMediaError=function(a){clearTimeout(this.delayTimeoutId),this.playState=createjs.Sound.PLAY_FAILED,this._sendEvent("failed")},a._handleMediaProgress=function(a){},a._handleAudioSpriteComplete=function(){this._playbackResource.pause(),this._handleSoundComplete()},a._handleCleanUp=function(){clearTimeout(this._audioSpriteTimeout),this._playbackResource.pause()},a._handleSoundReady=function(a){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(a){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="Mon, 14 Sep 2015 19:11:47 GMT"}();
/bower_components/SoundJS/lib/flashaudioplugin-0.6.2.combined.js
@@ -0,0 +1,1604 @@
/*!
* 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.
*/
 
 
//##############################################################################
// swfobject.js
//##############################################################################
 
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
 
var swfobject = function() {
var UNDEF = "undefined",
OBJECT = "object",
SHOCKWAVE_FLASH = "Shockwave Flash",
SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
FLASH_MIME_TYPE = "application/x-shockwave-flash",
EXPRESS_INSTALL_ID = "SWFObjectExprInst",
ON_READY_STATE_CHANGE = "onreadystatechange",
win = window,
doc = document,
nav = navigator,
plugin = false,
domLoadFnArr = [main],
regObjArr = [],
objIdArr = [],
listenersArr = [],
storedAltContent,
storedAltContentId,
storedCallbackFn,
storedCallbackObj,
isDomLoaded = false,
isExpressInstallActive = false,
dynamicStylesheet,
dynamicStylesheetMedia,
autoHideShow = true,
/* Centralized function for browser feature detection
- User agent string detection is only used when no good alternative is possible
- Is executed directly for optimal performance
*/
ua = function() {
var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
u = nav.userAgent.toLowerCase(),
p = nav.platform.toLowerCase(),
windows = p ? /win/.test(p) : /win/.test(u),
mac = p ? /mac/.test(p) : /mac/.test(u),
webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
playerVersion = [0,0,0],
d = null;
if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
d = nav.plugins[SHOCKWAVE_FLASH].description;
if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
plugin = true;
ie = false; // cascaded feature detection for Internet Explorer
d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
}
}
else if (typeof win.ActiveXObject != UNDEF) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if (a) { // a will return null when ActiveX is disabled
d = a.GetVariable("$version");
if (d) {
ie = true; // cascaded feature detection for Internet Explorer
d = d.split(" ")[1].split(",");
playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
}(),
/* Cross-browser onDomLoad
- Will fire an event as soon as the DOM of a web page is loaded
- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
- Regular onload serves as fallback
*/
onDomLoad = function() {
if (!ua.w3) { return; }
if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
callDomLoadFunctions();
}
if (!isDomLoaded) {
if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
}
if (ua.ie && ua.win) {
doc.attachEvent(ON_READY_STATE_CHANGE, function() {
if (doc.readyState == "complete") {
doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
callDomLoadFunctions();
}
});
if (win == top) { // if not inside an iframe
(function(){
if (isDomLoaded) { return; }
try {
doc.documentElement.doScroll("left");
}
catch(e) {
setTimeout(arguments.callee, 0);
return;
}
callDomLoadFunctions();
})();
}
}
if (ua.wk) {
(function(){
if (isDomLoaded) { return; }
if (!/loaded|complete/.test(doc.readyState)) {
setTimeout(arguments.callee, 0);
return;
}
callDomLoadFunctions();
})();
}
addLoadEvent(callDomLoadFunctions);
}
}();
function callDomLoadFunctions() {
if (isDomLoaded) { return; }
try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
t.parentNode.removeChild(t);
}
catch (e) { return; }
isDomLoaded = true;
var dl = domLoadFnArr.length;
for (var i = 0; i < dl; i++) {
domLoadFnArr[i]();
}
}
function addDomLoadEvent(fn) {
if (isDomLoaded) {
fn();
}
else {
domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
}
}
/* Cross-browser onload
- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
- Will fire an event as soon as a web page including all of its assets are loaded
*/
function addLoadEvent(fn) {
if (typeof win.addEventListener != UNDEF) {
win.addEventListener("load", fn, false);
}
else if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("load", fn, false);
}
else if (typeof win.attachEvent != UNDEF) {
addListener(win, "onload", fn);
}
else if (typeof win.onload == "function") {
var fnOld = win.onload;
win.onload = function() {
fnOld();
fn();
};
}
else {
win.onload = fn;
}
}
/* Main function
- Will preferably execute onDomLoad, otherwise onload (as a fallback)
*/
function main() {
if (plugin) {
testPlayerVersion();
}
else {
matchVersions();
}
}
/* Detect the Flash Player version for non-Internet Explorer browsers
- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
a. Both release and build numbers can be detected
b. Avoid wrong descriptions by corrupt installers provided by Adobe
c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
*/
function testPlayerVersion() {
var b = doc.getElementsByTagName("body")[0];
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
var t = b.appendChild(o);
if (t) {
var counter = 0;
(function(){
if (typeof t.GetVariable != UNDEF) {
var d = t.GetVariable("$version");
if (d) {
d = d.split(" ")[1].split(",");
ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
else if (counter < 10) {
counter++;
setTimeout(arguments.callee, 10);
return;
}
b.removeChild(o);
t = null;
matchVersions();
})();
}
else {
matchVersions();
}
}
/* Perform Flash Player and SWF version matching; static publishing only
*/
function matchVersions() {
var rl = regObjArr.length;
if (rl > 0) {
for (var i = 0; i < rl; i++) { // for each registered object element
var id = regObjArr[i].id;
var cb = regObjArr[i].callbackFn;
var cbObj = {success:false, id:id};
if (ua.pv[0] > 0) {
var obj = getElementById(id);
if (obj) {
if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
setVisibility(id, true);
if (cb) {
cbObj.success = true;
cbObj.ref = getObjectById(id);
cb(cbObj);
}
}
else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
var att = {};
att.data = regObjArr[i].expressInstall;
att.width = obj.getAttribute("width") || "0";
att.height = obj.getAttribute("height") || "0";
if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
// parse HTML object param element's name-value pairs
var par = {};
var p = obj.getElementsByTagName("param");
var pl = p.length;
for (var j = 0; j < pl; j++) {
if (p[j].getAttribute("name").toLowerCase() != "movie") {
par[p[j].getAttribute("name")] = p[j].getAttribute("value");
}
}
showExpressInstall(att, par, id, cb);
}
else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
displayAltContent(obj);
if (cb) { cb(cbObj); }
}
}
}
else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
setVisibility(id, true);
if (cb) {
var o = getObjectById(id); // test whether there is an HTML object element or not
if (o && typeof o.SetVariable != UNDEF) {
cbObj.success = true;
cbObj.ref = o;
}
cb(cbObj);
}
}
}
}
}
function getObjectById(objectIdStr) {
var r = null;
var o = getElementById(objectIdStr);
if (o && o.nodeName == "OBJECT") {
if (typeof o.SetVariable != UNDEF) {
r = o;
}
else {
var n = o.getElementsByTagName(OBJECT)[0];
if (n) {
r = n;
}
}
}
return r;
}
/* Requirements for Adobe Express Install
- only one instance can be active at a time
- fp 6.0.65 or higher
- Win/Mac OS only
- no Webkit engines older than version 312
*/
function canExpressInstall() {
return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
}
/* Show the Adobe Express Install dialog
- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
*/
function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
isExpressInstallActive = true;
storedCallbackFn = callbackFn || null;
storedCallbackObj = {success:false, id:replaceElemIdStr};
var obj = getElementById(replaceElemIdStr);
if (obj) {
if (obj.nodeName == "OBJECT") { // static publishing
storedAltContent = abstractAltContent(obj);
storedAltContentId = null;
}
else { // dynamic publishing
storedAltContent = obj;
storedAltContentId = replaceElemIdStr;
}
att.id = EXPRESS_INSTALL_ID;
if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + fv;
}
else {
par.flashvars = fv;
}
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
if (ua.ie && ua.win && obj.readyState != 4) {
var newObj = createElement("div");
replaceElemIdStr += "SWFObjectNew";
newObj.setAttribute("id", replaceElemIdStr);
obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
createSWF(att, par, replaceElemIdStr);
}
}
/* Functions to abstract and display alternative content
*/
function displayAltContent(obj) {
if (ua.ie && ua.win && obj.readyState != 4) {
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
var el = createElement("div");
obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
el.parentNode.replaceChild(abstractAltContent(obj), el);
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
else {
obj.parentNode.replaceChild(abstractAltContent(obj), obj);
}
}
 
function abstractAltContent(obj) {
var ac = createElement("div");
if (ua.win && ua.ie) {
ac.innerHTML = obj.innerHTML;
}
else {
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
if (nestedObj) {
var c = nestedObj.childNodes;
if (c) {
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
ac.appendChild(c[i].cloneNode(true));
}
}
}
}
}
return ac;
}
/* Cross-browser dynamic SWF creation
*/
function createSWF(attObj, parObj, id) {
var r, el = getElementById(id);
if (ua.wk && ua.wk < 312) { return r; }
if (el) {
if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
attObj.id = id;
}
if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
var att = "";
for (var i in attObj) {
if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
if (i.toLowerCase() == "data") {
parObj.movie = attObj[i];
}
else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
att += ' class="' + attObj[i] + '"';
}
else if (i.toLowerCase() != "classid") {
att += ' ' + i + '="' + attObj[i] + '"';
}
}
}
var par = "";
for (var j in parObj) {
if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
par += '<param name="' + j + '" value="' + parObj[j] + '" />';
}
}
el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
r = getElementById(attObj.id);
}
else { // well-behaving browsers
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
for (var m in attObj) {
if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
o.setAttribute("class", attObj[m]);
}
else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
o.setAttribute(m, attObj[m]);
}
}
}
for (var n in parObj) {
if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
createObjParam(o, n, parObj[n]);
}
}
el.parentNode.replaceChild(o, el);
r = o;
}
}
return r;
}
function createObjParam(el, pName, pValue) {
var p = createElement("param");
p.setAttribute("name", pName);
p.setAttribute("value", pValue);
el.appendChild(p);
}
/* Cross-browser SWF removal
- Especially needed to safely and completely remove a SWF in Internet Explorer
*/
function removeSWF(id) {
var obj = getElementById(id);
if (obj && obj.nodeName == "OBJECT") {
if (ua.ie && ua.win) {
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
removeObjectInIE(id);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
else {
obj.parentNode.removeChild(obj);
}
}
}
function removeObjectInIE(id) {
var obj = getElementById(id);
if (obj) {
for (var i in obj) {
if (typeof obj[i] == "function") {
obj[i] = null;
}
}
obj.parentNode.removeChild(obj);
}
}
/* Functions to optimize JavaScript compression
*/
function getElementById(id) {
var el = null;
try {
el = doc.getElementById(id);
}
catch (e) {}
return el;
}
function createElement(el) {
return doc.createElement(el);
}
/* Updated attachEvent function for Internet Explorer
- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
*/
function addListener(target, eventType, fn) {
target.attachEvent(eventType, fn);
listenersArr[listenersArr.length] = [target, eventType, fn];
}
/* Flash Player and SWF content version matching
*/
function hasPlayerVersion(rv) {
var pv = ua.pv, v = rv.split(".");
v[0] = parseInt(v[0], 10);
v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
v[2] = parseInt(v[2], 10) || 0;
return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
}
/* Cross-browser dynamic CSS creation
- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
*/
function createCSS(sel, decl, media, newStyle) {
if (ua.ie && ua.mac) { return; }
var h = doc.getElementsByTagName("head")[0];
if (!h) { return; } // to also support badly authored HTML pages that lack a head element
var m = (media && typeof media == "string") ? media : "screen";
if (newStyle) {
dynamicStylesheet = null;
dynamicStylesheetMedia = null;
}
if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
// create dynamic stylesheet + get a global reference to it
var s = createElement("style");
s.setAttribute("type", "text/css");
s.setAttribute("media", m);
dynamicStylesheet = h.appendChild(s);
if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
}
dynamicStylesheetMedia = m;
}
// add style rule
if (ua.ie && ua.win) {
if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
dynamicStylesheet.addRule(sel, decl);
}
}
else {
if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
}
}
}
function setVisibility(id, isVisible) {
if (!autoHideShow) { return; }
var v = isVisible ? "visible" : "hidden";
if (isDomLoaded && getElementById(id)) {
getElementById(id).style.visibility = v;
}
else {
createCSS("#" + id, "visibility:" + v);
}
}
 
/* Filter to avoid XSS attacks
*/
function urlEncodeIfNecessary(s) {
var regex = /[\\\"<>\.;]/;
var hasBadChars = regex.exec(s) != null;
return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
}
/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
*/
var cleanup = function() {
if (ua.ie && ua.win) {
window.attachEvent("onunload", function() {
// remove listeners to avoid memory leaks
var ll = listenersArr.length;
for (var i = 0; i < ll; i++) {
listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
}
// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
var il = objIdArr.length;
for (var j = 0; j < il; j++) {
removeSWF(objIdArr[j]);
}
// cleanup library's main closures to avoid memory leaks
for (var k in ua) {
ua[k] = null;
}
ua = null;
for (var l in swfobject) {
swfobject[l] = null;
}
swfobject = null;
});
}
}();
return {
/* Public API
- Reference: http://code.google.com/p/swfobject/wiki/documentation
*/
registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
if (ua.w3 && objectIdStr && swfVersionStr) {
var regObj = {};
regObj.id = objectIdStr;
regObj.swfVersion = swfVersionStr;
regObj.expressInstall = xiSwfUrlStr;
regObj.callbackFn = callbackFn;
regObjArr[regObjArr.length] = regObj;
setVisibility(objectIdStr, false);
}
else if (callbackFn) {
callbackFn({success:false, id:objectIdStr});
}
},
getObjectById: function(objectIdStr) {
if (ua.w3) {
return getObjectById(objectIdStr);
}
},
embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
var callbackObj = {success:false, id:replaceElemIdStr};
if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
setVisibility(replaceElemIdStr, false);
addDomLoadEvent(function() {
widthStr += ""; // auto-convert to string
heightStr += "";
var att = {};
if (attObj && typeof attObj === OBJECT) {
for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
att[i] = attObj[i];
}
}
att.data = swfUrlStr;
att.width = widthStr;
att.height = heightStr;
var par = {};
if (parObj && typeof parObj === OBJECT) {
for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
par[j] = parObj[j];
}
}
if (flashvarsObj && typeof flashvarsObj === OBJECT) {
for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + k + "=" + flashvarsObj[k];
}
else {
par.flashvars = k + "=" + flashvarsObj[k];
}
}
}
if (hasPlayerVersion(swfVersionStr)) { // create SWF
var obj = createSWF(att, par, replaceElemIdStr);
if (att.id == replaceElemIdStr) {
setVisibility(replaceElemIdStr, true);
}
callbackObj.success = true;
callbackObj.ref = obj;
}
else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
att.data = xiSwfUrlStr;
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
return;
}
else { // show alternative content
setVisibility(replaceElemIdStr, true);
}
if (callbackFn) { callbackFn(callbackObj); }
});
}
else if (callbackFn) { callbackFn(callbackObj); }
},
switchOffAutoHideShow: function() {
autoHideShow = false;
},
ua: ua,
getFlashPlayerVersion: function() {
return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
},
hasFlashPlayerVersion: hasPlayerVersion,
createSWF: function(attObj, parObj, replaceElemIdStr) {
if (ua.w3) {
return createSWF(attObj, parObj, replaceElemIdStr);
}
else {
return undefined;
}
},
showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
if (ua.w3 && canExpressInstall()) {
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
}
},
removeSWF: function(objElemIdStr) {
if (ua.w3) {
removeSWF(objElemIdStr);
}
},
createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
if (ua.w3) {
createCSS(selStr, declStr, mediaStr, newStyleBoolean);
}
},
addDomLoadEvent: addDomLoadEvent,
addLoadEvent: addLoadEvent,
getQueryParamValue: function(param) {
var q = doc.location.search || doc.location.hash;
if (q) {
if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
if (param == null) {
return urlEncodeIfNecessary(q);
}
var pairs = q.split("&");
for (var i = 0; i < pairs.length; i++) {
if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
}
}
}
return "";
},
// For internal usage only
expressInstallCallback: function() {
if (isExpressInstallActive) {
var obj = getElementById(EXPRESS_INSTALL_ID);
if (obj && storedAltContent) {
obj.parentNode.replaceChild(storedAltContent, obj);
if (storedAltContentId) {
setVisibility(storedAltContentId, true);
if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
}
if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
}
isExpressInstallActive = false;
}
}
};
}();
 
//##############################################################################
// FlashAudioLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* Loader provides a mechanism to preload Flash content via PreloadJS or internally. Instances are returned to
* the preloader, and the load method is called when the asset needs to be requested.
*
* @class FlashAudioLoader
* @param {String} loadItem The item to be loaded
* @param {Object} flash The flash instance that will do the preloading.
* @extends AbstractLoader
* @protected
*/
function Loader(loadItem) {
this.AbstractLoader_constructor(loadItem, false, createjs.AbstractLoader.SOUND);
 
 
// Public properties
/**
* ID used to facilitate communication with flash.
* Not doc'd because this should not be altered externally
* @property flashId
* @type {String}
*/
this.flashId = null;
 
}
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
*/
s._flash = null;
 
/**
* A list of loader instances that tried to load before _flash was set
* @property _preloadInstances
* @type {Array}
* @protected
*/
s._preloadInstances = [];
 
/**
* Set the Flash instance on the class, and start loading on any instances that had load called
* before flash was ready
* @method setFlash
* @param flash Flash instance that handles loading and playback
*/
s.setFlash = function(flash) {
s._flash = flash;
for(var i = s._preloadInstances.length; i--; ) {
var loader = s._preloadInstances.pop();
loader.load();
}
};
 
// public methods
p.load = function () {
if (s._flash == null) {
// register for future preloading
s._preloadInstances.push(this);
return;
}
 
this.flashId = s._flash.preload(this._item.src);
// Associate this preload instance with the FlashID, so callbacks can route here.
var e = new createjs.Event(createjs.FlashAudioPlugin._REG_FLASHID);
this.dispatchEvent(e);
};
 
/**
* called from flash when loading has progress
* @method handleProgress
* @param loaded
* @param total
* @protected
*/
p.handleProgress = function (loaded, total) {
this._sendProgress(loaded/total);
};
 
/**
* Called from Flash when sound is loaded. Set our ready state and fire callbacks / events
* @method handleComplete
* @protected
*/
p.handleComplete = function () {
this._result = this._item.src;
this._sendComplete();
};
 
/**
* Receive error event from flash and pass it to callback.
* @method handleError
* @param {Event} error
* @protected
*/
p.handleError = function (error) {
this._handleError(error);
};
 
p.destroy = function () {
var e = new createjs.Event(createjs.FlashAudioPlugin._UNREG_FLASHID);
this.dispatchEvent(e);
this.AbstractLoader_destroy();
};
 
p.toString = function () {
return "[FlashAudioLoader]";
};
 
createjs.FlashAudioLoader = createjs.promote(Loader, "AbstractLoader");
 
}());
 
//##############################################################################
// FlashAudioSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* FlashAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
* {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
*
* NOTE audio control is shuttled to a flash player instance via the flash reference.
*
* @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 FlashAudioSoundInstance
* @extends AbstractSoundInstance
* @constructor
*/
function FlashAudioSoundInstance(src, startTime, duration, playbackResource) {
this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
 
 
// Public Properties
/**
* ID used to facilitate communication with flash.
* Not doc'd because this should not be altered externally
* #property flashId
* @type {String}
*/
this.flashId = null; // To communicate with Flash
 
if(s._flash == null) { s._instances.push(this); }
};
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;
/**
* A reference to the Flash instance that gets created.
* #property flash
* @type {Object | Embed}
*/
s._flash = null;
 
/**
* A list of loader instances that tried to load before _flash was set
* #property _preloadInstances
* @type {Array}
* @private
*/
s._instances = [];
 
/**
* Set the Flash instance on the class, and start loading on any instances that had load called
* before flash was ready
* #method setFlash
* @param flash Flash instance that handles loading and playback
*/
s.setFlash = function(flash) {
s._flash = flash;
for(var i = s._instances.length; i--; ) {
var o = s._instances.pop();
o._setDurationFromSource();
}
};
 
 
// 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
p._updateVolume = function() {
if (this.flashId == null) { return; }
s._flash.setVolume(this.flashId, this._volume)
};
 
p._updatePan = function () {
if (this.flashId == null) { return; }
s._flash.setPan(this.flashId, this._pan);
};
 
p._setDurationFromSource = function() {
this._duration = s._flash.getDurationBySrc(this.src);
};
 
p._interrupt = function () {
if(this.flashId == null) { return; }
s._flash.interrupt(this.flashId); // OJR this is redundant, cleanup calls stop that does the same thing anyway
this.AbstractSoundInstance__interrupt();
};
 
p._handleCleanUp = function () {
s._flash.stopSound(this.flashId);
 
this._sendEvent(createjs.FlashAudioPlugin._UNREG_FLASHID);
this.flashId = null;
};
 
p._beginPlaying = function (playProps) {
if (s._flash == null) { return false; }
 
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);
}
this._paused = false;
 
this.flashId = s._flash.playSound(this.src, this._position, this._loop, this._volume, this._pan, this._startTime, this._duration);
if (this.flashId == null) {
this._playFailed();
return false;
}
 
if (this._muted) {this.setMute(true);}
this._sendEvent(createjs.FlashAudioPlugin._REG_FLASHID);
 
this.playState = createjs.Sound.PLAY_SUCCEEDED;
this._sendEvent("succeeded");
return true;
};
 
p._pause = function () {
if(this.flashId == null) { return; }
this._position = this._calculateCurrentPosition();
s._flash.pauseSound(this.flashId);
};
 
p._resume = function () {
if(this.flashId == null) { return; }
s._flash.resumeSound(this.flashId);
};
 
p._handleStop = function () {
if(this.flashId == null) { return; }
s._flash.stopSound(this.flashId);
};
 
p._updateVolume = function () {
var newVolume = this._muted ? 0 : this._volume;
s._flash.setVolume(this.flashId, newVolume);
};
// TODO remove unused .muteSound and .unmuteSound from Flash
 
p._calculateCurrentPosition = function() {
return s._flash.getPosition(this.flashId);
};
 
p._updatePosition = function() {
if(this.flashId == null) { return; }
s._flash.setPosition(this.flashId, this._position);
};
 
// Flash callbacks, only exist in FlashAudioPlugin
/**
* Called from Flash. Lets us know flash has finished playing a sound.
* #method handleSoundFinished
* @protected
*/
p.handleSoundFinished = function () {
this._loop = 0;
this._handleSoundComplete();
};
 
/**
* Called from Flash. Lets us know that flash has played a sound to completion and is looping it.
* #method handleSoundLoop
* @protected
*/
p.handleSoundLoop = function () {
this._loop--;
this._sendEvent("loop");
};
 
createjs.FlashAudioSoundInstance = createjs.promote(FlashAudioSoundInstance, "AbstractSoundInstance");
}());
 
//##############################################################################
// FlashAudioPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
"use strict";
 
/**
* 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
* plugin is recommended to be included if sound support is required in older browsers such as IE8.
*
* This plugin requires FlashAudioPlugin.swf and swfObject.js, which is compiled
* into the minified FlashAudioPlugin-X.X.X.min.js file. You must ensure that {{#crossLink "FlashAudioPlugin/swfPath:property"}}{{/crossLink}}
* is set when using this plugin, so that the script can find the swf.
*
* <h4>Example</h4>
*
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
* // Adds FlashAudioPlugin as a fallback if WebAudio and HTMLAudio do not work.
*
* Note that the SWF is embedded into a container DIV (with an id and classname of "SoundJSFlashContainer"), and
* will have an id of "flashAudioContainer". The container DIV is positioned 1 pixel off-screen to the left to avoid
* showing the 1x1 pixel white square.
*
* <h4>Known Browser and OS issues for Flash Audio</h4>
* <b>All browsers</b><br />
* <ul><li> There can be a delay in flash player starting playback of audio. This has been most noticeable in Firefox.
* Unfortunely this is an issue with the flash player and the browser and therefore cannot be addressed by SoundJS.</li></ul>
*
* @class FlashAudioPlugin
* @extends AbstractPlugin
* @constructor
*/
function FlashAudioPlugin() {
this.AbstractPlugin_constructor();
 
 
// Public Properties
/**
* A developer flag to output all flash events to the console (if it exists). Used for debugging.
*
* createjs.Sound.activePlugin.showOutput = true;
*
* @property showOutput
* @type {Boolean}
* @default false
*/
this.showOutput = false;
 
 
//Private Properties
/**
* The id name of the DIV that gets created for Flash content.
* @property _CONTAINER_ID
* @type {String}
* @default flashAudioContainer
* @protected
*/
this._CONTAINER_ID = "flashAudioContainer";
 
/**
* The id name of the DIV wrapper that contains the Flash content.
* @property _WRAPPER_ID
* @type {String}
* @default SoundJSFlashContainer
* @protected
* @since 0.4.1
*/
this._WRAPPER_ID = "SoundJSFlashContainer";
 
/**
* A reference to the DIV container that gets created to hold the Flash instance.
* @property _container
* @type {HTMLDivElement}
* @protected
*/
this._container = null,
 
/**
* A reference to the Flash instance that gets created.
* @property flash
* @type {Object | Embed}
* @protected
*/
this._flash = null;
 
/**
* Determines if the Flash object has been created and initialized. This is required to make <code>ExternalInterface</code>
* calls from JavaScript to Flash.
* @property flashReady
* @type {Boolean}
* @default false
*/
this.flashReady = false;
 
/**
* A hash of SoundInstances indexed by the related ID in Flash. This lookup is required to connect sounds in
* JavaScript to their respective instances in Flash.
* @property _flashInstances
* @type {Object}
* @protected
*/
this._flashInstances = {};
 
/**
* A hash of Sound Preload instances indexed by the related ID in Flash. This lookup is required to connect
* a preloading sound in Flash with its respective instance in JavaScript.
* @property _flashPreloadInstances
* @type {Object}
* @protected
*/
this._flashPreloadInstances = {};
//TODO consider combining _flashInstances and _flashPreloadInstances into a single hash
 
this._capabilities = s._capabilities;
 
this._loaderClass = createjs.FlashAudioLoader;
this._soundInstanceClass = createjs.FlashAudioSoundInstance;
 
// Create DIV
var w = this.wrapper = document.createElement("div");
w.id = this._WRAPPER_ID;
w.style.position = "absolute";
w.style.marginLeft = "-1px";
w.className = this._WRAPPER_ID;
document.body.appendChild(w);
 
// Create Placeholder
var c = this._container = document.createElement("div");
c.id = this._CONTAINER_ID;
c.appendChild(document.createTextNode("SoundJS Flash Container"));
w.appendChild(c);
 
var path = s.swfPath;
var val = swfobject.embedSWF(path + "FlashAudioPlugin.swf", this._CONTAINER_ID, "1", "1",
"9.0.0", null, null, {"AllowScriptAccess" : "always"}, null,
createjs.proxy(this._handleSWFReady, this)
);
};
 
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.
* @property _REG_FLASHID
* @type {String}
* @default registerflashid
* @static
* @protected
*/
s._REG_FLASHID = "registerflashid";
 
/**
* Event constant for the "unregisterFlashID" event for cleaner code.
* @property _UNREG_FLASHID
* @type {String}
* @default unregisterflashid
* @static
* @protected
*/
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
* capabilities.
* @property _capabilities
* @type {Object}
* @protected
* @static
*/
s._capabilities = null;
 
/**
* The path relative to the HTML page that the FlashAudioPlugin.swf resides. Note if this is not correct, this
* plugin will not work.
* @property swfPath
* @type {String}
* @default src/SoundJS
* @static
* @since 0.5.2
*/
s.swfPath = "src/soundjs/flashaudio/";
 
 
// Static Methods
/**
* Determine if the plugin can be used in the current browser/OS.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
// there is no flash player on mobile devices
if (createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry || createjs.BrowserDetect.isWindowsPhone) {return false;}
s._generateCapabilities();
if (swfobject == null) {return false;}
return swfobject.hasFlashPlayerVersion("9.0.0");
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
var c = s._capabilities = {
panning:true,
volume:true,
tracks:-1,
mp3:true,
ogg:false,
mpeg:true,
wav:true,
// our current implementation cannot support mp4 http://forums.adobe.com/thread/825408
m4a:false,
mp4:false,
aiff:false, // not listed in player but is Supported by Flash so this may be true
wma:false,
mid:false
};
};
 
 
//public methods
p.register = function (src, instances) {
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.removeAllSounds = function () {
this._flashInstances = {};
this._flashPreloadInstances = {};
// NOTE sound cannot be removed from a swf
 
this.AbstractPlugin_removeAllSounds();
};
 
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
si.on(s._REG_FLASHID, this.registerSoundInstance, this);
si.on(s._UNREG_FLASHID, this.unregisterSoundInstance, this);
return si;
};
 
p.toString = function () {
return "[FlashAudioPlugin]";
};
 
 
// private methods
/**
* The SWF used for sound preloading and playback has been initialized.
* @method _handleSWFReady
* @param {Object} event Contains a reference to the swf.
* @protected
*/
p._handleSWFReady = function (event) {
this._flash = event.ref;
};
 
/**
* The Flash application that handles preloading and playback is ready. We wait for a callback from Flash to
* ensure that everything is in place before playback begins.
* @method _handleFlashReady
* @protected
*/
p._handleFlashReady = function () {
this.flashReady = true;
 
this._loaderClass.setFlash(this._flash);
this._soundInstanceClass.setFlash(this._flash);
};
 
/**
* Internal function used to set the gain value for master audio. Should not be called externally.
* @method _updateVolume
* @return {Boolean}
* @protected
* @since 0.4.0
*/
p._updateVolume = function () {
var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
return this._flash.setMasterVolume(newVolume);
};
 
 
// Flash Communication
// Note we have decided not to include these in the docs
/*
* Used to couple a Flash loader instance with a <code>Loader</code> instance
* @method registerPreloadInstance
* @param {String} flashId Used to identify the Loader.
* @param {Loader} instance The actual instance.
*/
p.registerPreloadInstance = function (event) {
this._flashPreloadInstances[event.target.flashId] = event.target;
};
 
/*
* Used to decouple a <code>Loader</code> instance from Flash.
* @method unregisterPreloadInstance
* @param {String} flashId Used to identify the Loader.
*/
p.unregisterPreloadInstance = function (event) {
delete this._flashPreloadInstances[event.target.flashId];
};
 
/*
* Used to couple a Flash sound instance with a {{#crossLink "FlashAudioSoundInstance"}}{{/crossLink}}.
* @method registerSoundInstance
* @param {String} flashId Used to identify the FlashAudioSoundInstance.
* @param {Loader} instance The actual instance.
*/
p.registerSoundInstance = function (event) {
this._flashInstances[event.target.flashId] = event.target;
};
 
/*
* Used to decouple a {{#crossLink "FlashAudioSoundInstance"}}{{/crossLink}} from Flash.
* instance.
* @method unregisterSoundInstance
* @param {String} flashId Used to identify the FlashAudioSoundInstance.
* @param {Loader} instance The actual instance.
*/
p.unregisterSoundInstance = function (event) {
delete this._flashInstances[event.target.flashId];
};
 
/*
* Used to output traces from Flash to the console, if {{#crossLink "FlashAudioPlugin/showOutput"}}{{/crossLink}} is
* <code>true</code>.
* @method flashLog
* @param {String} data The information to be output.
*/
p.flashLog = function (data) {
try {
this.showOutput && console.log(data);
} catch (error) {
// older IE will cause error if console is not open
}
};
 
/*
* Handles events from Flash, and routes communication to a {{#crossLink "FlashAudioSoundInstance"}}{{/crossLink}} via
* the Flash ID. The method and arguments from Flash are run directly on the loader or sound instance.
* @method handleSoundEvent
* @param {String} flashId Used to identify the FlashAudioSoundInstance.
* @param {String} method Indicates the method to run.
*/
p.handleSoundEvent = function (flashId, method) {
var instance = this._flashInstances[flashId];
if (instance == null) {return;}
var args = [];
for (var i = 2, l = arguments.length; i < l; i++) {
args.push(arguments[i]);
}
try {
if (args.length == 0) {
instance[method]();
} else {
instance[method].apply(instance, args);
}
} catch (error) {
}
};
 
/*
* Handles events from Flash and routes communication to a <code>Loader</code> via the Flash ID. The method
* and arguments from Flash are run directly on the sound loader.
* @method handlePreloadEvent
* @param {String} flashId Used to identify the loader instance.
* @param {String} method Indicates the method to run.
*/
p.handlePreloadEvent = function (flashId, method) {
var instance = this._flashPreloadInstances[flashId];
if (instance == null) {
return;
}
var args = [];
for (var i = 2, l = arguments.length; i < l; i++) {
args.push(arguments[i]);
}
try {
if (args.length == 0) {
instance[method]();
} else {
instance[method].apply(instance, args);
}
} catch (error) {
}
};
 
/*
* Handles events from Flash intended for the FlashAudioPlugin class. Currently only a "ready" event is processed.
* @method handleEvent
* @param {String} method Indicates the method to run.
*/
p.handleEvent = function (method) {
switch (method) {
case "ready":
this._handleFlashReady();
break;
}
};
 
/*
* Handles error events from Flash. Note this function currently does not process any events.
* @method handleErrorEvent
* @param {String} error Indicates the error.
*/
p.handleErrorEvent = function (error) {
 
};
 
createjs.FlashAudioPlugin = createjs.promote(FlashAudioPlugin, "AbstractPlugin");
}());
 
//##############################################################################
// version_flashplugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
var s = createjs.FlashAudioPlugin = createjs.FlashAudioPlugin || {};
 
/**
* The version string for this release.
* @for FlashAudioPlugin
* @property version
* @type String
* @static
**/
s.version = /*=version*/"0.6.2"; // injected by build process
 
/**
* The build date for this release in UTC format.
* @for FlashAudioPlugin
* @property buildDate
* @type String
* @static
**/
s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:31 GMT"; // injected by build process
 
})();
/bower_components/SoundJS/lib/flashaudioplugin-0.6.2.min.js
@@ -0,0 +1,20 @@
/*!
* @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/)
*/
 
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
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+='<param name="'+i+'" value="'+b[i]+'" />');e.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+f+">"+h+"</object>",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;d<c.length;d++)if(c[d].substring(0,c[d].indexOf("="))==a)return v(c[d].substring(c[d].indexOf("=")+1))}return""},expressInstallCallback:function(){if(S){var a=p(H);a&&w&&(a.parentNode.replaceChild(w,a),x&&(u(x,!0),U.ie&&U.win&&(w.style.display="block")),y&&y(z)),S=!1}}}}();this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!1,createjs.AbstractLoader.SOUND),this.flashId=null}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c._flash=null,c._preloadInstances=[],c.setFlash=function(a){c._flash=a;for(var b=c._preloadInstances.length;b--;){var d=c._preloadInstances.pop();d.load()}},b.load=function(){if(null==c._flash)return void c._preloadInstances.push(this);this.flashId=c._flash.preload(this._item.src);var a=new createjs.Event(createjs.FlashAudioPlugin._REG_FLASHID);this.dispatchEvent(a)},b.handleProgress=function(a,b){this._sendProgress(a/b)},b.handleComplete=function(){this._result=this._item.src,this._sendComplete()},b.handleError=function(a){this._handleError(a)},b.destroy=function(){var a=new createjs.Event(createjs.FlashAudioPlugin._UNREG_FLASHID);this.dispatchEvent(a),this.AbstractLoader_destroy()},b.toString=function(){return"[FlashAudioLoader]"},createjs.FlashAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function FlashAudioSoundInstance(a,c,d,e){this.AbstractSoundInstance_constructor(a,c,d,e),this.flashId=null,null==b._flash&&b._instances.push(this)}var a=createjs.extend(FlashAudioSoundInstance,createjs.AbstractSoundInstance),b=FlashAudioSoundInstance;b._flash=null,b._instances=[],b.setFlash=function(a){b._flash=a;for(var c=b._instances.length;c--;){var d=b._instances.pop();d._setDurationFromSource()}},a.setLoop=function(a){null!=this.flashId&&b._flash.setLoop(this.flashId,a),this._loop=a},a.toString=function(){return"[FlashAudioSoundInstance]"},a._updateVolume=function(){null!=this.flashId&&b._flash.setVolume(this.flashId,this._volume)},a._updatePan=function(){null!=this.flashId&&b._flash.setPan(this.flashId,this._pan)},a._setDurationFromSource=function(){this._duration=b._flash.getDurationBySrc(this.src)},a._interrupt=function(){null!=this.flashId&&(b._flash.interrupt(this.flashId),this.AbstractSoundInstance__interrupt())},a._handleCleanUp=function(){b._flash.stopSound(this.flashId),this._sendEvent(createjs.FlashAudioPlugin._UNREG_FLASHID),this.flashId=null},a._beginPlaying=function(a){return null==b._flash?!1:(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)),this._paused=!1,this.flashId=b._flash.playSound(this.src,this._position,this._loop,this._volume,this._pan,this._startTime,this._duration),null==this.flashId?(this._playFailed(),!1):(this._muted&&this.setMute(!0),this._sendEvent(createjs.FlashAudioPlugin._REG_FLASHID),this.playState=createjs.Sound.PLAY_SUCCEEDED,this._sendEvent("succeeded"),!0))},a._pause=function(){null!=this.flashId&&(this._position=this._calculateCurrentPosition(),b._flash.pauseSound(this.flashId))},a._resume=function(){null!=this.flashId&&b._flash.resumeSound(this.flashId)},a._handleStop=function(){null!=this.flashId&&b._flash.stopSound(this.flashId)},a._updateVolume=function(){var a=this._muted?0:this._volume;b._flash.setVolume(this.flashId,a)},a._calculateCurrentPosition=function(){return b._flash.getPosition(this.flashId)},a._updatePosition=function(){null!=this.flashId&&b._flash.setPosition(this.flashId,this._position)},a.handleSoundFinished=function(){this._loop=0,this._handleSoundComplete()},a.handleSoundLoop=function(){this._loop--,this._sendEvent("loop")},createjs.FlashAudioSoundInstance=createjs.promote(FlashAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function FlashAudioPlugin(){this.AbstractPlugin_constructor(),this.showOutput=!1,this._CONTAINER_ID="flashAudioContainer",this._WRAPPER_ID="SoundJSFlashContainer",this._container=null,this._flash=null,this.flashReady=!1,this._flashInstances={},this._flashPreloadInstances={},this._capabilities=b._capabilities,this._loaderClass=createjs.FlashAudioLoader,this._soundInstanceClass=createjs.FlashAudioSoundInstance;var a=this.wrapper=document.createElement("div");a.id=this._WRAPPER_ID,a.style.position="absolute",a.style.marginLeft="-1px",a.className=this._WRAPPER_ID,document.body.appendChild(a);var c=this._container=document.createElement("div");c.id=this._CONTAINER_ID,c.appendChild(document.createTextNode("SoundJS Flash Container")),a.appendChild(c);{var d=b.swfPath;swfobject.embedSWF(d+"FlashAudioPlugin.swf",this._CONTAINER_ID,"1","1","9.0.0",null,null,{AllowScriptAccess:"always"},null,createjs.proxy(this._handleSWFReady,this))}}var a=createjs.extend(FlashAudioPlugin,createjs.AbstractPlugin),b=FlashAudioPlugin;b._REG_FLASHID="registerflashid",b._UNREG_FLASHID="unregisterflashid",b._capabilities=null,b.swfPath="src/soundjs/flashaudio/",b.isSupported=function(){return createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry||createjs.BrowserDetect.isWindowsPhone?!1:(b._generateCapabilities(),null==swfobject?!1:swfobject.hasFlashPlayerVersion("9.0.0"))},b._generateCapabilities=function(){if(null==b._capabilities){b._capabilities={panning:!0,volume:!0,tracks:-1,mp3:!0,ogg:!1,mpeg:!0,wav:!0,m4a:!1,mp4:!1,aiff:!1,wma:!1,mid:!1}}},a.register=function(a,c){var d=this.AbstractPlugin_register(a,c);return d.addEventListener(b._REG_FLASHID,createjs.proxy(this.registerPreloadInstance,this)),d.addEventListener(b._UNREG_FLASHID,createjs.proxy(this.unregisterPreloadInstance,this)),d},a.removeAllSounds=function(){this._flashInstances={},this._flashPreloadInstances={},this.AbstractPlugin_removeAllSounds()},a.create=function(a,c,d){var e=this.AbstractPlugin_create(a,c,d);return e.on(b._REG_FLASHID,this.registerSoundInstance,this),e.on(b._UNREG_FLASHID,this.unregisterSoundInstance,this),e},a.toString=function(){return"[FlashAudioPlugin]"},a._handleSWFReady=function(a){this._flash=a.ref},a._handleFlashReady=function(){this.flashReady=!0,this._loaderClass.setFlash(this._flash),this._soundInstanceClass.setFlash(this._flash)},a._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;return this._flash.setMasterVolume(a)},a.registerPreloadInstance=function(a){this._flashPreloadInstances[a.target.flashId]=a.target},a.unregisterPreloadInstance=function(a){delete this._flashPreloadInstances[a.target.flashId]},a.registerSoundInstance=function(a){this._flashInstances[a.target.flashId]=a.target},a.unregisterSoundInstance=function(a){delete this._flashInstances[a.target.flashId]},a.flashLog=function(a){try{this.showOutput&&console.log(a)}catch(b){}},a.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){}}},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="0.6.2",a.buildDate="Thu, 26 Nov 2015 20:44:31 GMT"}();
/bower_components/SoundJS/lib/flashaudioplugin-NEXT.combined.js
@@ -0,0 +1,1604 @@
/*!
* 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.
*/
 
 
//##############################################################################
// swfobject.js
//##############################################################################
 
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
 
var swfobject = function() {
var UNDEF = "undefined",
OBJECT = "object",
SHOCKWAVE_FLASH = "Shockwave Flash",
SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
FLASH_MIME_TYPE = "application/x-shockwave-flash",
EXPRESS_INSTALL_ID = "SWFObjectExprInst",
ON_READY_STATE_CHANGE = "onreadystatechange",
win = window,
doc = document,
nav = navigator,
plugin = false,
domLoadFnArr = [main],
regObjArr = [],
objIdArr = [],
listenersArr = [],
storedAltContent,
storedAltContentId,
storedCallbackFn,
storedCallbackObj,
isDomLoaded = false,
isExpressInstallActive = false,
dynamicStylesheet,
dynamicStylesheetMedia,
autoHideShow = true,
/* Centralized function for browser feature detection
- User agent string detection is only used when no good alternative is possible
- Is executed directly for optimal performance
*/
ua = function() {
var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
u = nav.userAgent.toLowerCase(),
p = nav.platform.toLowerCase(),
windows = p ? /win/.test(p) : /win/.test(u),
mac = p ? /mac/.test(p) : /mac/.test(u),
webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
playerVersion = [0,0,0],
d = null;
if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
d = nav.plugins[SHOCKWAVE_FLASH].description;
if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
plugin = true;
ie = false; // cascaded feature detection for Internet Explorer
d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
}
}
else if (typeof win.ActiveXObject != UNDEF) {
try {
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
if (a) { // a will return null when ActiveX is disabled
d = a.GetVariable("$version");
if (d) {
ie = true; // cascaded feature detection for Internet Explorer
d = d.split(" ")[1].split(",");
playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
}
catch(e) {}
}
return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
}(),
/* Cross-browser onDomLoad
- Will fire an event as soon as the DOM of a web page is loaded
- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
- Regular onload serves as fallback
*/
onDomLoad = function() {
if (!ua.w3) { return; }
if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
callDomLoadFunctions();
}
if (!isDomLoaded) {
if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
}
if (ua.ie && ua.win) {
doc.attachEvent(ON_READY_STATE_CHANGE, function() {
if (doc.readyState == "complete") {
doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
callDomLoadFunctions();
}
});
if (win == top) { // if not inside an iframe
(function(){
if (isDomLoaded) { return; }
try {
doc.documentElement.doScroll("left");
}
catch(e) {
setTimeout(arguments.callee, 0);
return;
}
callDomLoadFunctions();
})();
}
}
if (ua.wk) {
(function(){
if (isDomLoaded) { return; }
if (!/loaded|complete/.test(doc.readyState)) {
setTimeout(arguments.callee, 0);
return;
}
callDomLoadFunctions();
})();
}
addLoadEvent(callDomLoadFunctions);
}
}();
function callDomLoadFunctions() {
if (isDomLoaded) { return; }
try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
t.parentNode.removeChild(t);
}
catch (e) { return; }
isDomLoaded = true;
var dl = domLoadFnArr.length;
for (var i = 0; i < dl; i++) {
domLoadFnArr[i]();
}
}
function addDomLoadEvent(fn) {
if (isDomLoaded) {
fn();
}
else {
domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
}
}
/* Cross-browser onload
- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
- Will fire an event as soon as a web page including all of its assets are loaded
*/
function addLoadEvent(fn) {
if (typeof win.addEventListener != UNDEF) {
win.addEventListener("load", fn, false);
}
else if (typeof doc.addEventListener != UNDEF) {
doc.addEventListener("load", fn, false);
}
else if (typeof win.attachEvent != UNDEF) {
addListener(win, "onload", fn);
}
else if (typeof win.onload == "function") {
var fnOld = win.onload;
win.onload = function() {
fnOld();
fn();
};
}
else {
win.onload = fn;
}
}
/* Main function
- Will preferably execute onDomLoad, otherwise onload (as a fallback)
*/
function main() {
if (plugin) {
testPlayerVersion();
}
else {
matchVersions();
}
}
/* Detect the Flash Player version for non-Internet Explorer browsers
- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
a. Both release and build numbers can be detected
b. Avoid wrong descriptions by corrupt installers provided by Adobe
c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
*/
function testPlayerVersion() {
var b = doc.getElementsByTagName("body")[0];
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
var t = b.appendChild(o);
if (t) {
var counter = 0;
(function(){
if (typeof t.GetVariable != UNDEF) {
var d = t.GetVariable("$version");
if (d) {
d = d.split(" ")[1].split(",");
ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
}
}
else if (counter < 10) {
counter++;
setTimeout(arguments.callee, 10);
return;
}
b.removeChild(o);
t = null;
matchVersions();
})();
}
else {
matchVersions();
}
}
/* Perform Flash Player and SWF version matching; static publishing only
*/
function matchVersions() {
var rl = regObjArr.length;
if (rl > 0) {
for (var i = 0; i < rl; i++) { // for each registered object element
var id = regObjArr[i].id;
var cb = regObjArr[i].callbackFn;
var cbObj = {success:false, id:id};
if (ua.pv[0] > 0) {
var obj = getElementById(id);
if (obj) {
if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
setVisibility(id, true);
if (cb) {
cbObj.success = true;
cbObj.ref = getObjectById(id);
cb(cbObj);
}
}
else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
var att = {};
att.data = regObjArr[i].expressInstall;
att.width = obj.getAttribute("width") || "0";
att.height = obj.getAttribute("height") || "0";
if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
// parse HTML object param element's name-value pairs
var par = {};
var p = obj.getElementsByTagName("param");
var pl = p.length;
for (var j = 0; j < pl; j++) {
if (p[j].getAttribute("name").toLowerCase() != "movie") {
par[p[j].getAttribute("name")] = p[j].getAttribute("value");
}
}
showExpressInstall(att, par, id, cb);
}
else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
displayAltContent(obj);
if (cb) { cb(cbObj); }
}
}
}
else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
setVisibility(id, true);
if (cb) {
var o = getObjectById(id); // test whether there is an HTML object element or not
if (o && typeof o.SetVariable != UNDEF) {
cbObj.success = true;
cbObj.ref = o;
}
cb(cbObj);
}
}
}
}
}
function getObjectById(objectIdStr) {
var r = null;
var o = getElementById(objectIdStr);
if (o && o.nodeName == "OBJECT") {
if (typeof o.SetVariable != UNDEF) {
r = o;
}
else {
var n = o.getElementsByTagName(OBJECT)[0];
if (n) {
r = n;
}
}
}
return r;
}
/* Requirements for Adobe Express Install
- only one instance can be active at a time
- fp 6.0.65 or higher
- Win/Mac OS only
- no Webkit engines older than version 312
*/
function canExpressInstall() {
return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
}
/* Show the Adobe Express Install dialog
- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
*/
function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
isExpressInstallActive = true;
storedCallbackFn = callbackFn || null;
storedCallbackObj = {success:false, id:replaceElemIdStr};
var obj = getElementById(replaceElemIdStr);
if (obj) {
if (obj.nodeName == "OBJECT") { // static publishing
storedAltContent = abstractAltContent(obj);
storedAltContentId = null;
}
else { // dynamic publishing
storedAltContent = obj;
storedAltContentId = replaceElemIdStr;
}
att.id = EXPRESS_INSTALL_ID;
if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + fv;
}
else {
par.flashvars = fv;
}
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
if (ua.ie && ua.win && obj.readyState != 4) {
var newObj = createElement("div");
replaceElemIdStr += "SWFObjectNew";
newObj.setAttribute("id", replaceElemIdStr);
obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
createSWF(att, par, replaceElemIdStr);
}
}
/* Functions to abstract and display alternative content
*/
function displayAltContent(obj) {
if (ua.ie && ua.win && obj.readyState != 4) {
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
var el = createElement("div");
obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
el.parentNode.replaceChild(abstractAltContent(obj), el);
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
obj.parentNode.removeChild(obj);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
else {
obj.parentNode.replaceChild(abstractAltContent(obj), obj);
}
}
 
function abstractAltContent(obj) {
var ac = createElement("div");
if (ua.win && ua.ie) {
ac.innerHTML = obj.innerHTML;
}
else {
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
if (nestedObj) {
var c = nestedObj.childNodes;
if (c) {
var cl = c.length;
for (var i = 0; i < cl; i++) {
if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
ac.appendChild(c[i].cloneNode(true));
}
}
}
}
}
return ac;
}
/* Cross-browser dynamic SWF creation
*/
function createSWF(attObj, parObj, id) {
var r, el = getElementById(id);
if (ua.wk && ua.wk < 312) { return r; }
if (el) {
if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
attObj.id = id;
}
if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
var att = "";
for (var i in attObj) {
if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
if (i.toLowerCase() == "data") {
parObj.movie = attObj[i];
}
else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
att += ' class="' + attObj[i] + '"';
}
else if (i.toLowerCase() != "classid") {
att += ' ' + i + '="' + attObj[i] + '"';
}
}
}
var par = "";
for (var j in parObj) {
if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
par += '<param name="' + j + '" value="' + parObj[j] + '" />';
}
}
el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
r = getElementById(attObj.id);
}
else { // well-behaving browsers
var o = createElement(OBJECT);
o.setAttribute("type", FLASH_MIME_TYPE);
for (var m in attObj) {
if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
o.setAttribute("class", attObj[m]);
}
else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
o.setAttribute(m, attObj[m]);
}
}
}
for (var n in parObj) {
if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
createObjParam(o, n, parObj[n]);
}
}
el.parentNode.replaceChild(o, el);
r = o;
}
}
return r;
}
function createObjParam(el, pName, pValue) {
var p = createElement("param");
p.setAttribute("name", pName);
p.setAttribute("value", pValue);
el.appendChild(p);
}
/* Cross-browser SWF removal
- Especially needed to safely and completely remove a SWF in Internet Explorer
*/
function removeSWF(id) {
var obj = getElementById(id);
if (obj && obj.nodeName == "OBJECT") {
if (ua.ie && ua.win) {
obj.style.display = "none";
(function(){
if (obj.readyState == 4) {
removeObjectInIE(id);
}
else {
setTimeout(arguments.callee, 10);
}
})();
}
else {
obj.parentNode.removeChild(obj);
}
}
}
function removeObjectInIE(id) {
var obj = getElementById(id);
if (obj) {
for (var i in obj) {
if (typeof obj[i] == "function") {
obj[i] = null;
}
}
obj.parentNode.removeChild(obj);
}
}
/* Functions to optimize JavaScript compression
*/
function getElementById(id) {
var el = null;
try {
el = doc.getElementById(id);
}
catch (e) {}
return el;
}
function createElement(el) {
return doc.createElement(el);
}
/* Updated attachEvent function for Internet Explorer
- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
*/
function addListener(target, eventType, fn) {
target.attachEvent(eventType, fn);
listenersArr[listenersArr.length] = [target, eventType, fn];
}
/* Flash Player and SWF content version matching
*/
function hasPlayerVersion(rv) {
var pv = ua.pv, v = rv.split(".");
v[0] = parseInt(v[0], 10);
v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
v[2] = parseInt(v[2], 10) || 0;
return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
}
/* Cross-browser dynamic CSS creation
- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
*/
function createCSS(sel, decl, media, newStyle) {
if (ua.ie && ua.mac) { return; }
var h = doc.getElementsByTagName("head")[0];
if (!h) { return; } // to also support badly authored HTML pages that lack a head element
var m = (media && typeof media == "string") ? media : "screen";
if (newStyle) {
dynamicStylesheet = null;
dynamicStylesheetMedia = null;
}
if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
// create dynamic stylesheet + get a global reference to it
var s = createElement("style");
s.setAttribute("type", "text/css");
s.setAttribute("media", m);
dynamicStylesheet = h.appendChild(s);
if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
}
dynamicStylesheetMedia = m;
}
// add style rule
if (ua.ie && ua.win) {
if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
dynamicStylesheet.addRule(sel, decl);
}
}
else {
if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
}
}
}
function setVisibility(id, isVisible) {
if (!autoHideShow) { return; }
var v = isVisible ? "visible" : "hidden";
if (isDomLoaded && getElementById(id)) {
getElementById(id).style.visibility = v;
}
else {
createCSS("#" + id, "visibility:" + v);
}
}
 
/* Filter to avoid XSS attacks
*/
function urlEncodeIfNecessary(s) {
var regex = /[\\\"<>\.;]/;
var hasBadChars = regex.exec(s) != null;
return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
}
/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
*/
var cleanup = function() {
if (ua.ie && ua.win) {
window.attachEvent("onunload", function() {
// remove listeners to avoid memory leaks
var ll = listenersArr.length;
for (var i = 0; i < ll; i++) {
listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
}
// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
var il = objIdArr.length;
for (var j = 0; j < il; j++) {
removeSWF(objIdArr[j]);
}
// cleanup library's main closures to avoid memory leaks
for (var k in ua) {
ua[k] = null;
}
ua = null;
for (var l in swfobject) {
swfobject[l] = null;
}
swfobject = null;
});
}
}();
return {
/* Public API
- Reference: http://code.google.com/p/swfobject/wiki/documentation
*/
registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
if (ua.w3 && objectIdStr && swfVersionStr) {
var regObj = {};
regObj.id = objectIdStr;
regObj.swfVersion = swfVersionStr;
regObj.expressInstall = xiSwfUrlStr;
regObj.callbackFn = callbackFn;
regObjArr[regObjArr.length] = regObj;
setVisibility(objectIdStr, false);
}
else if (callbackFn) {
callbackFn({success:false, id:objectIdStr});
}
},
getObjectById: function(objectIdStr) {
if (ua.w3) {
return getObjectById(objectIdStr);
}
},
embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
var callbackObj = {success:false, id:replaceElemIdStr};
if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
setVisibility(replaceElemIdStr, false);
addDomLoadEvent(function() {
widthStr += ""; // auto-convert to string
heightStr += "";
var att = {};
if (attObj && typeof attObj === OBJECT) {
for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
att[i] = attObj[i];
}
}
att.data = swfUrlStr;
att.width = widthStr;
att.height = heightStr;
var par = {};
if (parObj && typeof parObj === OBJECT) {
for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
par[j] = parObj[j];
}
}
if (flashvarsObj && typeof flashvarsObj === OBJECT) {
for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
if (typeof par.flashvars != UNDEF) {
par.flashvars += "&" + k + "=" + flashvarsObj[k];
}
else {
par.flashvars = k + "=" + flashvarsObj[k];
}
}
}
if (hasPlayerVersion(swfVersionStr)) { // create SWF
var obj = createSWF(att, par, replaceElemIdStr);
if (att.id == replaceElemIdStr) {
setVisibility(replaceElemIdStr, true);
}
callbackObj.success = true;
callbackObj.ref = obj;
}
else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
att.data = xiSwfUrlStr;
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
return;
}
else { // show alternative content
setVisibility(replaceElemIdStr, true);
}
if (callbackFn) { callbackFn(callbackObj); }
});
}
else if (callbackFn) { callbackFn(callbackObj); }
},
switchOffAutoHideShow: function() {
autoHideShow = false;
},
ua: ua,
getFlashPlayerVersion: function() {
return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
},
hasFlashPlayerVersion: hasPlayerVersion,
createSWF: function(attObj, parObj, replaceElemIdStr) {
if (ua.w3) {
return createSWF(attObj, parObj, replaceElemIdStr);
}
else {
return undefined;
}
},
showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
if (ua.w3 && canExpressInstall()) {
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
}
},
removeSWF: function(objElemIdStr) {
if (ua.w3) {
removeSWF(objElemIdStr);
}
},
createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
if (ua.w3) {
createCSS(selStr, declStr, mediaStr, newStyleBoolean);
}
},
addDomLoadEvent: addDomLoadEvent,
addLoadEvent: addLoadEvent,
getQueryParamValue: function(param) {
var q = doc.location.search || doc.location.hash;
if (q) {
if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
if (param == null) {
return urlEncodeIfNecessary(q);
}
var pairs = q.split("&");
for (var i = 0; i < pairs.length; i++) {
if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
}
}
}
return "";
},
// For internal usage only
expressInstallCallback: function() {
if (isExpressInstallActive) {
var obj = getElementById(EXPRESS_INSTALL_ID);
if (obj && storedAltContent) {
obj.parentNode.replaceChild(storedAltContent, obj);
if (storedAltContentId) {
setVisibility(storedAltContentId, true);
if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
}
if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
}
isExpressInstallActive = false;
}
}
};
}();
 
//##############################################################################
// FlashAudioLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* Loader provides a mechanism to preload Flash content via PreloadJS or internally. Instances are returned to
* the preloader, and the load method is called when the asset needs to be requested.
*
* @class FlashAudioLoader
* @param {String} loadItem The item to be loaded
* @param {Object} flash The flash instance that will do the preloading.
* @extends AbstractLoader
* @protected
*/
function Loader(loadItem) {
this.AbstractLoader_constructor(loadItem, false, createjs.AbstractLoader.SOUND);
 
 
// Public properties
/**
* ID used to facilitate communication with flash.
* Not doc'd because this should not be altered externally
* @property flashId
* @type {String}
*/
this.flashId = null;
 
}
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
*/
s._flash = null;
 
/**
* A list of loader instances that tried to load before _flash was set
* @property _preloadInstances
* @type {Array}
* @protected
*/
s._preloadInstances = [];
 
/**
* Set the Flash instance on the class, and start loading on any instances that had load called
* before flash was ready
* @method setFlash
* @param flash Flash instance that handles loading and playback
*/
s.setFlash = function(flash) {
s._flash = flash;
for(var i = s._preloadInstances.length; i--; ) {
var loader = s._preloadInstances.pop();
loader.load();
}
};
 
// public methods
p.load = function () {
if (s._flash == null) {
// register for future preloading
s._preloadInstances.push(this);
return;
}
 
this.flashId = s._flash.preload(this._item.src);
// Associate this preload instance with the FlashID, so callbacks can route here.
var e = new createjs.Event(createjs.FlashAudioPlugin._REG_FLASHID);
this.dispatchEvent(e);
};
 
/**
* called from flash when loading has progress
* @method handleProgress
* @param loaded
* @param total
* @protected
*/
p.handleProgress = function (loaded, total) {
this._sendProgress(loaded/total);
};
 
/**
* Called from Flash when sound is loaded. Set our ready state and fire callbacks / events
* @method handleComplete
* @protected
*/
p.handleComplete = function () {
this._result = this._item.src;
this._sendComplete();
};
 
/**
* Receive error event from flash and pass it to callback.
* @method handleError
* @param {Event} error
* @protected
*/
p.handleError = function (error) {
this._handleError(error);
};
 
p.destroy = function () {
var e = new createjs.Event(createjs.FlashAudioPlugin._UNREG_FLASHID);
this.dispatchEvent(e);
this.AbstractLoader_destroy();
};
 
p.toString = function () {
return "[FlashAudioLoader]";
};
 
createjs.FlashAudioLoader = createjs.promote(Loader, "AbstractLoader");
 
}());
 
//##############################################################################
// FlashAudioSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* FlashAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
* {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
*
* NOTE audio control is shuttled to a flash player instance via the flash reference.
*
* @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 FlashAudioSoundInstance
* @extends AbstractSoundInstance
* @constructor
*/
function FlashAudioSoundInstance(src, startTime, duration, playbackResource) {
this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
 
 
// Public Properties
/**
* ID used to facilitate communication with flash.
* Not doc'd because this should not be altered externally
* #property flashId
* @type {String}
*/
this.flashId = null; // To communicate with Flash
 
if(s._flash == null) { s._instances.push(this); }
};
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;
/**
* A reference to the Flash instance that gets created.
* #property flash
* @type {Object | Embed}
*/
s._flash = null;
 
/**
* A list of loader instances that tried to load before _flash was set
* #property _preloadInstances
* @type {Array}
* @private
*/
s._instances = [];
 
/**
* Set the Flash instance on the class, and start loading on any instances that had load called
* before flash was ready
* #method setFlash
* @param flash Flash instance that handles loading and playback
*/
s.setFlash = function(flash) {
s._flash = flash;
for(var i = s._instances.length; i--; ) {
var o = s._instances.pop();
o._setDurationFromSource();
}
};
 
 
// 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
p._updateVolume = function() {
if (this.flashId == null) { return; }
s._flash.setVolume(this.flashId, this._volume)
};
 
p._updatePan = function () {
if (this.flashId == null) { return; }
s._flash.setPan(this.flashId, this._pan);
};
 
p._setDurationFromSource = function() {
this._duration = s._flash.getDurationBySrc(this.src);
};
 
p._interrupt = function () {
if(this.flashId == null) { return; }
s._flash.interrupt(this.flashId); // OJR this is redundant, cleanup calls stop that does the same thing anyway
this.AbstractSoundInstance__interrupt();
};
 
p._handleCleanUp = function () {
s._flash.stopSound(this.flashId);
 
this._sendEvent(createjs.FlashAudioPlugin._UNREG_FLASHID);
this.flashId = null;
};
 
p._beginPlaying = function (playProps) {
if (s._flash == null) { return false; }
 
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);
}
this._paused = false;
 
this.flashId = s._flash.playSound(this.src, this._position, this._loop, this._volume, this._pan, this._startTime, this._duration);
if (this.flashId == null) {
this._playFailed();
return false;
}
 
if (this._muted) {this.setMute(true);}
this._sendEvent(createjs.FlashAudioPlugin._REG_FLASHID);
 
this.playState = createjs.Sound.PLAY_SUCCEEDED;
this._sendEvent("succeeded");
return true;
};
 
p._pause = function () {
if(this.flashId == null) { return; }
this._position = this._calculateCurrentPosition();
s._flash.pauseSound(this.flashId);
};
 
p._resume = function () {
if(this.flashId == null) { return; }
s._flash.resumeSound(this.flashId);
};
 
p._handleStop = function () {
if(this.flashId == null) { return; }
s._flash.stopSound(this.flashId);
};
 
p._updateVolume = function () {
var newVolume = this._muted ? 0 : this._volume;
s._flash.setVolume(this.flashId, newVolume);
};
// TODO remove unused .muteSound and .unmuteSound from Flash
 
p._calculateCurrentPosition = function() {
return s._flash.getPosition(this.flashId);
};
 
p._updatePosition = function() {
if(this.flashId == null) { return; }
s._flash.setPosition(this.flashId, this._position);
};
 
// Flash callbacks, only exist in FlashAudioPlugin
/**
* Called from Flash. Lets us know flash has finished playing a sound.
* #method handleSoundFinished
* @protected
*/
p.handleSoundFinished = function () {
this._loop = 0;
this._handleSoundComplete();
};
 
/**
* Called from Flash. Lets us know that flash has played a sound to completion and is looping it.
* #method handleSoundLoop
* @protected
*/
p.handleSoundLoop = function () {
this._loop--;
this._sendEvent("loop");
};
 
createjs.FlashAudioSoundInstance = createjs.promote(FlashAudioSoundInstance, "AbstractSoundInstance");
}());
 
//##############################################################################
// FlashAudioPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
"use strict";
 
/**
* 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
* plugin is recommended to be included if sound support is required in older browsers such as IE8.
*
* This plugin requires FlashAudioPlugin.swf and swfObject.js, which is compiled
* into the minified FlashAudioPlugin-X.X.X.min.js file. You must ensure that {{#crossLink "FlashAudioPlugin/swfPath:property"}}{{/crossLink}}
* is set when using this plugin, so that the script can find the swf.
*
* <h4>Example</h4>
*
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
* // Adds FlashAudioPlugin as a fallback if WebAudio and HTMLAudio do not work.
*
* Note that the SWF is embedded into a container DIV (with an id and classname of "SoundJSFlashContainer"), and
* will have an id of "flashAudioContainer". The container DIV is positioned 1 pixel off-screen to the left to avoid
* showing the 1x1 pixel white square.
*
* <h4>Known Browser and OS issues for Flash Audio</h4>
* <b>All browsers</b><br />
* <ul><li> There can be a delay in flash player starting playback of audio. This has been most noticeable in Firefox.
* Unfortunely this is an issue with the flash player and the browser and therefore cannot be addressed by SoundJS.</li></ul>
*
* @class FlashAudioPlugin
* @extends AbstractPlugin
* @constructor
*/
function FlashAudioPlugin() {
this.AbstractPlugin_constructor();
 
 
// Public Properties
/**
* A developer flag to output all flash events to the console (if it exists). Used for debugging.
*
* createjs.Sound.activePlugin.showOutput = true;
*
* @property showOutput
* @type {Boolean}
* @default false
*/
this.showOutput = false;
 
 
//Private Properties
/**
* The id name of the DIV that gets created for Flash content.
* @property _CONTAINER_ID
* @type {String}
* @default flashAudioContainer
* @protected
*/
this._CONTAINER_ID = "flashAudioContainer";
 
/**
* The id name of the DIV wrapper that contains the Flash content.
* @property _WRAPPER_ID
* @type {String}
* @default SoundJSFlashContainer
* @protected
* @since 0.4.1
*/
this._WRAPPER_ID = "SoundJSFlashContainer";
 
/**
* A reference to the DIV container that gets created to hold the Flash instance.
* @property _container
* @type {HTMLDivElement}
* @protected
*/
this._container = null,
 
/**
* A reference to the Flash instance that gets created.
* @property flash
* @type {Object | Embed}
* @protected
*/
this._flash = null;
 
/**
* Determines if the Flash object has been created and initialized. This is required to make <code>ExternalInterface</code>
* calls from JavaScript to Flash.
* @property flashReady
* @type {Boolean}
* @default false
*/
this.flashReady = false;
 
/**
* A hash of SoundInstances indexed by the related ID in Flash. This lookup is required to connect sounds in
* JavaScript to their respective instances in Flash.
* @property _flashInstances
* @type {Object}
* @protected
*/
this._flashInstances = {};
 
/**
* A hash of Sound Preload instances indexed by the related ID in Flash. This lookup is required to connect
* a preloading sound in Flash with its respective instance in JavaScript.
* @property _flashPreloadInstances
* @type {Object}
* @protected
*/
this._flashPreloadInstances = {};
//TODO consider combining _flashInstances and _flashPreloadInstances into a single hash
 
this._capabilities = s._capabilities;
 
this._loaderClass = createjs.FlashAudioLoader;
this._soundInstanceClass = createjs.FlashAudioSoundInstance;
 
// Create DIV
var w = this.wrapper = document.createElement("div");
w.id = this._WRAPPER_ID;
w.style.position = "absolute";
w.style.marginLeft = "-1px";
w.className = this._WRAPPER_ID;
document.body.appendChild(w);
 
// Create Placeholder
var c = this._container = document.createElement("div");
c.id = this._CONTAINER_ID;
c.appendChild(document.createTextNode("SoundJS Flash Container"));
w.appendChild(c);
 
var path = s.swfPath;
var val = swfobject.embedSWF(path + "FlashAudioPlugin.swf", this._CONTAINER_ID, "1", "1",
"9.0.0", null, null, {"AllowScriptAccess" : "always"}, null,
createjs.proxy(this._handleSWFReady, this)
);
};
 
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.
* @property _REG_FLASHID
* @type {String}
* @default registerflashid
* @static
* @protected
*/
s._REG_FLASHID = "registerflashid";
 
/**
* Event constant for the "unregisterFlashID" event for cleaner code.
* @property _UNREG_FLASHID
* @type {String}
* @default unregisterflashid
* @static
* @protected
*/
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
* capabilities.
* @property _capabilities
* @type {Object}
* @protected
* @static
*/
s._capabilities = null;
 
/**
* The path relative to the HTML page that the FlashAudioPlugin.swf resides. Note if this is not correct, this
* plugin will not work.
* @property swfPath
* @type {String}
* @default src/SoundJS
* @static
* @since 0.5.2
*/
s.swfPath = "src/soundjs/flashaudio/";
 
 
// Static Methods
/**
* Determine if the plugin can be used in the current browser/OS.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
// there is no flash player on mobile devices
if (createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry || createjs.BrowserDetect.isWindowsPhone) {return false;}
s._generateCapabilities();
if (swfobject == null) {return false;}
return swfobject.hasFlashPlayerVersion("9.0.0");
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
var c = s._capabilities = {
panning:true,
volume:true,
tracks:-1,
mp3:true,
ogg:false,
mpeg:true,
wav:true,
// our current implementation cannot support mp4 http://forums.adobe.com/thread/825408
m4a:false,
mp4:false,
aiff:false, // not listed in player but is Supported by Flash so this may be true
wma:false,
mid:false
};
};
 
 
//public methods
p.register = function (src, instances) {
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.removeAllSounds = function () {
this._flashInstances = {};
this._flashPreloadInstances = {};
// NOTE sound cannot be removed from a swf
 
this.AbstractPlugin_removeAllSounds();
};
 
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
si.on(s._REG_FLASHID, this.registerSoundInstance, this);
si.on(s._UNREG_FLASHID, this.unregisterSoundInstance, this);
return si;
};
 
p.toString = function () {
return "[FlashAudioPlugin]";
};
 
 
// private methods
/**
* The SWF used for sound preloading and playback has been initialized.
* @method _handleSWFReady
* @param {Object} event Contains a reference to the swf.
* @protected
*/
p._handleSWFReady = function (event) {
this._flash = event.ref;
};
 
/**
* The Flash application that handles preloading and playback is ready. We wait for a callback from Flash to
* ensure that everything is in place before playback begins.
* @method _handleFlashReady
* @protected
*/
p._handleFlashReady = function () {
this.flashReady = true;
 
this._loaderClass.setFlash(this._flash);
this._soundInstanceClass.setFlash(this._flash);
};
 
/**
* Internal function used to set the gain value for master audio. Should not be called externally.
* @method _updateVolume
* @return {Boolean}
* @protected
* @since 0.4.0
*/
p._updateVolume = function () {
var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
return this._flash.setMasterVolume(newVolume);
};
 
 
// Flash Communication
// Note we have decided not to include these in the docs
/*
* Used to couple a Flash loader instance with a <code>Loader</code> instance
* @method registerPreloadInstance
* @param {String} flashId Used to identify the Loader.
* @param {Loader} instance The actual instance.
*/
p.registerPreloadInstance = function (event) {
this._flashPreloadInstances[event.target.flashId] = event.target;
};
 
/*
* Used to decouple a <code>Loader</code> instance from Flash.
* @method unregisterPreloadInstance
* @param {String} flashId Used to identify the Loader.
*/
p.unregisterPreloadInstance = function (event) {
delete this._flashPreloadInstances[event.target.flashId];
};
 
/*
* Used to couple a Flash sound instance with a {{#crossLink "FlashAudioSoundInstance"}}{{/crossLink}}.
* @method registerSoundInstance
* @param {String} flashId Used to identify the FlashAudioSoundInstance.
* @param {Loader} instance The actual instance.
*/
p.registerSoundInstance = function (event) {
this._flashInstances[event.target.flashId] = event.target;
};
 
/*
* Used to decouple a {{#crossLink "FlashAudioSoundInstance"}}{{/crossLink}} from Flash.
* instance.
* @method unregisterSoundInstance
* @param {String} flashId Used to identify the FlashAudioSoundInstance.
* @param {Loader} instance The actual instance.
*/
p.unregisterSoundInstance = function (event) {
delete this._flashInstances[event.target.flashId];
};
 
/*
* Used to output traces from Flash to the console, if {{#crossLink "FlashAudioPlugin/showOutput"}}{{/crossLink}} is
* <code>true</code>.
* @method flashLog
* @param {String} data The information to be output.
*/
p.flashLog = function (data) {
try {
this.showOutput && console.log(data);
} catch (error) {
// older IE will cause error if console is not open
}
};
 
/*
* Handles events from Flash, and routes communication to a {{#crossLink "FlashAudioSoundInstance"}}{{/crossLink}} via
* the Flash ID. The method and arguments from Flash are run directly on the loader or sound instance.
* @method handleSoundEvent
* @param {String} flashId Used to identify the FlashAudioSoundInstance.
* @param {String} method Indicates the method to run.
*/
p.handleSoundEvent = function (flashId, method) {
var instance = this._flashInstances[flashId];
if (instance == null) {return;}
var args = [];
for (var i = 2, l = arguments.length; i < l; i++) {
args.push(arguments[i]);
}
try {
if (args.length == 0) {
instance[method]();
} else {
instance[method].apply(instance, args);
}
} catch (error) {
}
};
 
/*
* Handles events from Flash and routes communication to a <code>Loader</code> via the Flash ID. The method
* and arguments from Flash are run directly on the sound loader.
* @method handlePreloadEvent
* @param {String} flashId Used to identify the loader instance.
* @param {String} method Indicates the method to run.
*/
p.handlePreloadEvent = function (flashId, method) {
var instance = this._flashPreloadInstances[flashId];
if (instance == null) {
return;
}
var args = [];
for (var i = 2, l = arguments.length; i < l; i++) {
args.push(arguments[i]);
}
try {
if (args.length == 0) {
instance[method]();
} else {
instance[method].apply(instance, args);
}
} catch (error) {
}
};
 
/*
* Handles events from Flash intended for the FlashAudioPlugin class. Currently only a "ready" event is processed.
* @method handleEvent
* @param {String} method Indicates the method to run.
*/
p.handleEvent = function (method) {
switch (method) {
case "ready":
this._handleFlashReady();
break;
}
};
 
/*
* Handles error events from Flash. Note this function currently does not process any events.
* @method handleErrorEvent
* @param {String} error Indicates the error.
*/
p.handleErrorEvent = function (error) {
 
};
 
createjs.FlashAudioPlugin = createjs.promote(FlashAudioPlugin, "AbstractPlugin");
}());
 
//##############################################################################
// version_flashplugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
var s = createjs.FlashAudioPlugin = createjs.FlashAudioPlugin || {};
 
/**
* The version string for this release.
* @for FlashAudioPlugin
* @property version
* @type String
* @static
**/
s.version = /*=version*/"NEXT"; // injected by build process
 
/**
* The build date for this release in UTC format.
* @for FlashAudioPlugin
* @property buildDate
* @type String
* @static
**/
s.buildDate = /*=date*/"Mon, 14 Sep 2015 19:11:47 GMT"; // injected by build process
 
})();
/bower_components/SoundJS/lib/flashaudioplugin-NEXT.min.js
@@ -0,0 +1,20 @@
/*!
* @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/)
*/
 
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
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+='<param name="'+i+'" value="'+b[i]+'" />');e.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+f+">"+h+"</object>",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;d<c.length;d++)if(c[d].substring(0,c[d].indexOf("="))==a)return v(c[d].substring(c[d].indexOf("=")+1))}return""},expressInstallCallback:function(){if(S){var a=p(H);a&&w&&(a.parentNode.replaceChild(w,a),x&&(u(x,!0),U.ie&&U.win&&(w.style.display="block")),y&&y(z)),S=!1}}}}();this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!1,createjs.AbstractLoader.SOUND),this.flashId=null}var b=createjs.extend(a,createjs.AbstractLoader),c=a;c._flash=null,c._preloadInstances=[],c.setFlash=function(a){c._flash=a;for(var b=c._preloadInstances.length;b--;){var d=c._preloadInstances.pop();d.load()}},b.load=function(){if(null==c._flash)return void c._preloadInstances.push(this);this.flashId=c._flash.preload(this._item.src);var a=new createjs.Event(createjs.FlashAudioPlugin._REG_FLASHID);this.dispatchEvent(a)},b.handleProgress=function(a,b){this._sendProgress(a/b)},b.handleComplete=function(){this._result=this._item.src,this._sendComplete()},b.handleError=function(a){this._handleError(a)},b.destroy=function(){var a=new createjs.Event(createjs.FlashAudioPlugin._UNREG_FLASHID);this.dispatchEvent(a),this.AbstractLoader_destroy()},b.toString=function(){return"[FlashAudioLoader]"},createjs.FlashAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function FlashAudioSoundInstance(a,c,d,e){this.AbstractSoundInstance_constructor(a,c,d,e),this.flashId=null,null==b._flash&&b._instances.push(this)}var a=createjs.extend(FlashAudioSoundInstance,createjs.AbstractSoundInstance),b=FlashAudioSoundInstance;b._flash=null,b._instances=[],b.setFlash=function(a){b._flash=a;for(var c=b._instances.length;c--;){var d=b._instances.pop();d._setDurationFromSource()}},a.setLoop=function(a){null!=this.flashId&&b._flash.setLoop(this.flashId,a),this._loop=a},a.toString=function(){return"[FlashAudioSoundInstance]"},a._updateVolume=function(){null!=this.flashId&&b._flash.setVolume(this.flashId,this._volume)},a._updatePan=function(){null!=this.flashId&&b._flash.setPan(this.flashId,this._pan)},a._setDurationFromSource=function(){this._duration=b._flash.getDurationBySrc(this.src)},a._interrupt=function(){null!=this.flashId&&(b._flash.interrupt(this.flashId),this.AbstractSoundInstance__interrupt())},a._handleCleanUp=function(){b._flash.stopSound(this.flashId),this._sendEvent(createjs.FlashAudioPlugin._UNREG_FLASHID),this.flashId=null},a._beginPlaying=function(a){return null==b._flash?!1:(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)),this._paused=!1,this.flashId=b._flash.playSound(this.src,this._position,this._loop,this._volume,this._pan,this._startTime,this._duration),null==this.flashId?(this._playFailed(),!1):(this._muted&&this.setMute(!0),this._sendEvent(createjs.FlashAudioPlugin._REG_FLASHID),this.playState=createjs.Sound.PLAY_SUCCEEDED,this._sendEvent("succeeded"),!0))},a._pause=function(){null!=this.flashId&&(this._position=this._calculateCurrentPosition(),b._flash.pauseSound(this.flashId))},a._resume=function(){null!=this.flashId&&b._flash.resumeSound(this.flashId)},a._handleStop=function(){null!=this.flashId&&b._flash.stopSound(this.flashId)},a._updateVolume=function(){var a=this._muted?0:this._volume;b._flash.setVolume(this.flashId,a)},a._calculateCurrentPosition=function(){return b._flash.getPosition(this.flashId)},a._updatePosition=function(){null!=this.flashId&&b._flash.setPosition(this.flashId,this._position)},a.handleSoundFinished=function(){this._loop=0,this._handleSoundComplete()},a.handleSoundLoop=function(){this._loop--,this._sendEvent("loop")},createjs.FlashAudioSoundInstance=createjs.promote(FlashAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function FlashAudioPlugin(){this.AbstractPlugin_constructor(),this.showOutput=!1,this._CONTAINER_ID="flashAudioContainer",this._WRAPPER_ID="SoundJSFlashContainer",this._container=null,this._flash=null,this.flashReady=!1,this._flashInstances={},this._flashPreloadInstances={},this._capabilities=b._capabilities,this._loaderClass=createjs.FlashAudioLoader,this._soundInstanceClass=createjs.FlashAudioSoundInstance;var a=this.wrapper=document.createElement("div");a.id=this._WRAPPER_ID,a.style.position="absolute",a.style.marginLeft="-1px",a.className=this._WRAPPER_ID,document.body.appendChild(a);var c=this._container=document.createElement("div");c.id=this._CONTAINER_ID,c.appendChild(document.createTextNode("SoundJS Flash Container")),a.appendChild(c);{var d=b.swfPath;swfobject.embedSWF(d+"FlashAudioPlugin.swf",this._CONTAINER_ID,"1","1","9.0.0",null,null,{AllowScriptAccess:"always"},null,createjs.proxy(this._handleSWFReady,this))}}var a=createjs.extend(FlashAudioPlugin,createjs.AbstractPlugin),b=FlashAudioPlugin;b._REG_FLASHID="registerflashid",b._UNREG_FLASHID="unregisterflashid",b._capabilities=null,b.swfPath="src/soundjs/flashaudio/",b.isSupported=function(){return createjs.BrowserDetect.isIOS||createjs.BrowserDetect.isAndroid||createjs.BrowserDetect.isBlackberry||createjs.BrowserDetect.isWindowsPhone?!1:(b._generateCapabilities(),null==swfobject?!1:swfobject.hasFlashPlayerVersion("9.0.0"))},b._generateCapabilities=function(){if(null==b._capabilities){b._capabilities={panning:!0,volume:!0,tracks:-1,mp3:!0,ogg:!1,mpeg:!0,wav:!0,m4a:!1,mp4:!1,aiff:!1,wma:!1,mid:!1}}},a.register=function(a,c){var d=this.AbstractPlugin_register(a,c);return d.addEventListener(b._REG_FLASHID,createjs.proxy(this.registerPreloadInstance,this)),d.addEventListener(b._UNREG_FLASHID,createjs.proxy(this.unregisterPreloadInstance,this)),d},a.removeAllSounds=function(){this._flashInstances={},this._flashPreloadInstances={},this.AbstractPlugin_removeAllSounds()},a.create=function(a,c,d){var e=this.AbstractPlugin_create(a,c,d);return e.on(b._REG_FLASHID,this.registerSoundInstance,this),e.on(b._UNREG_FLASHID,this.unregisterSoundInstance,this),e},a.toString=function(){return"[FlashAudioPlugin]"},a._handleSWFReady=function(a){this._flash=a.ref},a._handleFlashReady=function(){this.flashReady=!0,this._loaderClass.setFlash(this._flash),this._soundInstanceClass.setFlash(this._flash)},a._updateVolume=function(){var a=createjs.Sound._masterMute?0:this._volume;return this._flash.setMasterVolume(a)},a.registerPreloadInstance=function(a){this._flashPreloadInstances[a.target.flashId]=a.target},a.unregisterPreloadInstance=function(a){delete this._flashPreloadInstances[a.target.flashId]},a.registerSoundInstance=function(a){this._flashInstances[a.target.flashId]=a.target},a.unregisterSoundInstance=function(a){delete this._flashInstances[a.target.flashId]},a.flashLog=function(a){try{this.showOutput&&console.log(a)}catch(b){}},a.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){}}},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(a){},createjs.FlashAudioPlugin=createjs.promote(FlashAudioPlugin,"AbstractPlugin")}(),this.createjs=this.createjs||{},function(){var a=createjs.FlashAudioPlugin=createjs.FlashAudioPlugin||{};a.version="NEXT",a.buildDate="Mon, 14 Sep 2015 19:11:47 GMT"}();
/bower_components/SoundJS/lib/soundjs-0.6.2.combined.js
@@ -0,0 +1,7949 @@
/*!
* SoundJS
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
 
 
//##############################################################################
// version.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
/**
* Static class holding library specific information such as the version and buildDate of the library.
* The SoundJS class has been renamed {{#crossLink "Sound"}}{{/crossLink}}. Please see {{#crossLink "Sound"}}{{/crossLink}}
* for information on using sound.
* @class SoundJS
**/
var s = createjs.SoundJS = createjs.SoundJS || {};
 
/**
* The version string for this release.
* @property version
* @type String
* @static
**/
s.version = /*=version*/"0.6.2"; // injected by build process
 
/**
* The build date for this release in UTC format.
* @property buildDate
* @type String
* @static
**/
s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:31 GMT"; // injected by build process
 
})();
 
//##############################################################################
// extend.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
 
/**
* Sets up the prototype chain and constructor property for a new class.
*
* This should be called right after creating the class constructor.
*
* function MySubClass() {}
* createjs.extend(MySubClass, MySuperClass);
* MySubClass.prototype.doSomething = function() { }
*
* var foo = new MySubClass();
* console.log(foo instanceof MySuperClass); // true
* console.log(foo.prototype.constructor === MySubClass); // true
*
* @method extend
* @param {Function} subclass The subclass.
* @param {Function} superclass The superclass to extend.
* @return {Function} Returns the subclass's new prototype.
*/
createjs.extend = function(subclass, superclass) {
"use strict";
 
function o() { this.constructor = subclass; }
o.prototype = superclass.prototype;
return (subclass.prototype = new o());
};
 
//##############################################################################
// promote.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
 
/**
* Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
* It is recommended to use the super class's name as the prefix.
* An alias to the super class's constructor is always added in the format `prefix_constructor`.
* This allows the subclass to call super class methods without using `function.call`, providing better performance.
*
* For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
* would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
* prototype of `MySubClass` as `MySuperClass_draw`.
*
* This should be called after the class's prototype is fully defined.
*
* function ClassA(name) {
* this.name = name;
* }
* ClassA.prototype.greet = function() {
* return "Hello "+this.name;
* }
*
* function ClassB(name, punctuation) {
* this.ClassA_constructor(name);
* this.punctuation = punctuation;
* }
* createjs.extend(ClassB, ClassA);
* ClassB.prototype.greet = function() {
* return this.ClassA_greet()+this.punctuation;
* }
* createjs.promote(ClassB, "ClassA");
*
* var foo = new ClassB("World", "!?!");
* console.log(foo.greet()); // Hello World!?!
*
* @method promote
* @param {Function} subclass The class to promote super class methods on.
* @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
* @return {Function} Returns the subclass.
*/
createjs.promote = function(subclass, prefix) {
"use strict";
 
var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
if (supP) {
subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
for (var n in supP) {
if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
}
}
return subclass;
};
 
//##############################################################################
// IndexOf.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
 
/**
* Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
* that value. Returns -1 if value is not found.
*
* var i = createjs.indexOf(myArray, myElementToFind);
*
* @method indexOf
* @param {Array} array Array to search for searchElement
* @param searchElement Element to find in array.
* @return {Number} The first index of searchElement in array.
*/
createjs.indexOf = function (array, searchElement){
"use strict";
 
for (var i = 0,l=array.length; i < l; i++) {
if (searchElement === array[i]) {
return i;
}
}
return -1;
};
 
//##############################################################################
// Proxy.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the
* createjs namespace directly.
*
* <h4>Example</h4>
*
* myObject.addEventListener("change", createjs.proxy(myMethod, scope));
*
* @class Utility Methods
* @main Utility Methods
*/
 
(function() {
"use strict";
 
/**
* A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a
* callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the
* method gets called in the correct scope.
*
* Additional arguments can be passed that will be applied to the function when it is called.
*
* <h4>Example</h4>
*
* myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2));
*
* function myHandler(arg1, arg2) {
* // This gets called when myObject.myCallback is executed.
* }
*
* @method proxy
* @param {Function} method The function to call
* @param {Object} scope The scope to call the method name on
* @param {mixed} [arg] * Arguments that are appended to the callback for additional params.
* @public
* @static
*/
createjs.proxy = function (method, scope) {
var aArgs = Array.prototype.slice.call(arguments, 2);
return function () {
return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs));
};
}
 
}());
 
//##############################################################################
// BrowserDetect.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
(function() {
"use strict";
 
/**
* An object that determines the current browser, version, operating system, and other environment
* variables via user agent string.
*
* Used for audio because feature detection is unable to detect the many limitations of mobile devices.
*
* <h4>Example</h4>
*
* if (createjs.BrowserDetect.isIOS) { // do stuff }
*
* @property BrowserDetect
* @type {Object}
* @param {Boolean} isFirefox True if our browser is Firefox.
* @param {Boolean} isOpera True if our browser is opera.
* @param {Boolean} isChrome True if our browser is Chrome. Note that Chrome for Android returns true, but is a
* completely different browser with different abilities.
* @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPod).
* @param {Boolean} isAndroid True if our browser is Android.
* @param {Boolean} isBlackberry True if our browser is Blackberry.
* @constructor
* @static
*/
function BrowserDetect() {
throw "BrowserDetect cannot be instantiated";
};
 
var agent = BrowserDetect.agent = window.navigator.userAgent;
BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1);
BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1);
BrowserDetect.isOpera = (window.opera != null);
BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); // NOTE that Chrome on Android returns true but is a completely different browser with different abilities
BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone;
BrowserDetect.isAndroid = (agent.indexOf("Android") > -1) && !BrowserDetect.isWindowPhone;
BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1);
 
createjs.BrowserDetect = BrowserDetect;
 
}());
 
//##############################################################################
// 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.
*
* <h4>Example</h4>
* 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");
* }
*
* <b>Maintaining proper scope</b><br />
* 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.
*
* <b>Browser support</b>
* 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;
 
/**
* <strong>REMOVED</strong>. 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.
*
* 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.
*
* <h4>Example</h4>
*
* 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`).
*
* <b>IMPORTANT:</b> 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.
*
* <h4>Example</h4>
*
* 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.
*
* <b>Important Note:</b> 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.
*
* <h4>Example</h4>
*
* 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; i<l; i++) {
if (arr[i] == listener) {
if (l==1) { delete(listeners[type]); } // allows for faster checks.
else { arr.splice(i,1); }
break;
}
}
};
/**
* A shortcut to the removeEventListener method, with the same parameters and return value. This is a companion to the
* .on method.
*
* <b>IMPORTANT:</b> 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.
*
* <h4>Example</h4>
*
* // 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.
*
* <h4>Example</h4>
*
* // 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; i<l && !eventObj.propagationStopped; i++) {
list[i]._dispatchEvent(eventObj, 3);
}
}
return !eventObj.defaultPrevented;
};
 
/**
* Indicates whether there is at least one listener for the specified event type.
* @method hasEventListener
* @param {String} type The string type of the event.
* @return {Boolean} Returns true if there is at least one listener for the specified event.
**/
p.hasEventListener = function(type) {
var listeners = this._listeners, captureListeners = this._captureListeners;
return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type]));
};
/**
* Indicates whether there is at least one listener for the specified event type on this object or any of its
* ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the
* specified type is dispatched from this object, it will trigger at least one listener.
*
* This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire
* event flow for a listener, not just this object.
* @method willTrigger
* @param {String} type The string type of the event.
* @return {Boolean} Returns `true` if there is at least one listener for the specified event.
**/
p.willTrigger = function(type) {
var o = this;
while (o) {
if (o.hasEventListener(type)) { return true; }
o = o.parent;
}
return false;
};
 
/**
* @method toString
* @return {String} a string representation of the instance.
**/
p.toString = function() {
return "[EventDispatcher]";
};
 
 
// private methods:
/**
* @method _dispatchEvent
* @param {Object | String | 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; }
try { eventObj.currentTarget = this; } catch (e) {}
try { eventObj.eventPhase = eventPhase; } catch (e) {}
eventObj.removed = false;
arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) {
var o = arr[i];
if (o.handleEvent) { o.handleEvent(eventObj); }
else { o(eventObj); }
if (eventObj.removed) {
this.off(eventObj.type, o, eventPhase==1);
eventObj.removed = false;
}
}
}
};
 
 
createjs.EventDispatcher = EventDispatcher;
}());
 
//##############################################################################
// Event.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
(function() {
"use strict";
 
// constructor:
/**
* Contains properties and methods shared by all events for use with
* {{#crossLink "EventDispatcher"}}{{/crossLink}}.
*
* Note that Event objects are often reused, so you should never
* rely on an event object's state outside of the call stack it was received in.
* @class Event
* @param {String} type The event type.
* @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
* @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
* @constructor
**/
function Event(type, bubbles, cancelable) {
// public properties:
/**
* The type of event.
* @property type
* @type String
**/
this.type = type;
/**
* The object that generated an event.
* @property target
* @type Object
* @default null
* @readonly
*/
this.target = null;
/**
* The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
* always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
* is generated from childObj, then a listener on parentObj would receive the event with
* target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
* @property currentTarget
* @type Object
* @default null
* @readonly
*/
this.currentTarget = null;
/**
* For bubbling events, this indicates the current event phase:<OL>
* <LI> capture phase: starting from the top parent to the target</LI>
* <LI> at target phase: currently being dispatched from the target</LI>
* <LI> bubbling phase: from the target to the top parent</LI>
* </OL>
* @property eventPhase
* @type Number
* @default 0
* @readonly
*/
this.eventPhase = 0;
/**
* Indicates whether the event will bubble through the display list.
* @property bubbles
* @type Boolean
* @default false
* @readonly
*/
this.bubbles = !!bubbles;
/**
* Indicates whether the default behaviour of this event can be cancelled via
* {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
* @property cancelable
* @type Boolean
* @default false
* @readonly
*/
this.cancelable = !!cancelable;
/**
* The epoch time at which this event was created.
* @property timeStamp
* @type Number
* @default 0
* @readonly
*/
this.timeStamp = (new Date()).getTime();
/**
* Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
* on this event.
* @property defaultPrevented
* @type Boolean
* @default false
* @readonly
*/
this.defaultPrevented = false;
/**
* Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
* {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
* @property propagationStopped
* @type Boolean
* @default false
* @readonly
*/
this.propagationStopped = false;
/**
* Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
* on this event.
* @property immediatePropagationStopped
* @type Boolean
* @default false
* @readonly
*/
this.immediatePropagationStopped = false;
/**
* Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
* @property removed
* @type Boolean
* @default false
* @readonly
*/
this.removed = false;
}
var p = Event.prototype;
 
/**
* <strong>REMOVED</strong>. 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 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() {
this.defaultPrevented = this.cancelable&&true;
};
 
/**
* Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
* Mirrors the DOM event standard.
* @method stopPropagation
**/
p.stopPropagation = function() {
this.propagationStopped = true;
};
 
/**
* Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
* {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
* Mirrors the DOM event standard.
* @method stopImmediatePropagation
**/
p.stopImmediatePropagation = function() {
this.immediatePropagationStopped = this.propagationStopped = true;
};
/**
* Causes the active listener to be removed via removeEventListener();
*
* myBtn.addEventListener("click", function(evt) {
* // do stuff...
* evt.remove(); // removes this listener.
* });
*
* @method remove
**/
p.remove = function() {
this.removed = true;
};
/**
* Returns a clone of the Event instance.
* @method clone
* @return {Event} a clone of the Event instance.
**/
p.clone = function() {
return new Event(this.type, this.bubbles, this.cancelable);
};
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
*
* @method set
* @param {Object} props A generic object containing properties to copy to the instance.
* @return {Event} Returns the instance the method is called on (useful for chaining calls.)
* @chainable
*/
p.set = function(props) {
for (var n in props) { this[n] = props[n]; }
return this;
};
 
/**
* Returns a string representation of this object.
* @method toString
* @return {String} a string representation of the instance.
**/
p.toString = function() {
return "[Event (type="+this.type+")]";
};
 
createjs.Event = Event;
}());
 
//##############################################################################
// ErrorEvent.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
(function() {
"use strict";
 
/**
* A general error {{#crossLink "Event"}}{{/crossLink}}, that describes an error that occurred, as well as any details.
* @class ErrorEvent
* @param {String} [title] The error title
* @param {String} [message] The error description
* @param {Object} [data] Additional error data
* @constructor
*/
function ErrorEvent(title, message, data) {
this.Event_constructor("error");
 
/**
* The short error title, which indicates the type of error that occurred.
* @property title
* @type String
*/
this.title = title;
 
/**
* The verbose error message, containing details about the error.
* @property message
* @type String
*/
this.message = message;
 
/**
* Additional data attached to an error.
* @property data
* @type {Object}
*/
this.data = data;
}
 
var p = createjs.extend(ErrorEvent, createjs.Event);
 
p.clone = function() {
return new createjs.ErrorEvent(this.title, this.message, this.data);
};
 
createjs.ErrorEvent = createjs.promote(ErrorEvent, "Event");
 
}());
 
//##############################################################################
// ProgressEvent.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function (scope) {
"use strict";
 
// constructor
/**
* A CreateJS {{#crossLink "Event"}}{{/crossLink}} that is dispatched when progress changes.
* @class ProgressEvent
* @param {Number} loaded The amount that has been loaded. This can be any number relative to the total.
* @param {Number} [total=1] The total amount that will load. This will default to 1, so if the `loaded` value is
* a percentage (between 0 and 1), it can be omitted.
* @todo Consider having this event be a "fileprogress" event as well
* @constructor
*/
function ProgressEvent(loaded, total) {
this.Event_constructor("progress");
 
/**
* The amount that has been loaded (out of a total amount)
* @property loaded
* @type {Number}
*/
this.loaded = loaded;
 
/**
* The total "size" of the load.
* @property total
* @type {Number}
* @default 1
*/
this.total = (total == null) ? 1 : total;
 
/**
* The percentage (out of 1) that the load has been completed. This is calculated using `loaded/total`.
* @property progress
* @type {Number}
* @default 0
*/
this.progress = (total == 0) ? 0 : this.loaded / this.total;
};
 
var p = createjs.extend(ProgressEvent, createjs.Event);
 
/**
* Returns a clone of the ProgressEvent instance.
* @method clone
* @return {ProgressEvent} a clone of the Event instance.
**/
p.clone = function() {
return new createjs.ProgressEvent(this.loaded, this.total);
};
 
createjs.ProgressEvent = createjs.promote(ProgressEvent, "Event");
 
}(window));
 
//##############################################################################
// LoadItem.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* All loaders accept an item containing the properties defined in this class. If a raw object is passed instead,
* it will not be affected, but it must contain at least a {{#crossLink "src:property"}}{{/crossLink}} property. A
* string path or HTML tag is also acceptable, but it will be automatically converted to a LoadItem using the
* {{#crossLink "create"}}{{/crossLink}} method by {{#crossLink "AbstractLoader"}}{{/crossLink}}
* @class LoadItem
* @constructor
* @since 0.6.0
*/
function LoadItem() {
/**
* The source of the file that is being loaded. This property is <b>required</b>. The source can either be a
* string (recommended), or an HTML tag.
* This can also be an object, but in that case it has to include a type and be handled by a plugin.
* @property src
* @type {String}
* @default null
*/
this.src = null;
 
/**
* The type file that is being loaded. The type of the file is usually inferred by the extension, but can also
* be set manually. This is helpful in cases where a file does not have an extension.
* @property type
* @type {String}
* @default null
*/
this.type = null;
 
/**
* A string identifier which can be used to reference the loaded object. If none is provided, this will be
* automatically set to the {{#crossLink "src:property"}}{{/crossLink}}.
* @property id
* @type {String}
* @default null
*/
this.id = null;
 
/**
* Determines if a manifest will maintain the order of this item, in relation to other items in the manifest
* that have also set the `maintainOrder` property to `true`. This only applies when the max connections has
* been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}). Everything with this
* property set to `false` will finish as it is loaded. Ordered items are combined with script tags loading in
* order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} is set to `true`.
* @property maintainOrder
* @type {Boolean}
* @default false
*/
this.maintainOrder = false;
 
/**
* A callback used by JSONP requests that defines what global method to call when the JSONP content is loaded.
* @property callback
* @type {String}
* @default null
*/
this.callback = null;
 
/**
* An arbitrary data object, which is included with the loaded object.
* @property data
* @type {Object}
* @default null
*/
this.data = null;
 
/**
* The request method used for HTTP calls. Both {{#crossLink "AbstractLoader/GET:property"}}{{/crossLink}} or
* {{#crossLink "AbstractLoader/POST:property"}}{{/crossLink}} request types are supported, and are defined as
* constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @property method
* @type {String}
* @default get
*/
this.method = createjs.LoadItem.GET;
 
/**
* An object hash of name/value pairs to send to the server.
* @property values
* @type {Object}
* @default null
*/
this.values = null;
 
/**
* An object hash of headers to attach to an XHR request. PreloadJS will automatically attach some default
* headers when required, including "Origin", "Content-Type", and "X-Requested-With". You may override the
* default headers by including them in your headers object.
* @property headers
* @type {Object}
* @default null
*/
this.headers = null;
 
/**
* Enable credentials for XHR requests.
* @property withCredentials
* @type {Boolean}
* @default false
*/
this.withCredentials = false;
 
/**
* Set the mime type of XHR-based requests. This is automatically set to "text/plain; charset=utf-8" for text
* based files (json, xml, text, css, js).
* @property mimeType
* @type {String}
* @default null
*/
this.mimeType = null;
 
/**
* Sets the crossOrigin attribute for CORS-enabled images loading cross-domain.
* @property crossOrigin
* @type {boolean}
* @default Anonymous
*/
this.crossOrigin = null;
 
/**
* The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
* (level one) loading, as XHR (level 2) provides its own timeout event.
* @property loadTimeout
* @type {Number}
* @default 8000 (8 seconds)
*/
this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
};
 
var p = LoadItem.prototype = {};
var s = LoadItem;
 
/**
* Default duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
* (level one) loading, as XHR (level 2) provides its own timeout event.
* @property LOAD_TIMEOUT_DEFAULT
* @type {number}
* @static
*/
s.LOAD_TIMEOUT_DEFAULT = 8000;
 
/**
* Create a LoadItem.
* <ul>
* <li>String-based items are converted to a LoadItem with a populated {{#crossLink "src:property"}}{{/crossLink}}.</li>
* <li>LoadItem instances are returned as-is</li>
* <li>Objects are returned with any needed properties added</li>
* </ul>
* @method create
* @param {LoadItem|String|Object} value The load item value
* @returns {LoadItem|Object}
* @static
*/
s.create = function (value) {
if (typeof value == "string") {
var item = new LoadItem();
item.src = value;
return item;
} else if (value instanceof s) {
return value;
} else if (value instanceof Object && value.src) {
if (value.loadTimeout == null) {
value.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
}
return value;
} else {
throw new Error("Type not recognized.");
}
};
 
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
*
* <h4>Example</h4>
*
* var loadItem = new createjs.LoadItem().set({src:"image.png", maintainOrder:true});
*
* @method set
* @param {Object} props A generic object containing properties to copy to the LoadItem instance.
* @return {LoadItem} Returns the instance the method is called on (useful for chaining calls.)
*/
p.set = function(props) {
for (var n in props) { this[n] = props[n]; }
return this;
};
 
createjs.LoadItem = s;
 
}());
 
//##############################################################################
// RequestUtils.js
//##############################################################################
 
(function () {
 
/**
* Utilities that assist with parsing load items, and determining file types, etc.
* @class RequestUtils
*/
var s = {};
 
/**
* The Regular Expression used to test file URLS for an absolute path.
* @property ABSOLUTE_PATH
* @type {RegExp}
* @static
*/
s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
 
/**
* The Regular Expression used to test file URLS for a relative path.
* @property RELATIVE_PATH
* @type {RegExp}
* @static
*/
s.RELATIVE_PATT = (/^[./]*?\//i);
 
/**
* The Regular Expression used to test file URLS for an extension. Note that URIs must already have the query string
* removed.
* @property EXTENSION_PATT
* @type {RegExp}
* @static
*/
s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
 
/**
* Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
* <ul>
* <li>If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
* `//networkPath`)</li>
* <li>If the path is relative. Relative paths start with `../` or `/path` (or similar)</li>
* <li>The file extension. This is determined by the filename with an extension. Query strings are dropped, and
* the file path is expected to follow the format `name.ext`.</li>
* </ul>
* @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 <b>not</b> a binary type, as we can not play
* back using an audio tag if it is loaded as binary. Plugins can change the item type to binary to ensure they get
* a binary result to work with. Binary files are loaded using XHR2. Types are defined as static constants on
* {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @method isBinary
* @param {String} type The item type.
* @return {Boolean} If the specified type is binary.
* @static
*/
s.isBinary = function (type) {
switch (type) {
case createjs.AbstractLoader.IMAGE:
case createjs.AbstractLoader.BINARY:
return true;
default:
return false;
}
};
 
/**
* Check if item is a valid HTMLImageElement
* @method isImageTag
* @param {Object} item
* @returns {Boolean}
* @static
*/
s.isImageTag = function(item) {
return item instanceof HTMLImageElement;
};
 
/**
* Check if item is a valid HTMLAudioElement
* @method isAudioTag
* @param {Object} item
* @returns {Boolean}
* @static
*/
s.isAudioTag = function(item) {
if (window.HTMLAudioElement) {
return item instanceof HTMLAudioElement;
} else {
return false;
}
};
 
/**
* Check if item is a valid HTMLVideoElement
* @method isVideoTag
* @param {Object} item
* @returns {Boolean}
* @static
*/
s.isVideoTag = function(item) {
if (window.HTMLVideoElement) {
return item instanceof HTMLVideoElement;
} else {
return false;
}
};
 
/**
* Determine if a specific type is a text-based asset, and should be loaded as UTF-8.
* @method isText
* @param {String} type The item type.
* @return {Boolean} If the specified type is text.
* @static
*/
s.isText = function (type) {
switch (type) {
case createjs.AbstractLoader.TEXT:
case createjs.AbstractLoader.JSON:
case createjs.AbstractLoader.MANIFEST:
case createjs.AbstractLoader.XML:
case createjs.AbstractLoader.CSS:
case createjs.AbstractLoader.SVG:
case createjs.AbstractLoader.JAVASCRIPT:
case createjs.AbstractLoader.SPRITESHEET:
return true;
default:
return false;
}
};
 
/**
* Determine the type of the object using common extensions. Note that the type can be passed in with the load item
* if it is an unusual extension.
* @method getTypeByExtension
* @param {String} extension The file extension to use to determine the load type.
* @return {String} The determined load type (for example, <code>AbstractLoader.IMAGE</code>). Will return `null` if
* the type can not be determined by the extension.
* @static
*/
s.getTypeByExtension = function (extension) {
if (extension == null) {
return createjs.AbstractLoader.TEXT;
}
 
switch (extension.toLowerCase()) {
case "jpeg":
case "jpg":
case "gif":
case "png":
case "webp":
case "bmp":
return createjs.AbstractLoader.IMAGE;
case "ogg":
case "mp3":
case "webm":
return createjs.AbstractLoader.SOUND;
case "mp4":
case "webm":
case "ts":
return createjs.AbstractLoader.VIDEO;
case "json":
return createjs.AbstractLoader.JSON;
case "xml":
return createjs.AbstractLoader.XML;
case "css":
return createjs.AbstractLoader.CSS;
case "js":
return createjs.AbstractLoader.JAVASCRIPT;
case 'svg':
return createjs.AbstractLoader.SVG;
default:
return createjs.AbstractLoader.TEXT;
}
};
 
createjs.RequestUtils = s;
 
}());
 
//##############################################################################
// AbstractLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* The base loader, which defines all the generic methods, properties, and events. All loaders extend this class,
* including the {{#crossLink "LoadQueue"}}{{/crossLink}}.
* @class AbstractLoader
* @param {LoadItem|object|string} loadItem The item to be loaded.
* @param {Boolean} [preferXHR] Determines if the LoadItem should <em>try</em> and load using XHR, or take a
* tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the
* other, so this is a suggested directive.
* @param {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
* such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc.
* @extends EventDispatcher
*/
function AbstractLoader(loadItem, preferXHR, type) {
this.EventDispatcher_constructor();
 
// public properties
/**
* If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
* used for loading do not pile up resulting in more than one `complete` {{#crossLink "Event"}}{{/crossLink}}.
* @property loaded
* @type {Boolean}
* @default false
*/
this.loaded = false;
 
/**
* Determine if the loader was canceled. Canceled loads will not fire complete events. Note that this property
* is readonly, so {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "LoadQueue/close"}}{{/crossLink}}
* instead.
* @property canceled
* @type {Boolean}
* @default false
* @readonly
*/
this.canceled = false;
 
/**
* The current load progress (percentage) for this item. This will be a number between 0 and 1.
*
* <h4>Example</h4>
*
* var queue = new createjs.LoadQueue();
* queue.loadFile("largeImage.png");
* queue.on("progress", function() {
* console.log("Progress:", queue.progress, event.progress);
* });
*
* @property progress
* @type {Number}
* @default 0
*/
this.progress = 0;
 
/**
* The type of item this loader will load. See {{#crossLink "AbstractLoader"}}{{/crossLink}} for a full list of
* supported types.
* @property type
* @type {String}
*/
this.type = type;
 
/**
* A formatter function that converts the loaded raw result into the final result. For example, the JSONLoader
* converts a string of text into a JavaScript object. Not all loaders have a resultFormatter, and this property
* can be overridden to provide custom formatting.
*
* Optionally, a resultFormatter can return a callback function in cases where the formatting needs to be
* asynchronous, such as creating a new image. 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.
*
* <h4>Example asynchronous resultFormatter</h4>
*
* function _formatResult(loader) {
* return function(success, error) {
* if (errorCondition) { error(errorDetailEvent); }
* success(result);
* }
* }
* @property resultFormatter
* @type {Function}
* @default null
*/
this.resultFormatter = null;
 
// protected properties
/**
* The {{#crossLink "LoadItem"}}{{/crossLink}} this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}},
* but will be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}.
* @property _item
* @type {LoadItem|Object}
* @private
*/
if (loadItem) {
this._item = createjs.LoadItem.create(loadItem);
} else {
this._item = null;
}
 
/**
* Whether the loader will try and load content using XHR (true) or HTML tags (false).
* @property _preferXHR
* @type {Boolean}
* @private
*/
this._preferXHR = preferXHR;
 
/**
* The loaded result after it is formatted by an optional {{#crossLink "resultFormatter"}}{{/crossLink}}. For
* items that are not formatted, this will be the same as the {{#crossLink "_rawResult:property"}}{{/crossLink}}.
* The result is accessed using the {{#crossLink "getResult"}}{{/crossLink}} method.
* @property _result
* @type {Object|String}
* @private
*/
this._result = null;
 
/**
* The loaded result before it is formatted. The rawResult is accessed using the {{#crossLink "getResult"}}{{/crossLink}}
* method, and passing `true`.
* @property _rawResult
* @type {Object|String}
* @private
*/
this._rawResult = null;
 
/**
* A list of items that loaders load behind the scenes. This does not include the main item the loader is
* responsible for loading. Examples of loaders that have sub-items include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and
* {{#crossLink "ManifestLoader"}}{{/crossLink}}.
* @property _loadItems
* @type {null}
* @protected
*/
this._loadedItems = null;
 
/**
* The attribute the items loaded using tags use for the source.
* @type {string}
* @default null
* @private
*/
this._tagSrcAttribute = null;
 
/**
* An HTML tag (or similar) that a loader may use to load HTML content, such as images, scripts, etc.
* @property _tag
* @type {Object}
* @private
*/
this._tag = null;
};
 
var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
var s = AbstractLoader;
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
/**
* Defines a POST request, use for a method value when loading data.
* @property POST
* @type {string}
* @default post
* @static
*/
s.POST = "POST";
 
/**
* Defines a GET request, use for a method value when loading data.
* @property GET
* @type {string}
* @default get
* @static
*/
s.GET = "GET";
 
/**
* The preload type for generic binary types. Note that images are loaded as binary files when using XHR.
* @property BINARY
* @type {String}
* @default binary
* @static
* @since 0.6.0
*/
s.BINARY = "binary";
 
/**
* The preload type for css files. CSS files are loaded using a &lt;link&gt; when loaded with XHR, or a
* &lt;style&gt; 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 &lt;image&gt; 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
* &lt;script&gt; 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
* &lt;audio&gt; 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
* &lt;video&gt; tag.
* @property VIDEO
* @type {String}
* @default video
* @static
* @since 0.6.0
*/
s.VIDEO = "video";
 
/**
* The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths.
* @property SPRITESHEET
* @type {String}
* @default spritesheet
* @static
* @since 0.6.0
*/
s.SPRITESHEET = "spritesheet";
 
/**
* The preload type for SVG files.
* @property SVG
* @type {String}
* @default svg
* @static
* @since 0.6.0
*/
s.SVG = "svg";
 
/**
* The preload type for text files, which is also the default file type if the type can not be determined. Text is
* loaded as raw text.
* @property TEXT
* @type {String}
* @default text
* @static
* @since 0.6.0
*/
s.TEXT = "text";
 
/**
* The preload type for xml files. XML is loaded into an XML document.
* @property XML
* @type {String}
* @default xml
* @static
* @since 0.6.0
*/
s.XML = "xml";
 
// Events
/**
* The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to
* version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}.
* @event progress
* @since 0.3.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts.
* @event loadstart
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.3.1
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded.
* @event complete
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.3.0
*/
 
/**
* The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was
* encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was
* just a regular {{#crossLink "Event"}}{{/crossLink}}.
* @event error
* @since 0.3.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error.
* This enables loaders to maintain internal queues, and surface file load errors.
* @event fileerror
* @param {Object} target The object that dispatched the event.
* @param {String} type The even type ("fileerror")
* @param {LoadItem|object} The item that encountered the error
* @since 0.6.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables
* loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s
* and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a
* slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event.
* @event fileload
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type ("fileload")
* @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
* object will contain that value as a `src` property.
* @param {Object} result The HTML tag or parsed result of the loaded item.
* @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
* to a usable object.
* @since 0.6.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load.
* This allows updates to the loader for specific loading needs, such as binary or XHR image loading.
* @event initialize
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type ("initialize")
* @param {AbstractLoader} loader The loader that has been initialized.
*/
 
 
/**
* Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was
* passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
* {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will
* be a {{#crossLink "LoadItem"}}{{/crossLink}}.
* @method getItem
* @return {Object} The manifest item that this loader is responsible for loading.
* @since 0.6.0
*/
p.getItem = function () {
return this._item;
};
 
/**
* Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}}
* event is dispatched.
* @method getResult
* @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded
* data (if it exists).
* @return {Object}
* @since 0.6.0
*/
p.getResult = function (raw) {
return raw ? this._rawResult : this._result;
};
 
/**
* Return the `tag` this object creates or uses for loading.
* @method getTag
* @return {Object} The tag instance
* @since 0.6.0
*/
p.getTag = function () {
return this._tag;
};
 
/**
* Set the `tag` this item uses for loading.
* @method setTag
* @param {Object} tag The tag instance
* @since 0.6.0
*/
p.setTag = function(tag) {
this._tag = tag;
};
 
/**
* Begin loading the item. This method is required when using a loader by itself.
*
* <h4>Example</h4>
*
* var queue = new createjs.LoadQueue();
* queue.on("complete", handleComplete);
* queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet
* queue.load();
*
* @method load
*/
p.load = function () {
this._createRequest();
 
this._request.on("complete", this, this);
this._request.on("progress", this, this);
this._request.on("loadStart", this, this);
this._request.on("abort", this, this);
this._request.on("timeout", this, this);
this._request.on("error", this, this);
 
var evt = new createjs.Event("initialize");
evt.loader = this._request;
this.dispatchEvent(evt);
 
this._request.load();
};
 
/**
* Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in
* the background), but events will not longer be dispatched.
* @method cancel
*/
p.cancel = function () {
this.canceled = true;
this.destroy();
};
 
/**
* Clean up the loader.
* @method destroy
*/
p.destroy = function() {
if (this._request) {
this._request.removeAllEventListeners();
this._request.destroy();
}
 
this._request = null;
 
this._item = null;
this._rawResult = null;
this._result = null;
 
this._loadItems = null;
 
this.removeAllEventListeners();
};
 
/**
* Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}}
* to expose items it loads internally.
* @method getLoadedItems
* @return {Array} A list of the items loaded by the loader.
* @since 0.6.0
*/
p.getLoadedItems = function () {
return this._loadedItems;
};
 
 
// Private methods
/**
* Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or
* {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}.
* Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}},
* which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood.
* @method _createRequest
* @protected
*/
p._createRequest = function() {
if (!this._preferXHR) {
this._request = new createjs.TagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
} else {
this._request = new createjs.XHRRequest(this._item);
}
};
 
/**
* Create the HTML tag used for loading. This method does nothing by default, and needs to be implemented
* by loaders that require tag loading.
* @method _createTag
* @param {String} src The tag source
* @return {HTMLElement} The tag that was created
* @protected
*/
p._createTag = function(src) { return null; };
 
/**
* Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendLoadStart
* @protected
*/
p._sendLoadStart = function () {
if (this._isCanceled()) { return; }
this.dispatchEvent("loadstart");
};
 
/**
* Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}.
* @method _sendProgress
* @param {Number | Object} value The progress of the loaded item, or an object containing <code>loaded</code>
* and <code>total</code> properties.
* @protected
*/
p._sendProgress = function (value) {
if (this._isCanceled()) { return; }
var event = null;
if (typeof(value) == "number") {
this.progress = value;
event = new createjs.ProgressEvent(this.progress);
} else {
event = value;
this.progress = value.loaded / value.total;
event.progress = this.progress;
if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
}
this.hasEventListener("progress") && this.dispatchEvent(event);
};
 
/**
* Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
* @method _sendComplete
* @protected
*/
p._sendComplete = function () {
if (this._isCanceled()) { return; }
 
this.loaded = true;
 
var event = new createjs.Event("complete");
event.rawResult = this._rawResult;
 
if (this._result != null) {
event.result = this._result;
}
 
this.dispatchEvent(event);
};
 
/**
* Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendError
* @param {ErrorEvent} event The event object containing specific error properties.
* @protected
*/
p._sendError = function (event) {
if (this._isCanceled() || !this.hasEventListener("error")) { return; }
if (event == null) {
event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
}
this.dispatchEvent(event);
};
 
/**
* Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
* do not cause issues after the queue has been cleaned up.
* @method _isCanceled
* @return {Boolean} If the loader has been canceled.
* @protected
*/
p._isCanceled = function () {
if (window.createjs == null || this.canceled) {
return true;
}
return false;
};
 
/**
* A custom result formatter function, which is called just before a request dispatches its complete event. Most
* loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
* formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
* @property resultFormatter
* @type Function
* @return {Object} The formatted result
* @since 0.6.0
*/
p.resultFormatter = null;
 
/**
* Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but
* this method can be overridden for custom behaviours.
* @method handleEvent
* @param {Event} event The event that the internal request dispatches.
* @protected
* @since 0.6.0
*/
p.handleEvent = function (event) {
switch (event.type) {
case "complete":
this._rawResult = event.target._response;
var result = this.resultFormatter && this.resultFormatter(this);
if (result instanceof Function) {
result.call(this,
createjs.proxy(this._resultFormatSuccess, this),
createjs.proxy(this._resultFormatFailed, this)
);
} else {
this._result = result || this._rawResult;
this._sendComplete();
}
break;
case "progress":
this._sendProgress(event);
break;
case "error":
this._sendError(event);
break;
case "loadstart":
this._sendLoadStart();
break;
case "abort":
case "timeout":
if (!this._isCanceled()) {
this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_" + event.type.toUpperCase() + "_ERROR"));
}
break;
}
};
 
/**
* 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._resultFormatFailed = function (event) {
this._sendError(event);
};
 
/**
* @method buildPath
* @protected
* @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
* instead.
*/
p.buildPath = function (src, data) {
return createjs.RequestUtils.buildPath(src, data);
};
 
/**
* @method toString
* @return {String} a string representation of the instance.
*/
p.toString = function () {
return "[PreloadJS AbstractLoader]";
};
 
createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher");
 
}());
 
//##############################################################################
// AbstractMediaLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* The AbstractMediaLoader is a base class that handles some of the shared methods and properties of loaders that
* handle HTML media elements, such as Video and Audio.
* @class AbstractMediaLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @param {String} type The type of media to load. Usually "video" or "audio".
* @extends AbstractLoader
* @constructor
*/
function AbstractMediaLoader(loadItem, preferXHR, type) {
this.AbstractLoader_constructor(loadItem, preferXHR, type);
 
// public properties
this.resultFormatter = this._formatResult;
 
// protected properties
this._tagSrcAttribute = "src";
 
this.on("initialize", this._updateXHR, this);
};
 
var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
 
// static properties
// public methods
p.load = function () {
// TagRequest will handle most of this, but Sound / Video need a few custom properties, so just handle them here.
if (!this._tag) {
this._tag = this._createTag(this._item.src);
}
 
this._tag.preload = "auto";
this._tag.load();
 
this.AbstractLoader_load();
};
 
// protected methods
/**
* Creates a new tag for loading if it doesn't exist yet.
* @method _createTag
* @private
*/
p._createTag = function () {};
 
 
p._createRequest = function() {
if (!this._preferXHR) {
this._request = new createjs.MediaTagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
} else {
this._request = new createjs.XHRRequest(this._item);
}
};
 
// 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
* @param {AbstractLoader} loader
* @returns {HTMLVideoElement|HTMLAudioElement}
* @private
*/
p._formatResult = function (loader) {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.onstalled = null;
if (this._preferXHR) {
var URL = window.URL || window.webkitURL;
var result = loader.getResult(true);
 
loader.getTag().src = URL.createObjectURL(result);
}
return loader.getTag();
};
 
createjs.AbstractMediaLoader = createjs.promote(AbstractMediaLoader, "AbstractLoader");
 
}());
 
//##############################################################################
// AbstractRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* A base class for actual data requests, such as {{#crossLink "XHRRequest"}}{{/crossLink}}, {{#crossLink "TagRequest"}}{{/crossLink}},
* and {{#crossLink "MediaRequest"}}{{/crossLink}}. PreloadJS loaders will typically use a data loader under the
* hood to get data.
* @class AbstractRequest
* @param {LoadItem} item
* @constructor
*/
var AbstractRequest = function (item) {
this._item = item;
};
 
var p = createjs.extend(AbstractRequest, createjs.EventDispatcher);
 
// public methods
/**
* Begin a load.
* @method load
*/
p.load = function() {};
 
/**
* Clean up a request.
* @method destroy
*/
p.destroy = function() {};
 
/**
* Cancel an in-progress request.
* @method cancel
*/
p.cancel = function() {};
 
createjs.AbstractRequest = createjs.promote(AbstractRequest, "EventDispatcher");
 
}());
 
//##############################################################################
// TagRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* An {{#crossLink "AbstractRequest"}}{{/crossLink}} that loads HTML tags, such as images and scripts.
* @class TagRequest
* @param {LoadItem} loadItem
* @param {HTMLElement} tag
* @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
*/
function TagRequest(loadItem, tag, srcAttribute) {
this.AbstractRequest_constructor(loadItem);
 
// protected properties
/**
* The HTML tag instance that is used to load.
* @property _tag
* @type {HTMLElement}
* @protected
*/
this._tag = tag;
 
/**
* The tag attribute that specifies the source, such as "src", "href", etc.
* @property _tagSrcAttribute
* @type {String}
* @protected
*/
this._tagSrcAttribute = srcAttribute;
 
/**
* A method closure used for handling the tag load event.
* @property _loadedHandler
* @type {Function}
* @private
*/
this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
 
/**
* Determines if the element was added to the DOM automatically by PreloadJS, so it can be cleaned up after.
* @property _addedToDOM
* @type {Boolean}
* @private
*/
this._addedToDOM = false;
 
/**
* Determines what the tags initial style.visibility was, so we can set it correctly after a load.
*
* @type {null}
* @private
*/
this._startTagVisibility = null;
};
 
var p = createjs.extend(TagRequest, createjs.AbstractRequest);
 
// public methods
p.load = function () {
this._tag.onload = createjs.proxy(this._handleTagComplete, this);
this._tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this);
this._tag.onerror = createjs.proxy(this._handleError, this);
 
var evt = new createjs.Event("initialize");
evt.loader = this._tag;
 
this.dispatchEvent(evt);
 
this._hideTag();
 
this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
 
this._tag[this._tagSrcAttribute] = this._item.src;
 
// wdg:: Append the tag AFTER setting the src, or SVG loading on iOS will fail.
if (this._tag.parentNode == null) {
window.document.body.appendChild(this._tag);
this._addedToDOM = true;
}
};
 
p.destroy = function() {
this._clean();
this._tag = null;
 
this.AbstractRequest_destroy();
};
 
// private methods
/**
* Handle the readyStateChange event from a tag. We need this in place of the `onload` callback (mainly SCRIPT
* and LINK tags), but other cases may exist.
* @method _handleReadyStateChange
* @private
*/
p._handleReadyStateChange = function () {
clearTimeout(this._loadTimeout);
// This is strictly for tags in browsers that do not support onload.
var tag = this._tag;
 
// Complete is for old IE support.
if (tag.readyState == "loaded" || tag.readyState == "complete") {
this._handleTagComplete();
}
};
 
/**
* Handle any error events from the tag.
* @method _handleError
* @protected
*/
p._handleError = function() {
this._clean();
this.dispatchEvent("error");
};
 
/**
* Handle the tag's onload callback.
* @method _handleTagComplete
* @private
*/
p._handleTagComplete = function () {
this._rawResult = this._tag;
this._result = this.resultFormatter && this.resultFormatter(this) || this._rawResult;
 
this._clean();
this._showTag();
 
this.dispatchEvent("complete");
};
 
/**
* The tag request has not loaded within the time specified in loadTimeout.
* @method _handleError
* @param {Object} event The XHR error event.
* @private
*/
p._handleTimeout = function () {
this._clean();
this.dispatchEvent(new createjs.Event("timeout"));
};
 
/**
* Remove event listeners, but don't destroy the request object
* @method _clean
* @private
*/
p._clean = function() {
this._tag.onload = null;
this._tag.onreadystatechange = null;
this._tag.onerror = null;
if (this._addedToDOM && this._tag.parentNode != null) {
this._tag.parentNode.removeChild(this._tag);
}
clearTimeout(this._loadTimeout);
};
 
p._hideTag = function() {
this._startTagVisibility = this._tag.style.visibility;
this._tag.style.visibility = "hidden";
};
 
p._showTag = function() {
this._tag.style.visibility = this._startTagVisibility;
};
 
/**
* Handle a stalled audio event. The main place this happens is with HTMLAudio in Chrome when playing back audio
* that is already in a load, but not complete.
* @method _handleStalled
* @private
*/
p._handleStalled = function () {
//Ignore, let the timeout take care of it. Sometimes its not really stopped.
};
 
createjs.TagRequest = createjs.promote(TagRequest, "AbstractRequest");
 
}());
 
//##############################################################################
// MediaTagRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* An {{#crossLink "TagRequest"}}{{/crossLink}} that loads HTML tags for video and audio.
* @class MediaTagRequest
* @param {LoadItem} loadItem
* @param {HTMLAudioElement|HTMLVideoElement} tag
* @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
* @constructor
*/
function MediaTagRequest(loadItem, tag, srcAttribute) {
this.AbstractRequest_constructor(loadItem);
 
// protected properties
this._tag = tag;
this._tagSrcAttribute = srcAttribute;
this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
};
 
var p = createjs.extend(MediaTagRequest, createjs.TagRequest);
var s = MediaTagRequest;
 
// public methods
p.load = function () {
var sc = createjs.proxy(this._handleStalled, this);
this._stalledCallback = sc;
 
var pc = createjs.proxy(this._handleProgress, this);
this._handleProgress = pc;
 
this._tag.addEventListener("stalled", sc);
this._tag.addEventListener("progress", pc);
 
// This will tell us when audio is buffered enough to play through, but not when its loaded.
// The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient.
this._tag.addEventListener && this._tag.addEventListener("canplaythrough", this._loadedHandler, false); // canplaythrough callback doesn't work in Chrome, so we use an event.
 
this.TagRequest_load();
};
 
// private methods
p._handleReadyStateChange = function () {
clearTimeout(this._loadTimeout);
// This is strictly for tags in browsers that do not support onload.
var tag = this._tag;
 
// Complete is for old IE support.
if (tag.readyState == "loaded" || tag.readyState == "complete") {
this._handleTagComplete();
}
};
 
p._handleStalled = function () {
//Ignore, let the timeout take care of it. Sometimes its not really stopped.
};
 
/**
* An XHR request has reported progress.
* @method _handleProgress
* @param {Object} event The XHR progress event.
* @private
*/
p._handleProgress = function (event) {
if (!event || event.loaded > 0 && event.total == 0) {
return; // Sometimes we get no "total", so just ignore the progress event.
}
 
var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
this.dispatchEvent(newEvent);
};
 
// protected methods
p._clean = function () {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.removeEventListener("stalled", this._stalledCallback);
this._tag.removeEventListener("progress", this._progressCallback);
 
this.TagRequest__clean();
};
 
createjs.MediaTagRequest = createjs.promote(MediaTagRequest, "TagRequest");
 
}());
 
//##############################################################################
// XHRRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* A preloader that loads items using XHR requests, usually XMLHttpRequest. However XDomainRequests will be used
* for cross-domain requests if possible, and older versions of IE fall back on to ActiveX objects when necessary.
* XHR requests load the content as text or binary data, provide progress and consistent completion events, and
* can be canceled during load. Note that XHR is not supported in IE 6 or earlier, and is not recommended for
* cross-domain loading.
* @class XHRRequest
* @constructor
* @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* for an overview of supported file properties.
* @extends AbstractLoader
*/
function XHRRequest (item) {
this.AbstractRequest_constructor(item);
 
// protected properties
/**
* A reference to the XHR request used to load the content.
* @property _request
* @type {XMLHttpRequest | XDomainRequest | ActiveX.XMLHTTP}
* @private
*/
this._request = null;
 
/**
* A manual load timeout that is used for browsers that do not support the onTimeout event on XHR (XHR level 1,
* typically IE9).
* @property _loadTimeout
* @type {Number}
* @private
*/
this._loadTimeout = null;
 
/**
* The browser's XHR (XMLHTTPRequest) version. Supported versions are 1 and 2. There is no official way to detect
* the version, so we use capabilities to make a best guess.
* @property _xhrLevel
* @type {Number}
* @default 1
* @private
*/
this._xhrLevel = 1;
 
/**
* The response of a loaded file. This is set because it is expensive to look up constantly. This property will be
* null until the file is loaded.
* @property _response
* @type {mixed}
* @private
*/
this._response = null;
 
/**
* The response of the loaded file before it is modified. In most cases, content is converted from raw text to
* an HTML tag or a formatted object which is set to the <code>result</code> property, but the developer may still
* want to access the raw content as it was loaded.
* @property _rawResponse
* @type {String|Object}
* @private
*/
this._rawResponse = null;
 
this._canceled = false;
 
// Setup our event handlers now.
this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this);
this._handleProgressProxy = createjs.proxy(this._handleProgress, this);
this._handleAbortProxy = createjs.proxy(this._handleAbort, this);
this._handleErrorProxy = createjs.proxy(this._handleError, this);
this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this);
this._handleLoadProxy = createjs.proxy(this._handleLoad, this);
this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this);
 
if (!this._createXHR(item)) {
//TODO: Throw error?
}
};
 
var p = createjs.extend(XHRRequest, createjs.AbstractRequest);
 
// static properties
/**
* A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE.
* @property ACTIVEX_VERSIONS
* @type {Array}
* @since 0.4.2
* @private
*/
XHRRequest.ACTIVEX_VERSIONS = [
"Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP",
"Microsoft.XMLHTTP"
];
 
// Public methods
/**
* Look up the loaded result.
* @method getResult
* @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content
* loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
* returned instead.
* @return {Object} A result object containing the content that was loaded, such as:
* <ul>
* <li>An image tag (&lt;image /&gt;) for images</li>
* <li>A script tag for JavaScript (&lt;script /&gt;). Note that scripts loaded with tags may be added to the
* HTML head.</li>
* <li>A style tag for CSS (&lt;style /&gt;)</li>
* <li>Raw text for TEXT</li>
* <li>A formatted JavaScript object defined by JSON</li>
* <li>An XML document</li>
* <li>An binary arraybuffer loaded by XHR</li>
* </ul>
* Note that if a raw result is requested, but not found, the result will be returned instead.
*/
p.getResult = function (raw) {
if (raw && this._rawResponse) {
return this._rawResponse;
}
return this._response;
};
 
// Overrides abstract method in AbstractRequest
p.cancel = function () {
this.canceled = true;
this._clean();
this._request.abort();
};
 
// Overrides abstract method in AbstractLoader
p.load = function () {
if (this._request == null) {
this._handleError();
return;
}
 
//Events
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) {
this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
}
 
// Sometimes we get back 404s immediately, particularly when there is a cross origin request. // note this does not catch in Chrome
try {
if (!this._item.values || this._item.method == createjs.AbstractLoader.GET) {
this._request.send();
} else if (this._item.method == createjs.AbstractLoader.POST) {
this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
}
} catch (error) {
this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
}
};
 
p.setResponseType = function (type) {
// 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;
};
 
/**
* Get all the response headers from the XmlHttpRequest.
*
* <strong>From the docs:</strong> Return all the HTTP headers, excluding headers that are a case-insensitive match
* for Set-Cookie or Set-Cookie2, as a single string, with each header line separated by a U+000D CR U+000A LF pair,
* excluding the status line, and with each header name and header value separated by a U+003A COLON U+0020 SPACE
* pair.
* @method getAllResponseHeaders
* @return {String}
* @since 0.4.1
*/
p.getAllResponseHeaders = function () {
if (this._request.getAllResponseHeaders instanceof Function) {
return this._request.getAllResponseHeaders();
} else {
return null;
}
};
 
/**
* Get a specific response header from the XmlHttpRequest.
*
* <strong>From the docs:</strong> Returns the header field value from the response of which the field name matches
* header, unless the field name is Set-Cookie or Set-Cookie2.
* @method getResponseHeader
* @param {String} header The header name to retrieve.
* @return {String}
* @since 0.4.1
*/
p.getResponseHeader = function (header) {
if (this._request.getResponseHeader instanceof Function) {
return this._request.getResponseHeader(header);
} else {
return null;
}
};
 
// protected methods
/**
* The XHR request has reported progress.
* @method _handleProgress
* @param {Object} event The XHR progress event.
* @private
*/
p._handleProgress = function (event) {
if (!event || event.loaded > 0 && event.total == 0) {
return; // Sometimes we get no "total", so just ignore the progress event.
}
 
var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
this.dispatchEvent(newEvent);
};
 
/**
* The XHR request has reported a load start.
* @method _handleLoadStart
* @param {Object} event The XHR loadStart event.
* @private
*/
p._handleLoadStart = function (event) {
clearTimeout(this._loadTimeout);
this.dispatchEvent("loadstart");
};
 
/**
* The XHR request has reported an abort event.
* @method handleAbort
* @param {Object} event The XHR abort event.
* @private
*/
p._handleAbort = function (event) {
this._clean();
this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED", null, event));
};
 
/**
* The XHR request has reported an error event.
* @method _handleError
* @param {Object} event The XHR error event.
* @private
*/
p._handleError = function (event) {
this._clean();
this.dispatchEvent(new createjs.ErrorEvent(event.message));
};
 
/**
* The XHR request has reported a readyState change. Note that older browsers (IE 7 & 8) do not provide an onload
* event, so we must monitor the readyStateChange to determine if the file is loaded.
* @method _handleReadyStateChange
* @param {Object} event The XHR readyStateChange event.
* @private
*/
p._handleReadyStateChange = function (event) {
if (this._request.readyState == 4) {
this._handleLoad();
}
};
 
/**
* The XHR request has completed. This is called by the XHR request directly, or by a readyStateChange that has
* <code>request.readyState == 4</code>. Only the first call to this method will be processed.
* @method _handleLoad
* @param {Object} event The XHR load event.
* @private
*/
p._handleLoad = function (event) {
if (this.loaded) {
return;
}
this.loaded = true;
 
var error = this._checkError();
if (error) {
this._handleError(error);
return;
}
 
this._response = this._getResponse();
// 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"));
};
 
/**
* The XHR request has timed out. This is called by the XHR request directly, or via a <code>setTimeout</code>
* callback.
* @method _handleTimeout
* @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout.
* @private
*/
p._handleTimeout = function (event) {
this._clean();
 
this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
};
 
// Protected
/**
* Determine if there is an error in the current load. This checks the status of the request for problem codes. Note
* that this does not check for an actual response. Currently, it only checks for 404 or 0 error code.
* @method _checkError
* @return {int} If the request status returns an error code.
* @private
*/
p._checkError = function () {
//LM: Probably need additional handlers here, maybe 501
var status = parseInt(this._request.status);
 
switch (status) {
case 404: // Not Found
case 0: // Not Loaded
return new Error(status);
}
return null;
};
 
/**
* Validate the response. Different browsers have different approaches, some of which throw errors when accessed
* in other browsers. If there is no response, the <code>_response</code> property will remain null.
* @method _getResponse
* @private
*/
p._getResponse = function () {
if (this._response != null) {
return this._response;
}
 
if (this._request.response != null) {
return this._request.response;
}
 
// Android 2.2 uses .responseText
try {
if (this._request.responseText != null) {
return this._request.responseText;
}
} catch (e) {
}
 
// When loading XML, IE9 does not return .response, instead it returns responseXML.xml
try {
if (this._request.responseXML != null) {
return this._request.responseXML;
}
} catch (e) {
}
 
return null;
};
 
/**
* Create an XHR request. Depending on a number of factors, we get totally different results.
* <ol><li>Some browsers get an <code>XDomainRequest</code> when loading cross-domain.</li>
* <li>XMLHttpRequest are created when available.</li>
* <li>ActiveX.XMLHTTP objects are used in older IE browsers.</li>
* <li>Text requests override the mime type if possible</li>
* <li>Origin headers are sent for crossdomain requests in some browsers.</li>
* <li>Binary loads set the response type to "arraybuffer"</li></ol>
* @method _createXHR
* @param {Object} item The requested item that is being loaded.
* @return {Boolean} If an XHR request or equivalent was successfully created.
* @private
*/
p._createXHR = function (item) {
// Check for cross-domain loads. We can't fully support them, but we can try.
var crossdomain = createjs.RequestUtils.isCrossDomain(item);
var headers = {};
 
// Create the request. Fallback to whatever support we have.
var req = null;
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
// This is 8 or 9, so use XDomainRequest instead.
if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) {
req = new XDomainRequest();
}
} else { // Old IE versions use a different approach
for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
var axVersion = s.ACTIVEX_VERSIONS[i];
try {
req = new ActiveXObject(axVersion);
break;
} catch (e) {
}
}
if (req == null) {
return false;
}
}
 
// Default to utf-8 for Text requests.
if (item.mimeType == null && createjs.RequestUtils.isText(item.type)) {
item.mimeType = "text/plain; charset=utf-8";
}
 
// IE9 doesn't support overrideMimeType(), so we need to check for it.
if (item.mimeType && req.overrideMimeType) {
req.overrideMimeType(item.mimeType);
}
 
// Determine the XHR level
this._xhrLevel = (typeof req.responseType === "string") ? 2 : 1;
 
var src = null;
if (item.method == createjs.AbstractLoader.GET) {
src = createjs.RequestUtils.buildPath(item.src, item.values);
} else {
src = item.src;
}
 
// Open the request. Set cross-domain flags if it is supported (XHR level 1 only)
req.open(item.method || createjs.AbstractLoader.GET, src, true);
 
if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) {
headers["Origin"] = location.origin;
}
 
// To send data we need to set the Content-type header)
if (item.values && item.method == createjs.AbstractLoader.POST) {
headers["Content-Type"] = "application/x-www-form-urlencoded";
}
 
if (!crossdomain && !headers["X-Requested-With"]) {
headers["X-Requested-With"] = "XMLHttpRequest";
}
 
if (item.headers) {
for (var n in item.headers) {
headers[n] = item.headers[n];
}
}
 
for (n in headers) {
req.setRequestHeader(n, headers[n])
}
 
if (req instanceof XMLHttpRequest && item.withCredentials !== undefined) {
req.withCredentials = item.withCredentials;
}
 
this._request = req;
 
return true;
};
 
/**
* A request has completed (or failed or canceled), and needs to be disposed.
* @method _clean
* @private
*/
p._clean = function () {
clearTimeout(this._loadTimeout);
 
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 () {
return "[PreloadJS XHRRequest]";
};
 
createjs.XHRRequest = createjs.promote(XHRRequest, "AbstractRequest");
 
}());
 
//##############################################################################
// SoundLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* A loader for HTML audio files. PreloadJS can not load WebAudio files, as a WebAudio context is required, which
* should be created by either a library playing the sound (such as <a href="http://soundjs.com">SoundJS</a>, or an
* external framework that handles audio playback. To load content that can be played by WebAudio, use the
* {{#crossLink "BinaryLoader"}}{{/crossLink}}, and handle the audio context decoding manually.
* @class SoundLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @extends AbstractMediaLoader
* @constructor
*/
function SoundLoader(loadItem, preferXHR) {
this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
 
// protected properties
if (createjs.RequestUtils.isAudioTag(loadItem)) {
this._tag = loadItem;
} else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
this._tag = loadItem;
} else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
}
 
if (this._tag != null) {
this._preferXHR = false;
}
};
 
var p = createjs.extend(SoundLoader, createjs.AbstractMediaLoader);
var s = SoundLoader;
 
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "AbstractLoader/SOUND:property"}}{{/crossLink}}.
* @method canLoadItem
* @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
* @returns {Boolean} Whether the loader can load the item.
* @static
*/
s.canLoadItem = function (item) {
return item.type == createjs.AbstractLoader.SOUND;
};
 
// protected methods
p._createTag = function (src) {
var tag = document.createElement("audio");
tag.autoplay = false;
tag.preload = "none";
 
//LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
tag.src = src;
return tag;
};
 
createjs.SoundLoader = createjs.promote(SoundLoader, "AbstractMediaLoader");
 
}());
 
//##############################################################################
// AudioSprite.js
//##############################################################################
 
// NOTE this is "Class" is purely to document audioSprite Setup and usage.
 
 
/**
* <strong>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.</strong>
*
* 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.
*
* <strong>Benefits of Audio Sprites:</strong>
* <ul>
* <li>More robust support for older browsers and devices that only allow a single audio instance, such as iOS 5.</li>
* <li>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.</li>
* <li>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.</li>
* </ul>
*
* <strong>Drawbacks of Audio Sprites</strong>
* <ul>
* <li>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.</li>
* <li>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.</li>
* <li>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.</li>
* </ul>
*
* <h4>Example</h4>
*
* 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
* <a href="https://github.com/tonistiigi/audiosprite" target="_blank">https://github.com/tonistiigi/audiosprite</a>,
* as well as a <a href="http://jsfiddle.net/bharat_battu/g8fFP/12/" target="_blank">jsfiddle</a> 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:
* <ul>
* <li>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 <code>INTERRUPT_TYPE</code>
* constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.</li>
* <li>delay - The amount of time to delay the start of audio playback, in milliseconds.</li>
* <li>offset - The offset from the start of the audio to begin playback, in milliseconds.</li>
* <li>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.</li>
* <li>volume - The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.</li>
* <li>pan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).</li>
* <li>startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.</li>
* <li>duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.</li>
* </ul>
*
* <h4>Example</h4>
*
* var ppc = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
* createjs.Sound.play("mySound", ppc);
* mySoundInstance.play(ppc);
*
* @class PlayPropsConfig
* @constructor
* @since 0.6.1
*/
// TODO think of a better name for this class
var PlayPropsConfig = function () {
// Public Properties
/**
* How to interrupt any currently playing instances of audio with the same source,
* if the maximum number of instances of the sound are already playing. Values are defined as
* <code>INTERRUPT_TYPE</code> constants on the Sound class, with the default defined by
* {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
* @property interrupt
* @type {string}
* @default null
*/
this.interrupt = null;
 
/**
* The amount of time to delay the start of audio playback, in milliseconds.
* @property delay
* @type {Number}
* @default null
*/
this.delay = null;
 
/**
* The offset from the start of the audio to begin playback, in milliseconds.
* @property offset
* @type {number}
* @default null
*/
this.offset = null;
 
/**
* How many times the audio loops when it reaches the end of playback. The default is 0 (no
* loops), and -1 can be used for infinite playback.
* @property loop
* @type {number}
* @default null
*/
this.loop = null;
 
/**
* The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.
* @property volume
* @type {number}
* @default null
*/
this.volume = null;
 
/**
* The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* @property pan
* @type {number}
* @default null
*/
this.pan = null;
 
/**
* Used to create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* @property startTime
* @type {number}
* @default null
*/
this.startTime = null;
 
/**
* Used to create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
* @property duration
* @type {number}
* @default null
*/
this.duration = null;
};
var p = PlayPropsConfig.prototype = {};
var s = PlayPropsConfig;
 
 
// Static Methods
/**
* Creates a PlayPropsConfig from another PlayPropsConfig or an Object.
*
* @method create
* @param {PlayPropsConfig|Object} value The play properties
* @returns {PlayPropsConfig}
* @static
*/
s.create = function (value) {
if (value instanceof s || value instanceof Object) {
var ppc = new createjs.PlayPropsConfig();
ppc.set(value);
return ppc;
} else {
throw new Error("Type not recognized.");
}
};
 
// Public Methods
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
*
* <h4>Example</h4>
*
* var PlayPropsConfig = new createjs.PlayPropsConfig().set({loop:-1, volume:0.7});
*
* @method set
* @param {Object} props A generic object containing properties to copy to the PlayPropsConfig instance.
* @return {PlayPropsConfig} Returns the instance the method is called on (useful for chaining calls.)
*/
p.set = function(props) {
for (var n in props) { this[n] = props[n]; }
return this;
};
 
p.toString = function() {
return "[PlayPropsConfig]";
};
 
createjs.PlayPropsConfig = s;
 
}());
 
//##############################################################################
// Sound.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
 
 
(function () {
"use strict";
 
/**
* The Sound class is the public API for creating sounds, controlling the overall sound levels, and managing plugins.
* All Sound APIs on this class are static.
*
* <b>Registering and Preloading</b><br />
* Before you can play a sound, it <b>must</b> be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}},
* or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. If you don't register a
* sound prior to attempting to play it using {{#crossLink "Sound/play"}}{{/crossLink}} or create it using {{#crossLink "Sound/createInstance"}}{{/crossLink}},
* the sound source will be automatically registered but playback will fail as the source will not be ready. If you use
* <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, registration is handled for you when the sound is
* preloaded. It is recommended to preload sounds either internally using the register functions or externally using
* PreloadJS so they are ready when you want to use them.
*
* <b>Playback</b><br />
* To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method.
* This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc.
* Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs.
*
* <b>Plugins</b><br />
* By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
* are used (when available), although developers can change plugin priority or add new plugins (such as the
* provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API
* methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see
* {{#crossLink "Sound/installPlugins"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]);
* createjs.Sound.alternateExtensions = ["mp3"];
* 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", this.handleComplete, this);
* instance.volume = 0.5;
* }
*
* The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument
* of {{#crossLink "Sound/registerSound"}}{{/crossLink}}. Note that if not specified, the active plugin will apply
* a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a
* default limit of 100.
*
* createjs.Sound.registerSound("sound.mp3", "soundId", 4);
*
* Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
* automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
* load. As a result, it may fail to play the first time play is called if the audio is not finished loading. Use
* the {{#crossLink "Sound/fileload: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);
*
* <b>Audio Sprites</b><br />
* 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.
*
* <h4>Example</h4>
*
* 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");
*
* <b>Mobile Playback</b><br />
* 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.
* <ul>
* <li>
* 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.
* </li>
* <li>
* 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")
* </li>
* <li>
* 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.
* </li>
* <li>
* When using the <a href="http://www.createjs.com/docs/easeljs/classes/Touch.html">EaselJS Touch class</a>,
* 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:
* <ol>
* <li>Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).</li>
* <li>Set the `preventSelection` property on the EaselJS `Stage` to `false`.</li>
* </ol>
* These settings may change how your application behaves, and are not recommended.
* </li>
* </ul>
*
* <b>Loading Alternate Paths and Extension-less Files</b><br />
* 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.
*
* Priority is determined by the property order (first property is tried first). This is supported by both internal loading
* and loading with PreloadJS.
*
* <em>Note: an id is required for playback.</em>
*
* <h4>Example</h4>
*
* 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);
*
* <h3>Known Browser and OS issues</h3>
* <b>IE 9 HTML Audio limitations</b><br />
* <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
* muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
* when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
* <li>MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
* encoding with 64kbps works.</li>
* <li>Occasionally very short samples will get cut off.</li>
* <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by
* hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe
* estimate.</li></ul>
*
* <b>Firefox 25 Web Audio limitations</b>
* <ul><li>mp3 audio files do not load properly on all windows machines, reported
* <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br>
* For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if
* possible.</li></ul>
 
* <b>Safari limitations</b><br />
* <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
*
* <b>iOS 6 Web Audio limitations</b><br />
* <ul><li>Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
* Mobile Playback above.</li>
* <li>A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio
* at a different sampleRate.</li>
* </ul>
*
* <b>Android HTML Audio limitations</b><br />
* <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
* <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
* a delay.</li></ul>
*
* <b>Web Audio and PreloadJS</b><br />
* <ul><li>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.</li><ul>
*
* @class Sound
* @static
* @uses EventDispatcher
*/
function Sound() {
throw "Sound cannot be instantiated";
}
 
var s = Sound;
 
 
// Static Properties
/**
* The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
* instances of the sound are already playing.
* @property INTERRUPT_ANY
* @type {String}
* @default any
* @static
*/
s.INTERRUPT_ANY = "any";
 
/**
* The interrupt value to interrupt the earliest currently playing instance with the same source that progressed the
* least distance in the audio track, if the maximum number of instances of the sound are already playing.
* @property INTERRUPT_EARLY
* @type {String}
* @default early
* @static
*/
s.INTERRUPT_EARLY = "early";
 
/**
* The interrupt value to interrupt the currently playing instance with the same source that progressed the most
* distance in the audio track, if the maximum number of instances of the sound are already playing.
* @property INTERRUPT_LATE
* @type {String}
* @default late
* @static
*/
s.INTERRUPT_LATE = "late";
 
/**
* The interrupt value to not interrupt any currently playing instances with the same source, if the maximum number of
* instances of the sound are already playing.
* @property INTERRUPT_NONE
* @type {String}
* @default none
* @static
*/
s.INTERRUPT_NONE = "none";
 
/**
* Defines the playState of an instance that is still initializing.
* @property PLAY_INITED
* @type {String}
* @default playInited
* @static
*/
s.PLAY_INITED = "playInited";
 
/**
* Defines the playState of an instance that is currently playing or paused.
* @property PLAY_SUCCEEDED
* @type {String}
* @default playSucceeded
* @static
*/
s.PLAY_SUCCEEDED = "playSucceeded";
 
/**
* Defines the playState of an instance that was interrupted by another instance.
* @property PLAY_INTERRUPTED
* @type {String}
* @default playInterrupted
* @static
*/
s.PLAY_INTERRUPTED = "playInterrupted";
 
/**
* Defines the playState of an instance that completed playback.
* @property PLAY_FINISHED
* @type {String}
* @default playFinished
* @static
*/
s.PLAY_FINISHED = "playFinished";
 
/**
* Defines the playState of an instance that failed to play. This is usually caused by a lack of available channels
* when the interrupt mode was "INTERRUPT_NONE", the playback stalled, or the sound could not be found.
* @property PLAY_FAILED
* @type {String}
* @default playFailed
* @static
*/
s.PLAY_FAILED = "playFailed";
 
/**
* A list of the default supported extensions that Sound will <i>try</i> to play. Plugins will check if the browser
* can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to
* support additional media types.
*
* NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
*
* More details on file formats can be found at <a href="http://en.wikipedia.org/wiki/Audio_file_format" target="_blank">http://en.wikipedia.org/wiki/Audio_file_format</a>.<br />
* A very detailed list of file formats can be found at <a href="http://www.fileinfo.com/filetypes/audio" target="_blank">http://www.fileinfo.com/filetypes/audio</a>.
* @property SUPPORTED_EXTENSIONS
* @type {Array[String]}
* @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
* @since 0.4.0
* @static
*/
s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
 
/**
* Some extensions use another type of extension support to play (one of them is a codex). This allows you to map
* that support so plugins can accurately determine if an extension is supported. Adding to this list can help
* plugins determine more accurately if an extension is supported.
*
* A useful list of extensions for each format can be found at <a href="http://html5doctor.com/html5-audio-the-state-of-play/" target="_blank">http://html5doctor.com/html5-audio-the-state-of-play/</a>.
* @property EXTENSION_MAP
* @type {Object}
* @since 0.4.0
* @default {m4a:"mp4"}
* @static
*/
s.EXTENSION_MAP = {
m4a:"mp4"
};
 
/**
* The RegExp pattern used to parse file URIs. This supports simple file names, as well as full domain URIs with
* query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6.
* @property FILE_PATTERN
* @type {RegExp}
* @static
* @protected
*/
s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
 
 
// Class Public properties
/**
* Determines the default behavior for interrupting other currently playing instances with the same source, if the
* maximum number of instances of the sound are already playing. Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}
* but this can be set and will change playback behavior accordingly. This is only used when {{#crossLink "Sound/play"}}{{/crossLink}}
* is called without passing a value for interrupt.
* @property defaultInterruptBehavior
* @type {String}
* @default Sound.INTERRUPT_NONE, or "none"
* @static
* @since 0.4.0
*/
s.defaultInterruptBehavior = s.INTERRUPT_NONE; // OJR does s.INTERRUPT_ANY make more sense as default? Needs game dev testing to see which case makes more sense.
 
/**
* An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin.
* These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your
* extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need
* to exist in the same location, as only the extension is altered.
*
* Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}}
* and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading.
*
* <h4>Example</h4>
*
* 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.
*
* <h4>Example</h4>
*
* createjs.Sound.volume = 0.5;
*
*
* @property volume
* @type {Number}
* @default 1
* @since 0.6.1
*/
s._masterVolume = 1;
Object.defineProperty(s, "volume", {
get: function () {return this._masterVolume;},
set: function (value) {
if (Number(value) == null) {return false;}
value = Math.max(0, Math.min(1, value));
s._masterVolume = value;
if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterVolume(value);
}
}
}
});
 
/**
* Mute/Unmute all audio. Note that muted audio still plays at 0 volume. This global mute value is maintained
* separately and when set will override, but not change the mute property of individual instances. To mute an individual
* instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
*
* <h4>Example</h4>
*
* createjs.Sound.muted = true;
*
*
* @property muted
* @type {Boolean}
* @default false
* @since 0.6.1
*/
s._masterMute = false;
// OJR references to the methods were not working, so the code had to be duplicated here
Object.defineProperty(s, "muted", {
get: function () {return this._masterMute;},
set: function (value) {
if (value == null) {return false;}
 
this._masterMute = value;
if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterMute(value);
}
}
return true;
}
});
 
/**
* Get the active plugins capabilities, which help determine if a plugin can be used in the current environment,
* or if the plugin supports a specific feature. Capabilities include:
* <ul>
* <li><b>panning:</b> If the plugin can pan audio from left to right</li>
* <li><b>volume;</b> If the plugin can control audio volume.</li>
* <li><b>tracks:</b> The maximum number of audio tracks that can be played back at a time. This will be -1
* if there is no known limit.</li>
* <br />An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
* <li><b>mp3:</b> If MP3 audio is supported.</li>
* <li><b>ogg:</b> If OGG audio is supported.</li>
* <li><b>wav:</b> If WAV audio is supported.</li>
* <li><b>mpeg:</b> If MPEG audio is supported.</li>
* <li><b>m4a:</b> If M4A audio is supported.</li>
* <li><b>mp4:</b> If MP4 audio is supported.</li>
* <li><b>aiff:</b> If aiff audio is supported.</li>
* <li><b>wma:</b> If wma audio is supported.</li>
* <li><b>mid:</b> If mid audio is supported.</li>
* </ul>
*
* You can get a specific capability of the active plugin using standard object notation
*
* <h4>Example</h4>
*
* var mp3 = createjs.Sound.capabilities.mp3;
*
* Note this property is read only.
*
* @property capabilities
* @type {Object}
* @static
* @readOnly
* @since 0.6.1
*/
Object.defineProperty(s, "capabilities", {
get: function () {
if (s.activePlugin == null) {return null;}
return s.activePlugin._capabilities;
},
set: function (value) { return false;}
});
 
 
// Class Private properties
/**
* Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
* plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}).
* If plugins have been registered, but none are applicable, then sound playback will fail.
* @property _pluginsRegistered
* @type {Boolean}
* @default false
* @static
* @protected
*/
s._pluginsRegistered = false;
 
/**
* Used internally to assign unique IDs to each AbstractSoundInstance.
* @property _lastID
* @type {Number}
* @static
* @protected
*/
s._lastID = 0;
 
/**
* An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
* all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
* When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}}
* method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}}
* method.
* @property _instances
* @type {Array}
* @protected
* @static
*/
s._instances = [];
 
/**
* An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
* @property _idHash
* @type {Object}
* @protected
* @static
*/
s._idHash = {};
 
/**
* An object hash that stores preloading sound sources via the parsed source that is passed to the plugin. Contains the
* source, id, and data that was passed in by the user. Parsed sources can contain multiple instances of source, id,
* and data.
* @property _preloadHash
* @type {Object}
* @protected
* @static
*/
s._preloadHash = {};
 
/**
* An object hash storing {{#crossLink "PlayPropsConfig"}}{{/crossLink}} via the parsed source that is passed as defaultPlayProps in
* {{#crossLink "Sound/registerSound"}}{{/crossLink}} and {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* @property _defaultPlayPropsHash
* @type {Object}
* @protected
* @static
* @since 0.6.1
*/
s._defaultPlayPropsHash = {};
 
 
// EventDispatcher methods:
s.addEventListener = null;
s.removeEventListener = null;
s.removeAllEventListeners = null;
s.dispatchEvent = null;
s.hasEventListener = null;
s._listeners = null;
 
createjs.EventDispatcher.initialize(s); // inject EventDispatcher methods.
 
 
// Events
/**
* This event is fired when a file finishes loading internally. This event is fired for each loaded sound,
* so any handler methods should look up the <code>event.src</code> to handle a particular sound.
* @event fileload
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @param {String} src The source of the sound that was loaded.
* @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
* @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
* @since 0.4.1
*/
 
/**
* This event is fired when a file fails loading internally. This event is fired for each loaded sound,
* so any handler methods should look up the <code>event.src</code> to handle a particular sound.
* @event fileerror
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @param {String} src The source of the sound that was loaded.
* @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
* @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
* @since 0.6.0
*/
 
 
// Class Public Methods
/**
* Get the preload rules to allow Sound to be used as a plugin by <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
* Any load calls that have the matching type or extension will fire the callback method, and use the resulting
* object, which is potentially modified by Sound. This helps when determining the correct path, as well as
* registering the audio instance(s) with Sound. This method should not be called, except by PreloadJS.
* @method getPreloadHandlers
* @return {Object} An object containing:
* <ul><li>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.</li>
* <li>types: A list of file types that are supported by Sound (currently supports "sound").</li>
* <li>extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).</li></ul>
* @static
* @protected
*/
s.getPreloadHandlers = function () {
return {
callback:createjs.proxy(s.initLoad, s),
types:["sound"],
extensions:s.SUPPORTED_EXTENSIONS
};
};
 
/**
* Used to dispatch fileload events from internal loading.
* @method _handleLoadComplete
* @param event A loader event.
* @protected
* @static
* @since 0.6.0
*/
s._handleLoadComplete = function(event) {
var src = event.target.getItem().src;
if (!s._preloadHash[src]) {return;}
 
for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
var item = s._preloadHash[src][i];
s._preloadHash[src][i] = true;
 
if (!s.hasEventListener("fileload")) { continue; }
 
var event = new createjs.Event("fileload");
event.src = item.src;
event.id = item.id;
event.data = item.data;
event.sprite = item.sprite;
 
s.dispatchEvent(event);
}
};
 
/**
* Used to dispatch error events from internal preloading.
* @param event
* @protected
* @since 0.6.0
* @static
*/
s._handleLoadError = function(event) {
var src = event.target.getItem().src;
if (!s._preloadHash[src]) {return;}
 
for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
var item = s._preloadHash[src][i];
s._preloadHash[src][i] = false;
 
if (!s.hasEventListener("fileerror")) { continue; }
 
var event = new createjs.Event("fileerror");
event.src = item.src;
event.id = item.id;
event.data = item.data;
event.sprite = item.sprite;
 
s.dispatchEvent(event);
}
};
 
/**
* Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
*
* @method _registerPlugin
* @param {Object} plugin The plugin class to install.
* @return {Boolean} Whether the plugin was successfully initialized.
* @static
* @private
*/
s._registerPlugin = function (plugin) {
// Note: Each plugin is passed in as a class reference, but we store the activePlugin as an instance
if (plugin.isSupported()) {
s.activePlugin = new plugin();
return true;
}
return false;
};
 
/**
* Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
*
* <h4>Example</h4>
*
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
*
* @method registerPlugins
* @param {Array} plugins An array of plugins classes to install.
* @return {Boolean} Whether a plugin was successfully initialized.
* @static
*/
s.registerPlugins = function (plugins) {
s._pluginsRegistered = true;
for (var i = 0, l = plugins.length; i < l; i++) {
if (s._registerPlugin(plugins[i])) {
return true;
}
}
return false;
};
 
/**
* Initialize the default plugins. This method is automatically called when any audio is played or registered before
* the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the
* default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* if (!createjs.initializeDefaultPlugins()) { return; }
*
* @method initializeDefaultPlugins
* @returns {Boolean} True if a plugin was initialized, false otherwise.
* @since 0.4.0
* @static
*/
s.initializeDefaultPlugins = function () {
if (s.activePlugin != null) {return true;}
if (s._pluginsRegistered) {return false;}
if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
return false;
};
 
/**
* Determines if Sound has been initialized, and a plugin has been activated.
*
* <h4>Example</h4>
* This example sets up a Flash fallback, but only if there is no plugin specified yet.
*
* if (!createjs.Sound.isReady()) {
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
* }
*
* @method isReady
* @return {Boolean} If Sound has initialized a plugin.
* @static
*/
s.isReady = function () {
return (s.activePlugin != null);
};
 
/**
* Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
*
* @method getCapabilities
* @return {Object} An object containing the capabilities of the active plugin.
* @static
* @deprecated
*/
s.getCapabilities = function () {
if (s.activePlugin == null) {return null;}
return s.activePlugin._capabilities;
};
 
/**
* Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
*
* @method getCapability
* @param {String} key The capability to retrieve
* @return {Number|Boolean} The value of the capability.
* @static
* @see getCapabilities
* @deprecated
*/
s.getCapability = function (key) {
if (s.activePlugin == null) {return null;}
return s.activePlugin._capabilities[key];
};
 
/**
* Process manifest items from <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. This method is intended
* for usage by a plugin, and not for direct interaction.
* @method initLoad
* @param {Object} src The object to load.
* @return {Object|AbstractLoader} An instance of AbstractLoader.
* @protected
* @static
*/
s.initLoad = function (loadItem) {
return s._registerSound(loadItem);
};
 
/**
* Internal method for loading sounds. This should not be called directly.
*
* @method _registerSound
* @param {Object} src The object to load, containing src property and optionally containing id and data.
* @return {Object} An object with the modified values that were passed in, which defines the sound.
* Returns false if the source cannot be parsed or no plugins can be initialized.
* Returns true if the source is already loaded.
* @static
* @private
* @since 0.6.0
*/
 
s._registerSound = function (loadItem) {
if (!s.initializeDefaultPlugins()) {return false;}
 
var details;
if (loadItem.src instanceof Object) {
details = s._parseSrc(loadItem.src);
details.src = loadItem.path + details.src;
} else {
details = s._parsePath(loadItem.src);
}
if (details == null) {return false;}
loadItem.src = details.src;
loadItem.type = "sound";
 
var data = loadItem.data;
var numChannels = null;
if (data != null) {
if (!isNaN(data.channels)) {
numChannels = parseInt(data.channels);
} else if (!isNaN(data)) {
numChannels = parseInt(data);
}
 
if(data.audioSprite) {
var sp;
for(var i = data.audioSprite.length; i--; ) {
sp = data.audioSprite[i];
s._idHash[sp.id] = {src: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
 
if (sp.defaultPlayProps) {
s._defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
}
}
}
}
if (loadItem.id != null) {s._idHash[loadItem.id] = {src: loadItem.src}};
var loader = s.activePlugin.register(loadItem);
 
SoundChannel.create(loadItem.src, numChannels);
 
// return the number of instances to the user. This will also be returned in the load event.
if (data == null || !isNaN(data)) {
loadItem.data = numChannels || SoundChannel.maxPerChannel();
} else {
loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
}
 
if (loader.type) {loadItem.type = loader.type;}
 
if (loadItem.defaultPlayProps) {
s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
}
return loader;
};
 
/**
* Register an audio file for loading and future playback in Sound. This is automatically called when using
* <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. It is recommended to register all sounds that
* need to be played back in order to properly prepare and preload them. Sound does internal preloading when required.
*
* <h4>Example</h4>
*
* createjs.Sound.alternateExtensions = ["mp3"];
* createjs.Sound.on("fileload", handleLoad); // add an event listener for when load is completed
* createjs.Sound.registerSound("myAudioPath/mySound.ogg", "myID", 3);
* createjs.Sound.registerSound({ogg:"path1/mySound.ogg", mp3:"path2/mySoundNoExtension"}, "myID", 3);
*
*
* @method registerSound
* @param {String | Object} src The source or an Object with a "src" property or an Object with multiple extension labeled src properties.
* @param {String} [id] An id specified by the user to play the sound later. Note id is required for when src is multiple extension labeled src properties.
* @param {Number | Object} [data] Data associated with the item. Sound uses the data parameter as the number of
* channels for an audio instance, however a "channels" property can be appended to the data object if it is used
* for other information. The audio channels will set a default based on plugin if no value is found.
* Sound also uses the data property to hold an {{#crossLink "AudioSprite"}}{{/crossLink}} array of objects in the following format {id, startTime, duration}.<br/>
* id used to play the sound later, in the same manner as a sound src with an id.<br/>
* startTime is the initial offset to start playback and loop from, in milliseconds.<br/>
* duration is the amount of time to play the clip for, in milliseconds.<br/>
* This allows Sound to support audio sprites that are played back by id.
* @param {string} basePath Set a path that will be prepended to src for loading.
* @param {Object | PlayPropsConfig} defaultPlayProps Optional Playback properties that will be set as the defaults on any new AbstractSoundInstance.
* See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for options.
* @return {Object} An object with the modified values that were passed in, which defines the sound.
* Returns false if the source cannot be parsed or no plugins can be initialized.
* Returns true if the source is already loaded.
* @static
* @since 0.4.0
*/
s.registerSound = function (src, id, data, basePath, defaultPlayProps) {
var loadItem = {src: src, id: id, data:data, defaultPlayProps:defaultPlayProps};
if (src instanceof Object && src.src) {
basePath = id;
loadItem = src;
}
loadItem = createjs.LoadItem.create(loadItem);
loadItem.path = basePath;
 
if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + src;}
 
var loader = s._registerSound(loadItem);
if(!loader) {return false;}
 
if (!s._preloadHash[loadItem.src]) { s._preloadHash[loadItem.src] = [];}
s._preloadHash[loadItem.src].push(loadItem);
if (s._preloadHash[loadItem.src].length == 1) {
// OJR note this will disallow reloading a sound if loading fails or the source changes
loader.on("complete", createjs.proxy(this._handleLoadComplete, this));
loader.on("error", createjs.proxy(this._handleLoadError, this));
s.activePlugin.preload(loader);
} else {
if (s._preloadHash[loadItem.src][0] == true) {return true;}
}
 
return loadItem;
};
 
/**
* Register an array of audio files for loading and future playback in Sound. It is recommended to register all
* sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading
* when required.
*
* <h4>Example</h4>
*
* var assetPath = "./myAudioPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
* {src:"asset2.mp3", id:"works"}
* {src:{mp3:"path1/asset3.mp3", ogg:"path2/asset3NoExtension}, id:"better"}
* ];
* createjs.Sound.alternateExtensions = ["mp3"]; // if the passed extension is not supported, try this extension
* createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
* createjs.Sound.registerSounds(sounds, assetPath);
*
* @method registerSounds
* @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for
* {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code>
* with "id" and "data" being optional.
* You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to load.
* Note id is required if src is an object with extension labeled src properties.
* @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing
* audio that was loaded with a basePath by src, the basePath must be included.
* @return {Object} An array of objects with the modified values that were passed in, which defines each sound.
* Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized.
* Also, it will return true for any values when the source is already loaded.
* @static
* @since 0.6.0
*/
s.registerSounds = function (sounds, basePath) {
var returnValues = [];
if (sounds.path) {
if (!basePath) {
basePath = sounds.path;
} else {
basePath = basePath + sounds.path;
}
sounds = sounds.manifest;
// TODO document this feature
}
for (var i = 0, l = sounds.length; i < l; i++) {
returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
}
return returnValues;
};
 
/**
* Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* <br />Note this will stop playback on active instances playing this sound before deleting them.
* <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
*
* <h4>Example</h4>
*
* createjs.Sound.removeSound("myID");
* createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
* createjs.Sound.removeSound("myPath/myOtherSound.mp3", "myBasePath/");
* createjs.Sound.removeSound({mp3:"musicNoExtension", ogg:"music.ogg"}, "myBasePath/");
*
* @method removeSound
* @param {String | Object} src The src or ID of the audio, or an Object with a "src" property, or an Object with multiple extension labeled src properties.
* @param {string} basePath Set a path that will be prepended to each src when removing.
* @return {Boolean} True if sound is successfully removed.
* @static
* @since 0.4.1
*/
s.removeSound = function(src, basePath) {
if (s.activePlugin == null) {return false;}
 
if (src instanceof Object && src.src) {src = src.src;}
 
var details;
if (src instanceof Object) {
details = s._parseSrc(src);
} else {
src = s._getSrcById(src).src;
details = s._parsePath(src);
}
if (details == null) {return false;}
src = details.src;
if (basePath != null) {src = basePath + src;}
 
for(var prop in s._idHash){
if(s._idHash[prop].src == src) {
delete(s._idHash[prop]);
}
}
 
// clear from SoundChannel, which also stops and deletes all instances
SoundChannel.removeSrc(src);
 
delete(s._preloadHash[src]);
 
s.activePlugin.removeSound(src);
 
return true;
};
 
/**
* Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* <br />Note this will stop playback on active instances playing this audio before deleting them.
* <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
*
* <h4>Example</h4>
*
* assetPath = "./myPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
* {src:"asset2.mp3", id:"works"}
* ];
* createjs.Sound.removeSounds(sounds, assetPath);
*
* @method removeSounds
* @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for
* {{#crossLink "Sound/removeSound"}}{{/crossLink}}: <code>{srcOrID:srcURIorID}</code>.
* You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to remove.
* @param {string} basePath Set a path that will be prepended to each src when removing.
* @return {Object} An array of Boolean values representing if the sounds with the same array index were
* successfully removed.
* @static
* @since 0.4.1
*/
s.removeSounds = function (sounds, basePath) {
var returnValues = [];
if (sounds.path) {
if (!basePath) {
basePath = sounds.path;
} else {
basePath = basePath + sounds.path;
}
sounds = sounds.manifest;
}
for (var i = 0, l = sounds.length; i < l; i++) {
returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath);
}
return returnValues;
};
 
/**
* Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* <br />Note this will stop playback on all active sound instances before deleting them.
*
* <h4>Example</h4>
*
* createjs.Sound.removeAllSounds();
*
* @method removeAllSounds
* @static
* @since 0.4.1
*/
s.removeAllSounds = function() {
s._idHash = {};
s._preloadHash = {};
SoundChannel.removeAll();
if (s.activePlugin) {s.activePlugin.removeAllSounds();}
};
 
/**
* Check if a source has been loaded by internal preloaders. This is necessary to ensure that sounds that are
* not completed preloading will not kick off a new internal preload if they are played.
*
* <h4>Example</h4>
*
* var mySound = "assetPath/asset0.ogg";
* if(createjs.Sound.loadComplete(mySound) {
* createjs.Sound.play(mySound);
* }
*
* @method loadComplete
* @param {String} src The src or id that is being loaded.
* @return {Boolean} If the src is already loaded.
* @since 0.4.0
* @static
*/
s.loadComplete = function (src) {
if (!s.isReady()) { return false; }
var details = s._parsePath(src);
if (details) {
src = s._getSrcById(details.src).src;
} else {
src = s._getSrcById(src).src;
}
if(s._preloadHash[src] == undefined) {return false;}
return (s._preloadHash[src][0] == true); // src only loads once, so if it's true for the first it's true for all
};
 
/**
* Parse the path of a sound. Alternate extensions will be attempted in order if the
* current extension is not supported
* @method _parsePath
* @param {String} value The path to an audio source.
* @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
* and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
* @protected
* @static
*/
s._parsePath = function (value) {
if (typeof(value) != "string") {value = value.toString();}
 
var match = value.match(s.FILE_PATTERN);
if (match == null) {return false;}
 
var name = match[4];
var ext = match[5];
var c = s.capabilities;
var i = 0;
while (!c[ext]) {
ext = s.alternateExtensions[i++];
if (i > s.alternateExtensions.length) { return null;} // no extensions are supported
}
value = value.replace("."+match[5], "."+ext);
 
var ret = {name:name, src:value, extension:ext};
return ret;
};
 
/**
* Parse the path of a sound based on properties of src matching with supported extensions.
* Returns false if none of the properties are supported
* @method _parseSrc
* @param {Object} value The paths to an audio source, indexed by extension type.
* @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
* and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
* @protected
* @static
*/
s._parseSrc = function (value) {
var ret = {name:undefined, src:undefined, extension:undefined};
var c = s.capabilities;
 
for (var prop in value) {
if(value.hasOwnProperty(prop) && c[prop]) {
ret.src = value[prop];
ret.extension = prop;
break;
}
}
if (!ret.src) {return false;} // no matches
 
var i = ret.src.lastIndexOf("/");
if (i != -1) {
ret.name = ret.src.slice(i+1);
} else {
ret.name = ret.src;
}
 
return ret;
};
 
/* ---------------
Static API.
--------------- */
/**
* Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a
* AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}.
* Note that even on sounds with failed playback, you may still be able to call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
* since the failure could be due to lack of available channels. If the src does not have a supported extension or
* if there is no available plugin, a default AbstractSoundInstance will be returned which will not play any audio, but will not generate errors.
*
* <h4>Example</h4>
*
* createjs.Sound.on("fileload", handleLoad);
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* createjs.Sound.play("myID");
* // store off AbstractSoundInstance for controlling
* var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
* }
*
* NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* <b>Parameters Deprecated</b><br />
* The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
*
* @method play
* @param {String} src The src or ID of the audio.
* @param {String | Object} [interrupt="none"|options] <b>This parameter will be renamed playProps in the next release.</b><br />
* This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
* including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
* <br /><strong>OR</strong><br />
* <b>Deprecated</b> How to interrupt any currently playing instances of audio with the same source,
* if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
* constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
* @param {Number} [delay=0] <b>Deprecated</b> The amount of time to delay the start of audio playback, in milliseconds.
* @param {Number} [offset=0] <b>Deprecated</b> The offset from the start of the audio to begin playback, in milliseconds.
* @param {Number} [loop=0] <b>Deprecated</b> 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] <b>Deprecated</b> The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.
* @param {Number} [pan=0] <b>Deprecated</b> The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* @param {Number} [startTime=null] <b>Deprecated</b> To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* @param {Number} [duration=null] <b>Deprecated</b> To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
* @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
* @static
*/
s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) {
var playProps;
if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
playProps = createjs.PlayPropsConfig.create(interrupt);
} else {
playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan, startTime:startTime, duration:duration});
}
var instance = s.createInstance(src, playProps.startTime, playProps.duration);
var ok = s._playInstance(instance, playProps);
if (!ok) {instance._playFailed();}
return instance;
};
 
/**
* Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a
* supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be
* called safely but does nothing.
*
* <h4>Example</h4>
*
* var myInstance = null;
* createjs.Sound.on("fileload", handleLoad);
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* myInstance = createjs.Sound.createInstance("myID");
* // alternately we could call the following
* myInstance = createjs.Sound.createInstance("myAudioPath/mySound.mp3");
* }
*
* NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* @method createInstance
* @param {String} src The src or ID of the audio.
* @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
* @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
* Unsupported extensions will return the default AbstractSoundInstance.
* @since 0.4.0
* @static
*/
s.createInstance = function (src, startTime, duration) {
if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}
 
var defaultPlayProps = s._defaultPlayPropsHash[src]; // for audio sprites, which create and store defaults by id
src = s._getSrcById(src);
 
var details = s._parsePath(src.src);
 
var instance = null;
if (details != null && details.src != null) {
SoundChannel.create(details.src);
if (startTime == null) {startTime = src.startTime;}
instance = s.activePlugin.create(details.src, startTime, duration || src.duration);
 
defaultPlayProps = defaultPlayProps || s._defaultPlayPropsHash[details.src];
if(defaultPlayProps) {
instance.applyPlayProps(defaultPlayProps);
}
} else {
instance = new createjs.DefaultSoundInstance(src, startTime, duration);
}
 
instance.uniqueId = s._lastID++;
 
return instance;
};
 
/**
* Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped,
* call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* createjs.Sound.stop();
*
* @method stop
* @static
*/
s.stop = function () {
var instances = this._instances;
for (var i = instances.length; i--; ) {
instances[i].stop(); // NOTE stop removes instance from this._instances
}
};
 
/**
* Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
*
* @method setVolume
* @param {Number} value The master volume value. The acceptable range is 0-1.
* @static
* @deprecated
*/
s.setVolume = function (value) {
if (Number(value) == null) {return false;}
value = Math.max(0, Math.min(1, value));
s._masterVolume = value;
if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterVolume(value);
}
}
};
 
/**
* Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
*
* @method getVolume
* @return {Number} The master volume, in a range of 0-1.
* @static
* @deprecated
*/
s.getVolume = function () {
return this._masterVolume;
};
 
/**
* Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
*
* @method setMute
* @param {Boolean} value Whether the audio should be muted or not.
* @return {Boolean} If the mute was set.
* @static
* @since 0.4.0
* @deprecated
*/
s.setMute = function (value) {
if (value == null) {return false;}
 
this._masterMute = value;
if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterMute(value);
}
}
return true;
};
 
/**
* Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
*
* @method getMute
* @return {Boolean} The mute value of Sound.
* @static
* @since 0.4.0
* @deprecated
*/
s.getMute = function () {
return this._masterMute;
};
 
/**
* Set the default playback properties for all new SoundInstances of the passed in src or ID.
* See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
*
* @method setDefaultPlayProps
* @param {String} src The src or ID used to register the audio.
* @param {Object | PlayPropsConfig} playProps The playback properties you would like to set.
* @since 0.6.1
*/
s.setDefaultPlayProps = function(src, playProps) {
src = s._getSrcById(src);
s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
};
 
/**
* Get the default playback properties for the passed in src or ID. These properties are applied to all
* new SoundInstances. Returns null if default does not exist.
*
* @method getDefaultPlayProps
* @param {String} src The src or ID used to register the audio.
* @returns {PlayPropsConfig} returns an existing PlayPropsConfig or null if one does not exist
* @since 0.6.1
*/
s.getDefaultPlayProps = function(src) {
src = s._getSrcById(src);
return s._defaultPlayPropsHash[s._parsePath(src.src).src];
};
 
 
/* ---------------
Internal methods
--------------- */
/**
* Play an instance. This is called by the static API, as well as from plugins. This allows the core class to
* control delays.
* @method _playInstance
* @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
* @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If the sound can start playing. Sounds that fail immediately will return false. Sounds that
* have a delay will return true, but may still fail to play.
* @protected
* @static
*/
s._playInstance = function (instance, playProps) {
var defaultPlayProps = s._defaultPlayPropsHash[instance.src] || {};
if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || s.defaultInterruptBehavior};
if (playProps.delay == null) {playProps.delay = defaultPlayProps.delay || 0;}
if (playProps.offset == null) {playProps.offset = instance.getPosition();}
if (playProps.loop == null) {playProps.loop = instance.loop;}
if (playProps.volume == null) {playProps.volume = instance.volume;}
if (playProps.pan == null) {playProps.pan = instance.pan;}
 
if (playProps.delay == 0) {
var ok = s._beginPlaying(instance, playProps);
if (!ok) {return false;}
} else {
//Note that we can't pass arguments to proxy OR setTimeout (IE only), so just wrap the function call.
// OJR WebAudio may want to handle this differently, so it might make sense to move this functionality into the plugins in the future
var delayTimeoutId = setTimeout(function () {
s._beginPlaying(instance, playProps);
}, playProps.delay);
instance.delayTimeoutId = delayTimeoutId;
}
 
this._instances.push(instance);
 
return true;
};
 
/**
* Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}.
* @method _beginPlaying
* @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback.
* @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If the sound can start playing. If there are no available channels, or the instance fails to
* start, this will return false.
* @protected
* @static
*/
s._beginPlaying = function (instance, playProps) {
if (!SoundChannel.add(instance, playProps.interrupt)) {
return false;
}
var result = instance._beginPlaying(playProps);
if (!result) {
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);}
return false;
}
return true;
};
 
/**
* Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned
* instead.
* @method _getSrcById
* @param {String} value The ID the sound was registered with.
* @return {String} The source of the sound if it has been registered with this ID or the value that was passed in.
* @protected
* @static
*/
s._getSrcById = function (value) {
return s._idHash[value] || {src: value};
};
 
/**
* A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from
* Sound management. It will be added again, if the sound re-plays. Note that this method is called from the
* instances themselves.
* @method _playFinished
* @param {AbstractSoundInstance} instance The instance that finished playback.
* @protected
* @static
*/
s._playFinished = function (instance) {
SoundChannel.remove(instance);
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);} // OJR this will always be > -1, there is no way for an instance to exist without being added to this._instances
};
 
createjs.Sound = Sound;
 
/**
* An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for
* each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class.
*
* The number of sounds is artificially limited by Sound in order to prevent over-saturation of a
* single sound, as well as to stay within hardware limitations, although the latter may disappear with better
* browser support.
*
* When a sound is played, this class ensures that there is an available instance, or interrupts an appropriate
* sound that is already playing.
* #class SoundChannel
* @param {String} src The source of the instances
* @param {Number} [max=1] The number of instances allowed
* @constructor
* @protected
*/
function SoundChannel(src, max) {
this.init(src, max);
}
 
/* ------------
Static API
------------ */
/**
* A hash of channel instances indexed by source.
* #property channels
* @type {Object}
* @static
*/
SoundChannel.channels = {};
 
/**
* Create a sound channel. Note that if the sound channel already exists, this will fail.
* #method create
* @param {String} src The source for the channel
* @param {Number} max The maximum amount this channel holds. The default is {{#crossLink "SoundChannel.maxDefault"}}{{/crossLink}}.
* @return {Boolean} If the channels were created.
* @static
*/
SoundChannel.create = function (src, max) {
var channel = SoundChannel.get(src);
if (channel == null) {
SoundChannel.channels[src] = new SoundChannel(src, max);
return true;
}
return false;
};
/**
* Delete a sound channel, stop and delete all related instances. Note that if the sound channel does not exist, this will fail.
* #method remove
* @param {String} src The source for the channel
* @return {Boolean} If the channels were deleted.
* @static
*/
SoundChannel.removeSrc = function (src) {
var channel = SoundChannel.get(src);
if (channel == null) {return false;}
channel._removeAll(); // this stops and removes all active instances
delete(SoundChannel.channels[src]);
return true;
};
/**
* Delete all sound channels, stop and delete all related instances.
* #method removeAll
* @static
*/
SoundChannel.removeAll = function () {
for(var channel in SoundChannel.channels) {
SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances
}
SoundChannel.channels = {};
};
/**
* Add an instance to a sound channel.
* #method add
* @param {AbstractSoundInstance} instance The instance to add to the channel
* @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}}
* for details on interrupt modes.
* @return {Boolean} The success of the method call. If the channel is full, it will return false.
* @static
*/
SoundChannel.add = function (instance, interrupt) {
var channel = SoundChannel.get(instance.src);
if (channel == null) {return false;}
return channel._add(instance, interrupt);
};
/**
* Remove an instance from the channel.
* #method remove
* @param {AbstractSoundInstance} instance The instance to remove from the channel
* @return The success of the method call. If there is no channel, it will return false.
* @static
*/
SoundChannel.remove = function (instance) {
var channel = SoundChannel.get(instance.src);
if (channel == null) {return false;}
channel._remove(instance);
return true;
};
/**
* Get the maximum number of sounds you can have in a channel.
* #method maxPerChannel
* @return {Number} The maximum number of sounds you can have in a channel.
*/
SoundChannel.maxPerChannel = function () {
return p.maxDefault;
};
/**
* Get a channel instance by its src.
* #method get
* @param {String} src The src to use to look up the channel
* @static
*/
SoundChannel.get = function (src) {
return SoundChannel.channels[src];
};
 
var p = SoundChannel.prototype;
p.constructor = SoundChannel;
 
/**
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
* for details.
*
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
*
* @method initialize
* @protected
* @deprecated
*/
// p.initialize = function() {}; // searchable for devs wondering where it is.
 
 
/**
* The source of the channel.
* #property src
* @type {String}
*/
p.src = null;
 
/**
* The maximum number of instances in this channel. -1 indicates no limit
* #property max
* @type {Number}
*/
p.max = null;
 
/**
* The default value to set for max, if it isn't passed in. Also used if -1 is passed.
* #property maxDefault
* @type {Number}
* @default 100
* @since 0.4.0
*/
p.maxDefault = 100;
 
/**
* The current number of active instances.
* #property length
* @type {Number}
*/
p.length = 0;
 
/**
* Initialize the channel.
* #method init
* @param {String} src The source of the channel
* @param {Number} max The maximum number of instances in the channel
* @protected
*/
p.init = function (src, max) {
this.src = src;
this.max = max || this.maxDefault;
if (this.max == -1) {this.max = this.maxDefault;}
this._instances = [];
};
 
/**
* Get an instance by index.
* #method get
* @param {Number} index The index to return.
* @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
*/
p._get = function (index) {
return this._instances[index];
};
 
/**
* Add a new instance to the channel.
* #method add
* @param {AbstractSoundInstance} instance The instance to add.
* @return {Boolean} The success of the method call. If the channel is full, it will return false.
*/
p._add = function (instance, interrupt) {
if (!this._getSlot(interrupt, instance)) {return false;}
this._instances.push(instance);
this.length++;
return true;
};
 
/**
* Remove an instance from the channel, either when it has finished playing, or it has been interrupted.
* #method remove
* @param {AbstractSoundInstance} instance The instance to remove
* @return {Boolean} The success of the remove call. If the instance is not found in this channel, it will
* return false.
*/
p._remove = function (instance) {
var index = createjs.indexOf(this._instances, instance);
if (index == -1) {return false;}
this._instances.splice(index, 1);
this.length--;
return true;
};
 
/**
* Stop playback and remove all instances from the channel. Usually in response to a delete call.
* #method removeAll
*/
p._removeAll = function () {
// Note that stop() removes the item from the list
for (var i=this.length-1; i>=0; i--) {
this._instances[i].stop();
}
};
 
/**
* Get an available slot depending on interrupt value and if slots are available.
* #method getSlot
* @param {String} interrupt The interrupt value to use.
* @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful.
* @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
* an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false.
*/
p._getSlot = function (interrupt, instance) {
var target, replacement;
 
if (interrupt != Sound.INTERRUPT_NONE) {
// First replacement candidate
replacement = this._get(0);
if (replacement == null) {
return true;
}
}
 
for (var i = 0, l = this.max; i < l; i++) {
target = this._get(i);
 
// Available Space
if (target == null) {
return true;
}
 
// Audio is complete or not playing
if (target.playState == Sound.PLAY_FINISHED ||
target.playState == Sound.PLAY_INTERRUPTED ||
target.playState == Sound.PLAY_FAILED) {
replacement = target;
break;
}
 
if (interrupt == Sound.INTERRUPT_NONE) {
continue;
}
 
// Audio is a better candidate than the current target, according to playhead
if ((interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) ||
(interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) {
replacement = target;
}
}
 
if (replacement != null) {
replacement._interrupt();
this._remove(replacement);
return true;
}
return false;
};
 
p.toString = function () {
return "[Sound SoundChannel]";
};
// do not add SoundChannel to namespace
 
}());
 
//##############################################################################
// AbstractSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
/**
* A AbstractSoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or
* {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The AbstractSoundInstance is returned by the active plugin
* for control by the user.
*
* <h4>Example</h4>
*
* var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
*
* A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound
* API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments.
*
* Once a AbstractSoundInstance is created, a reference can be stored that can be used to control the audio directly through
* the AbstractSoundInstance. If the reference is not stored, the AbstractSoundInstance will play out its audio (and any loops), and
* is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio
* playback has completed, a simple call to the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} instance method
* will rebuild the references the Sound class need to control it.
*
* var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2});
* myInstance.on("loop", handleLoop);
* function handleLoop(event) {
* myInstance.volume = myInstance.volume * 0.5;
* }
*
* Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails
*
* var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
* myInstance.on("complete", handleComplete);
* myInstance.on("loop", handleLoop);
* myInstance.on("failed", handleFailed);
*
*
* @class AbstractSoundInstance
* @param {String} src The path to and file name of the sound.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @param {Object} playbackResource Any resource needed by plugin to support audio playback.
* @extends EventDispatcher
* @constructor
*/
 
(function () {
"use strict";
 
 
// Constructor:
var AbstractSoundInstance = function (src, startTime, duration, playbackResource) {
this.EventDispatcher_constructor();
 
 
// public properties:
/**
* The source of the sound.
* @property src
* @type {String}
* @default null
*/
this.src = src;
 
/**
* The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}.
* @property uniqueId
* @type {String} | Number
* @default -1
*/
this.uniqueId = -1;
 
/**
* The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}.
* @property playState
* @type {String}
* @default null
*/
this.playState = null;
 
/**
* A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this AbstractSoundInstance is played with a delay.
* This allows AbstractSoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins.
* @property delayTimeoutId
* @type {timeoutVariable}
* @default null
* @protected
* @since 0.4.0
*/
this.delayTimeoutId = null;
// TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins
 
 
// private properties
// Getter / Setter Properties
// OJR TODO find original reason that we didn't use defined functions. I think it was performance related
/**
* The volume of the sound, between 0 and 1.
*
* The actual output volume of a sound can be calculated using:
* <code>myInstance.volume * createjs.Sound.getVolume();</code>
*
* @property volume
* @type {Number}
* @default 1
*/
this._volume = 1;
Object.defineProperty(this, "volume", {
get: this.getVolume,
set: this.setVolume
});
 
/**
* The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
*
* <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio.
*
* @property pan
* @type {Number}
* @default 0
*/
this._pan = 0;
Object.defineProperty(this, "pan", {
get: this.getPan,
set: this.setPan
});
 
/**
* Audio sprite property used to determine the starting offset.
* @property startTime
* @type {Number}
* @default 0
* @since 0.6.1
*/
this._startTime = Math.max(0, startTime || 0);
Object.defineProperty(this, "startTime", {
get: this.getStartTime,
set: this.setStartTime
});
 
/**
* Sets or gets the length of the audio clip, value is in milliseconds.
*
* @property duration
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._duration = Math.max(0, duration || 0);
Object.defineProperty(this, "duration", {
get: this.getDuration,
set: this.setDuration
});
 
/**
* Object that holds plugin specific resource need for audio playback.
* This is set internally by the plugin. For example, WebAudioPlugin will set an array buffer,
* HTMLAudioPlugin will set a tag, FlashAudioPlugin will set a flash reference.
*
* @property playbackResource
* @type {Object}
* @default null
*/
this._playbackResource = null;
Object.defineProperty(this, "playbackResource", {
get: this.getPlaybackResource,
set: this.setPlaybackResource
});
if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
 
/**
* The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
*
* @property position
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._position = 0;
Object.defineProperty(this, "position", {
get: this.getPosition,
set: this.setPosition
});
 
/**
* The number of play loops remaining. Negative values will loop infinitely.
*
* @property loop
* @type {Number}
* @default 0
* @public
* @since 0.6.0
*/
this._loop = 0;
Object.defineProperty(this, "loop", {
get: this.getLoop,
set: this.setLoop
});
 
/**
* Mutes or unmutes the current audio instance.
*
* @property muted
* @type {Boolean}
* @default false
* @since 0.6.0
*/
this._muted = false;
Object.defineProperty(this, "muted", {
get: this.getMuted,
set: this.setMuted
});
 
/**
* Pauses or resumes the current audio instance.
*
* @property paused
* @type {Boolean}
*/
this._paused = false;
Object.defineProperty(this, "paused", {
get: this.getPaused,
set: this.setPaused
});
 
 
// Events
/**
* The event that is fired when playback has started successfully.
* @event succeeded
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when playback is interrupted. This happens when another sound with the same
* src property is played using an interrupt value that causes this instance to stop playing.
* @event interrupted
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when playback has failed. This happens when there are too many channels with the same
* src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or
* the sound could not be played, perhaps due to a 404 error.
* @event failed
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when a sound has completed playing but has loops remaining.
* @event loop
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when playback completes. This means that the sound has finished playing in its
* entirety, including its loop iterations.
* @event complete
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
};
 
var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Public Methods:
/**
* Play an instance. This method is intended to be called on SoundInstances that already exist (created
* with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}).
*
* <h4>Example</h4>
*
* var myInstance = createjs.Sound.createInstance(mySrc);
* myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
*
* Note that if this sound is already playing, this call will still set the passed in parameters.
 
* <b>Parameters Deprecated</b><br />
* 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] <b>This parameter will be renamed playProps in the next release.</b><br />
* This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
* including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
* <br /><strong>OR</strong><br />
* <b>Deprecated</b> How to interrupt any currently playing instances of audio with the same source,
* if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
* constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
* @param {Number} [delay=0] <b>Deprecated</b> The amount of time to delay the start of audio playback, in milliseconds.
* @param {Number} [offset=0] <b>Deprecated</b> The offset from the start of the audio to begin playback, in milliseconds.
* @param {Number} [loop=0] <b>Deprecated</b> 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] <b>Deprecated</b> The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.
* @param {Number} [pan=0] <b>Deprecated</b> The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* Note that pan is not supported for HTML Audio.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.play = function (interrupt, delay, offset, loop, volume, pan) {
var playProps;
if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
playProps = createjs.PlayPropsConfig.create(interrupt);
} else {
playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan});
}
 
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this.applyPlayProps(playProps);
if (this._paused) { this.setPaused(false); }
return;
}
this._cleanUp();
createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
return this;
};
 
/**
* Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "AbstractSoundInstance/resume"}}{{/crossLink}}
* will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
*
* If you don't want to lose your position use yourSoundInstance.paused = true instead. {{#crossLink "AbstractSoundInstance/paused"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* myInstance.stop();
*
* @method stop
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.stop = function () {
this._position = 0;
this._paused = false;
this._handleStop();
this._cleanUp();
this.playState = createjs.Sound.PLAY_FINISHED;
return this;
};
 
/**
* Remove all external references and resources from AbstractSoundInstance. Note this is irreversible and AbstractSoundInstance will no longer work
* @method destroy
* @since 0.6.0
*/
p.destroy = function() {
this._cleanUp();
this.src = null;
this.playbackResource = null;
 
this.removeAllEventListeners();
};
 
/**
* Takes an PlayPropsConfig or Object with the same properties and sets them on this instance.
* @method applyPlayProps
* @param {PlayPropsConfig | Object} playProps A PlayPropsConfig or object containing the same properties.
* @since 0.6.1
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.applyPlayProps = function(playProps) {
if (playProps.offset != null) { this.setPosition(playProps.offset) }
if (playProps.loop != null) { this.setLoop(playProps.loop); }
if (playProps.volume != null) { this.setVolume(playProps.volume); }
if (playProps.pan != null) { this.setPan(playProps.pan); }
if (playProps.startTime != null) {
this.setStartTime(playProps.startTime);
this.setDuration(playProps.duration);
}
return this;
};
 
p.toString = function () {
return "[AbstractSoundInstance]";
};
 
// get/set methods that allow support for IE8
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property,
*
* @deprecated
* @method getPaused
* @returns {boolean} If the instance is currently paused
* @since 0.6.0
*/
p.getPaused = function() {
return this._paused;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPaused
* @param {boolean} value
* @since 0.6.0
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.setPaused = function (value) {
if ((value !== true && value !== false) || this._paused == value) {return;}
if (value == true && this.playState != createjs.Sound.PLAY_SUCCEEDED) {return;}
this._paused = value;
if(value) {
this._pause();
} else {
this._resume();
}
clearTimeout(this.delayTimeoutId);
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setVolume
* @param {Number} value The volume to set, between 0 and 1.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.setVolume = function (value) {
if (value == this._volume) { return this; }
this._volume = Math.max(0, Math.min(1, value));
if (!this._muted) {
this._updateVolume();
}
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getVolume
* @return {Number} The current volume of the sound instance.
*/
p.getVolume = function () {
return this._volume;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setMuted
* @param {Boolean} value If the sound should be muted.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
* @since 0.6.0
*/
p.setMuted = function (value) {
if (value !== true && value !== false) {return;}
this._muted = value;
this._updateVolume();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getMuted
* @return {Boolean} If the sound is muted.
* @since 0.6.0
*/
p.getMuted = function () {
return this._muted;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPan
* @param {Number} value The pan value, between -1 (left) and 1 (right).
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
p.setPan = function (value) {
if(value == this._pan) { return this; }
this._pan = Math.max(-1, Math.min(1, value));
this._updatePan();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getPan
* @return {Number} The value of the pan, between -1 (left) and 1 (right).
*/
p.getPan = function () {
return this._pan;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getPosition
* @return {Number} The position of the playhead in the sound, in milliseconds.
*/
p.getPosition = function () {
if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._position = this._calculateCurrentPosition();
}
return this._position;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPosition
* @param {Number} value The position to place the playhead, in milliseconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
p.setPosition = function (value) {
this._position = Math.max(0, value);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._updatePosition();
}
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getStartTime
* @return {Number} The startTime of the sound instance in milliseconds.
*/
p.getStartTime = function () {
return this._startTime;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setStartTime
* @param {number} value The new startTime time in milli seconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
p.setStartTime = function (value) {
if (value == this._startTime) { return this; }
this._startTime = Math.max(0, value || 0);
this._updateStartTime();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getDuration
* @return {Number} The duration of the sound instance in milliseconds.
*/
p.getDuration = function () {
return this._duration;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setDuration
* @param {number} value The new duration time in milli seconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
*/
p.setDuration = function (value) {
if (value == this._duration) { return this; }
this._duration = Math.max(0, value || 0);
this._updateDuration();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPlayback
* @param {Object} value The new playback resource.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
**/
p.setPlaybackResource = function (value) {
this._playbackResource = value;
if (this._duration == 0) { this._setDurationFromSource(); }
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPlayback
* @param {Object} value The new playback resource.
* @return {Object} playback resource used for playing audio
* @since 0.6.0
**/
p.getPlaybackResource = function () {
return this._playbackResource;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getLoop
* @return {number}
* @since 0.6.0
**/
p.getLoop = function () {
return this._loop;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property,
*
* @deprecated
* @method setLoop
* @param {number} value The number of times to loop after play.
* @since 0.6.0
*/
p.setLoop = function (value) {
if(this._playbackResource != null) {
// remove looping
if (this._loop != 0 && value == 0) {
this._removeLooping(value);
}
// add looping
else if (this._loop == 0 && value != 0) {
this._addLooping(value);
}
}
this._loop = value;
};
 
 
// Private Methods:
/**
* A helper method that dispatches all events for AbstractSoundInstance.
* @method _sendEvent
* @param {String} type The event type
* @protected
*/
p._sendEvent = function (type) {
var event = new createjs.Event(type);
this.dispatchEvent(event);
};
 
/**
* Clean up the instance. Remove references and clean up any additional properties such as timers.
* @method _cleanUp
* @protected
*/
p._cleanUp = function () {
clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
this._handleCleanUp();
this._paused = false;
 
createjs.Sound._playFinished(this); // TODO change to an event
};
 
/**
* The sound has been interrupted.
* @method _interrupt
* @protected
*/
p._interrupt = function () {
this._cleanUp();
this.playState = createjs.Sound.PLAY_INTERRUPTED;
this._sendEvent("interrupted");
};
 
/**
* Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the
* src is loaded, otherwise playback will fail.
* @method _beginPlaying
* @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If playback succeeded.
* @protected
*/
// OJR FlashAudioSoundInstance overwrites
p._beginPlaying = function (playProps) {
this.setPosition(playProps.offset);
this.setLoop(playProps.loop);
this.setVolume(playProps.volume);
this.setPan(playProps.pan);
if (playProps.startTime != null) {
this.setStartTime(playProps.startTime);
this.setDuration(playProps.duration);
}
 
if (this._playbackResource != null && this._position < this._duration) {
this._paused = false;
this._handleSoundReady();
this.playState = createjs.Sound.PLAY_SUCCEEDED;
this._sendEvent("succeeded");
return true;
} else {
this._playFailed();
return false;
}
};
 
/**
* Play has failed, which can happen for a variety of reasons.
* Cleans up instance and dispatches failed event
* @method _playFailed
* @private
*/
p._playFailed = function () {
this._cleanUp();
this.playState = createjs.Sound.PLAY_FAILED;
this._sendEvent("failed");
};
 
/**
* Audio has finished playing. Manually loop it if required.
* @method _handleSoundComplete
* @param event
* @protected
*/
p._handleSoundComplete = function (event) {
this._position = 0; // have to set this as it can be set by pause during playback
 
if (this._loop != 0) {
this._loop--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1
this._handleLoop();
this._sendEvent("loop");
return;
}
 
this._cleanUp();
this.playState = createjs.Sound.PLAY_FINISHED;
this._sendEvent("complete");
};
 
// Plugin specific code
/**
* Handles starting playback when the sound is ready for playing.
* @method _handleSoundReady
* @protected
*/
p._handleSoundReady = function () {
// plugin specific code
};
 
/**
* Internal function used to update the volume based on the instance volume, master volume, instance mute value,
* and master mute value.
* @method _updateVolume
* @protected
*/
p._updateVolume = function () {
// plugin specific code
};
 
/**
* Internal function used to update the pan
* @method _updatePan
* @protected
* @since 0.6.0
*/
p._updatePan = function () {
// plugin specific code
};
 
/**
* Internal function used to update the startTime of the audio.
* @method _updateStartTime
* @protected
* @since 0.6.1
*/
p._updateStartTime = function () {
// plugin specific code
};
 
/**
* Internal function used to update the duration of the audio.
* @method _updateDuration
* @protected
* @since 0.6.0
*/
p._updateDuration = function () {
// plugin specific code
};
 
/**
* Internal function used to get the duration of the audio from the source we'll be playing.
* @method _updateDuration
* @protected
* @since 0.6.0
*/
p._setDurationFromSource = function () {
// plugin specific code
};
 
/**
* Internal function that calculates the current position of the playhead and sets this._position to that value
* @method _calculateCurrentPosition
* @protected
* @since 0.6.0
*/
p._calculateCurrentPosition = function () {
// plugin specific code that sets this.position
};
 
/**
* Internal function used to update the position of the playhead.
* @method _updatePosition
* @protected
* @since 0.6.0
*/
p._updatePosition = function () {
// plugin specific code
};
 
/**
* Internal function called when looping is removed during playback.
* @method _removeLooping
* @param {number} value The number of times to loop after play.
* @protected
* @since 0.6.0
*/
p._removeLooping = function (value) {
// plugin specific code
};
 
/**
* Internal function called when looping is added during playback.
* @method _addLooping
* @param {number} value The number of times to loop after play.
* @protected
* @since 0.6.0
*/
p._addLooping = function (value) {
// plugin specific code
};
 
/**
* Internal function called when pausing playback
* @method _pause
* @protected
* @since 0.6.0
*/
p._pause = function () {
// plugin specific code
};
 
/**
* Internal function called when resuming playback
* @method _resume
* @protected
* @since 0.6.0
*/
p._resume = function () {
// plugin specific code
};
 
/**
* Internal function called when stopping playback
* @method _handleStop
* @protected
* @since 0.6.0
*/
p._handleStop = function() {
// plugin specific code
};
 
/**
* Internal function called when AbstractSoundInstance is being cleaned up
* @method _handleCleanUp
* @protected
* @since 0.6.0
*/
p._handleCleanUp = function() {
// plugin specific code
};
 
/**
* Internal function called when AbstractSoundInstance has played to end and is looping
* @method _handleLoop
* @protected
* @since 0.6.0
*/
p._handleLoop = function () {
// plugin specific code
};
 
createjs.AbstractSoundInstance = createjs.promote(AbstractSoundInstance, "EventDispatcher");
createjs.DefaultSoundInstance = createjs.AbstractSoundInstance; // used when no plugin is supported
}());
 
//##############################################################################
// AbstractPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
 
// constructor:
/**
* A default plugin class used as a base for all other plugins.
* @class AbstractPlugin
* @constructor
* @since 0.6.0
*/
 
var AbstractPlugin = function () {
// private properties:
/**
* The capabilities of the plugin.
* method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
* @protected
* @static
*/
this._capabilities = null;
 
/**
* Object hash indexed by the source URI of all created loaders, used to properly destroy them if sources are removed.
* @type {Object}
* @protected
*/
this._loaders = {};
 
/**
* Object hash indexed by the source URI of each file to indicate if an audio source has begun loading,
* is currently loading, or has completed loading. Can be used to store non boolean data after loading
* is complete (for example arrayBuffers for web audio).
* @property _audioSources
* @type {Object}
* @protected
*/
this._audioSources = {};
 
/**
* Object hash indexed by the source URI of all created SoundInstances, updates the playbackResource if it loads after they are created,
* and properly destroy them if sources are removed
* @type {Object}
* @protected
*/
this._soundInstances = {};
 
/**
* The internal master volume value of the plugin.
* @property _volume
* @type {Number}
* @default 1
* @protected
*/
this._volume = 1;
 
/**
* A reference to a loader class used by a plugin that must be set.
* @type {Object}
* @protected
*/
this._loaderClass;
 
/**
* A reference to an AbstractSoundInstance class used by a plugin that must be set.
* @type {Object}
* @protected;
*/
this._soundInstanceClass;
};
var p = AbstractPlugin.prototype;
 
/**
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
* for details.
*
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
*
* @method initialize
* @protected
* @deprecated
*/
// p.initialize = function() {}; // searchable for devs wondering where it is.
 
 
// Static Properties:
// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
/**
* The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
* @protected
* @static
*/
AbstractPlugin._capabilities = null;
 
/**
* Determine if the plugin can be used in the current browser/OS.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
AbstractPlugin.isSupported = function () {
return true;
};
 
 
// public methods:
/**
* Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}.
* Note all plugins provide a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a>
* can use to assist with preloading.
* @method register
* @param {String} loadItem An Object containing the source of the audio
* Note that not every plugin will manage this value.
* @return {Object} A result object, containing a "tag" for preloading purposes.
*/
p.register = function (loadItem) {
var loader = this._loaders[loadItem.src];
if(loader && !loader.canceled) {return this._loaders[loadItem.src];} // already loading/loaded this, so don't load twice
// OJR potential issue that we won't be firing loaded event, might need to trigger if this is already loaded?
this._audioSources[loadItem.src] = true;
this._soundInstances[loadItem.src] = [];
loader = new this._loaderClass(loadItem);
loader.on("complete", this._handlePreloadComplete, this);
this._loaders[loadItem.src] = loader;
return loader;
};
 
// note sound calls register before calling preload
/**
* Internally preload a sound.
* @method preload
* @param {Loader} loader The sound URI to load.
*/
p.preload = function (loader) {
loader.on("error", this._handlePreloadError, this);
loader.load();
};
 
/**
* Checks if preloading has started for a specific source. If the source is found, we can assume it is loading,
* or has already finished loading.
* @method isPreloadStarted
* @param {String} src The sound URI to check.
* @return {Boolean}
*/
p.isPreloadStarted = function (src) {
return (this._audioSources[src] != null);
};
 
/**
* Checks if preloading has finished for a specific source.
* @method isPreloadComplete
* @param {String} src The sound URI to load.
* @return {Boolean}
*/
p.isPreloadComplete = function (src) {
return (!(this._audioSources[src] == null || this._audioSources[src] == true));
};
 
/**
* Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
* @method removeSound
* @param {String} src The sound URI to unload.
*/
p.removeSound = function (src) {
if (!this._soundInstances[src]) { return; }
for (var i = this._soundInstances[src].length; i--; ) {
var item = this._soundInstances[src][i];
item.destroy();
}
delete(this._soundInstances[src]);
delete(this._audioSources[src]);
if(this._loaders[src]) { this._loaders[src].destroy(); }
delete(this._loaders[src]);
};
 
/**
* Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
* @method removeAllSounds
* @param {String} src The sound URI to unload.
*/
p.removeAllSounds = function () {
for(var key in this._audioSources) {
this.removeSound(key);
}
};
 
/**
* Create a sound instance. If the sound has not been preloaded, it is internally preloaded here.
* @method create
* @param {String} src The sound source to use.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @return {AbstractSoundInstance} A sound instance for playback and control.
*/
p.create = function (src, startTime, duration) {
if (!this.isPreloadStarted(src)) {
this.preload(this.register(src));
}
var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
this._soundInstances[src].push(si);
return si;
};
 
// if a plugin does not support volume and mute, it should set these to null
/**
* Set the master volume of the plugin, which affects all SoundInstances.
* @method setVolume
* @param {Number} value The volume to set, between 0 and 1.
* @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the
* instances manually otherwise.
*/
p.setVolume = function (value) {
this._volume = value;
this._updateVolume();
return true;
};
 
/**
* Get the master volume of the plugin, which affects all SoundInstances.
* @method getVolume
* @return {Number} The volume level, between 0 and 1.
*/
p.getVolume = function () {
return this._volume;
};
 
/**
* Mute all sounds via the plugin.
* @method setMute
* @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up
* the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here.
* @return {Boolean} If the mute call succeeds.
*/
p.setMute = function (value) {
this._updateVolume();
return true;
};
 
// plugins should overwrite this method
p.toString = function () {
return "[AbstractPlugin]";
};
 
 
// private methods:
/**
* Handles internal preload completion.
* @method _handlePreloadComplete
* @protected
*/
p._handlePreloadComplete = function (event) {
var src = event.target.getItem().src;
this._audioSources[src] = event.result;
for (var i = 0, l = this._soundInstances[src].length; i < l; i++) {
var item = this._soundInstances[src][i];
item.setPlaybackResource(this._audioSources[src]);
// ToDo consider adding play call here if playstate == playfailed
}
};
 
/**
* Handles internal preload erros
* @method _handlePreloadError
* @param event
* @protected
*/
p._handlePreloadError = function(event) {
//delete(this._audioSources[src]);
};
 
/**
* Set the gain value for master audio. Should not be called externally.
* @method _updateVolume
* @protected
*/
p._updateVolume = function () {
// Plugin Specific code
};
 
createjs.AbstractPlugin = AbstractPlugin;
}());
 
//##############################################################################
// WebAudioLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* Loader provides a mechanism to preload Web Audio content via PreloadJS or internally. Instances are returned to
* the preloader, and the load method is called when the asset needs to be requested.
*
* @class WebAudioLoader
* @param {String} loadItem The item to be loaded
* @extends XHRRequest
* @protected
*/
function Loader(loadItem) {
this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
 
};
var p = createjs.extend(Loader, createjs.AbstractLoader);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
/**
* web audio context required for decoding audio
* @property context
* @type {AudioContext}
* @static
*/
Loader.context = null;
 
 
// public methods
p.toString = function () {
return "[WebAudioLoader]";
};
 
 
// private methods
p._createRequest = function() {
this._request = new createjs.XHRRequest(this._item, false);
this._request.setResponseType("arraybuffer");
};
 
p._sendComplete = function (event) {
// OJR we leave this wrapped in Loader because we need to reference src and the handler only receives a single argument, the decodedAudio
Loader.context.decodeAudioData(this._rawResult,
createjs.proxy(this._handleAudioDecoded, this),
createjs.proxy(this._sendError, this));
};
 
 
/**
* The audio has been decoded.
* @method handleAudioDecoded
* @param decoded
* @protected
*/
p._handleAudioDecoded = function (decodedAudio) {
this._result = decodedAudio;
this.AbstractLoader__sendComplete();
};
 
createjs.WebAudioLoader = createjs.promote(Loader, "AbstractLoader");
}());
 
//##############################################################################
// WebAudioSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
/**
* WebAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
* {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
*
* WebAudioSoundInstance exposes audioNodes for advanced users.
*
* @param {String} src The path to and file name of the sound.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @param {Object} playbackResource Any resource needed by plugin to support audio playback.
* @class WebAudioSoundInstance
* @extends AbstractSoundInstance
* @constructor
*/
(function () {
"use strict";
 
function WebAudioSoundInstance(src, startTime, duration, playbackResource) {
this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
 
 
// public properties
/**
* NOTE this is only intended for use by advanced users.
* <br />GainNode for controlling <code>WebAudioSoundInstance</code> volume. Connected to the {{#crossLink "WebAudioSoundInstance/destinationNode:property"}}{{/crossLink}}.
* @property gainNode
* @type {AudioGainNode}
* @since 0.4.0
*
*/
this.gainNode = s.context.createGain();
 
/**
* NOTE this is only intended for use by advanced users.
* <br />A panNode allowing left and right audio channel panning only. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
* @property panNode
* @type {AudioPannerNode}
* @since 0.4.0
*/
this.panNode = s.context.createPanner();
this.panNode.panningModel = s._panningModel;
this.panNode.connect(this.gainNode);
this._updatePan();
 
/**
* NOTE this is only intended for use by advanced users.
* <br />sourceNode is the audio source. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/panNode:property"}}{{/crossLink}}.
* @property sourceNode
* @type {AudioNode}
* @since 0.4.0
*
*/
this.sourceNode = null;
 
 
// private properties
/**
* Timeout that is created internally to handle sound playing to completion.
* Stored so we can remove it when stop, pause, or cleanup are called
* @property _soundCompleteTimeout
* @type {timeoutVariable}
* @default null
* @protected
* @since 0.4.0
*/
this._soundCompleteTimeout = null;
 
/**
* NOTE this is only intended for use by very advanced users.
* _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth
* looping. Connected to {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
* @property _sourceNodeNext
* @type {AudioNode}
* @default null
* @protected
* @since 0.4.1
*
*/
this._sourceNodeNext = null;
 
/**
* Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
* @property _playbackStartTime
* @type {Number}
* @default 0
* @protected
* @since 0.4.0
*/
this._playbackStartTime = 0;
 
// Proxies, make removing listeners easier.
this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
};
var p = createjs.extend(WebAudioSoundInstance, createjs.AbstractSoundInstance);
var s = WebAudioSoundInstance;
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
/**
* Note this is only intended for use by advanced users.
* <br />Audio context used to create nodes. This is and needs to be the same context used by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
* @property context
* @type {AudioContext}
* @static
* @since 0.6.0
*/
s.context = null;
 
/**
* Note this is only intended for use by advanced users.
* <br />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.
* <br /> Audio node from WebAudioPlugin that sequences to <code>context.destination</code>
* @property destinationNode
* @type {AudioNode}
* @static
* @since 0.6.0
*/
s.destinationNode = null;
 
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
* @type {Number / String}
* @protected
* @static
* @since 0.6.0
*/
s._panningModel = "equalpower";
 
 
// Public methods
p.destroy = function() {
this.AbstractSoundInstance_destroy();
 
this.panNode.disconnect(0);
this.panNode = null;
this.gainNode.disconnect(0);
this.gainNode = null;
};
 
p.toString = function () {
return "[WebAudioSoundInstance]";
};
 
 
// Private Methods
p._updatePan = function() {
this.panNode.setPosition(this._pan, 0, -0.5);
// z need to be -0.5 otherwise the sound only plays in left, right, or center
};
 
p._removeLooping = function(value) {
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
 
p._addLooping = function(value) {
if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
};
 
p._setDurationFromSource = function () {
this._duration = this.playbackResource.duration * 1000;
};
 
p._handleCleanUp = function () {
if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
}
 
if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
// OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work.
 
clearTimeout(this._soundCompleteTimeout);
 
this._playbackStartTime = 0; // This is used by getPosition
};
 
/**
* Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection
* @method _cleanUpAudioNode
* @param audioNode
* @return {audioNode}
* @protected
* @since 0.4.1
*/
p._cleanUpAudioNode = function(audioNode) {
if(audioNode) {
audioNode.stop(0);
audioNode.disconnect(0);
// necessary to prevent leak on iOS Safari 7-9. will throw in almost all other
// browser implementations.
try { audioNode.buffer = s._scratchBuffer; } catch(e) {}
audioNode = null;
}
return audioNode;
};
 
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
 
var dur = this._duration * 0.001;
var pos = this._position * 0.001;
if (pos > dur) {pos = dur;}
this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
 
this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
 
if(this._loop != 0) {
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
}
};
 
/**
* Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
* @method _createAndPlayAudioNode
* @param {Number} startTime The time to add this to the web audio context, in seconds.
* @param {Number} offset The amount of time into the src audio to start playback, in seconds.
* @return {audioNode}
* @protected
* @since 0.4.1
*/
p._createAndPlayAudioNode = function(startTime, offset) {
var audioNode = s.context.createBufferSource();
audioNode.buffer = this.playbackResource;
audioNode.connect(this.panNode);
var dur = this._duration * 0.001;
audioNode.startTime = startTime + dur;
audioNode.start(audioNode.startTime, offset+(this._startTime*0.001), dur - offset);
return audioNode;
};
 
p._pause = function () {
this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
 
if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
 
clearTimeout(this._soundCompleteTimeout);
};
 
p._resume = function () {
this._handleSoundReady();
};
 
/*
p._handleStop = function () {
// web audio does not need to do anything extra
};
*/
 
p._updateVolume = function () {
var newVolume = this._muted ? 0 : this._volume;
if (newVolume != this.gainNode.gain.value) {
this.gainNode.gain.value = newVolume;
}
};
 
p._calculateCurrentPosition = function () {
return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
};
 
p._updatePosition = function () {
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
clearTimeout(this._soundCompleteTimeout);
 
if (!this._paused) {this._handleSoundReady();}
};
 
// OJR we are using a look ahead approach to ensure smooth looping.
// We add _sourceNodeNext to the audio context so that it starts playing even if this callback is delayed.
// This technique is described here: http://www.html5rocks.com/en/tutorials/audio/scheduling/
// NOTE the cost of this is that our audio loop may not always match the loop event timing precisely.
p._handleLoop = function () {
this._cleanUpAudioNode(this.sourceNode);
this.sourceNode = this._sourceNodeNext;
this._playbackStartTime = this.sourceNode.startTime;
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
};
 
p._updateDuration = function () {
if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._pause();
this._resume();
}
};
 
createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
}());
 
//##############################################################################
// WebAudioPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
"use strict";
 
/**
* Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used
* anywhere that it is supported. To change plugin priority, check out the Sound API
* {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
 
* <h4>Known Browser and OS issues for Web Audio</h4>
* <b>Firefox 25</b>
* <li>
* mp3 audio files do not load properly on all windows machines, reported <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>.
* <br />For this reason it is recommended to pass another FireFox-supported type (i.e. ogg) as the default
* extension, until this bug is resolved
* </li>
*
* <b>Webkit (Chrome and Safari)</b>
* <li>
* 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.
* </li>
*
* <b>iOS 6 limitations</b>
* <ul>
* <li>
* Sound is initially muted and will only unmute through play being called inside a user initiated event
* (touch/click). 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.
* </li>
* <li>
* 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.
* </li>
* </ul>
* @class WebAudioPlugin
* @extends AbstractPlugin
* @constructor
* @since 0.4.0
*/
function WebAudioPlugin() {
this.AbstractPlugin_constructor();
 
 
// Private Properties
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
* @type {Number / String}
* @protected
*/
this._panningModel = s._panningModel;;
 
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
* @property context
* @type {AudioContext}
*/
this.context = s.context;
 
/**
* A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
* It is connected to <code>context.destination</code>.
*
* Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode.
* @property dynamicsCompressorNode
* @type {AudioNode}
*/
this.dynamicsCompressorNode = this.context.createDynamicsCompressor();
this.dynamicsCompressorNode.connect(this.context.destination);
 
/**
* A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}.
*
* Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode.
* @property gainNode
* @type {AudioGainNode}
*/
this.gainNode = this.context.createGain();
this.gainNode.connect(this.dynamicsCompressorNode);
createjs.WebAudioSoundInstance.destinationNode = this.gainNode;
 
this._capabilities = s._capabilities;
 
this._loaderClass = createjs.WebAudioLoader;
this._soundInstanceClass = createjs.WebAudioSoundInstance;
 
this._addPropsToClasses();
}
var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Static Properties
var s = WebAudioPlugin;
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
* method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
* @protected
* @static
*/
s._capabilities = null;
 
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
* @type {Number / String}
* @protected
* @static
*/
s._panningModel = "equalpower";
 
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
*
* Advanced users can set this to an existing context, but <b>must</b> do so before they call
* {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
*
* @property context
* @type {AudioContext}
* @static
*/
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 <b>must</b> do so before they call
* {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
*
* @property _scratchBuffer
* @type {AudioBuffer}
* @protected
* @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;
 
 
// Static Public Methods
/**
* Determine if the plugin can be used in the current browser/OS.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
// check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file
var isMobilePhoneGap = createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry;
// OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing.
if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally
s._generateCapabilities();
if (s.context == null) {return false;}
return true;
};
 
/**
* Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they
* require the first sound to be played inside of a user initiated event (touch/click). This is called when
* {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}
* for example).
*
* <h4>Example</h4>
*
* function handleTouch(event) {
* createjs.WebAudioPlugin.playEmptySound();
* }
*
* @method playEmptySound
* @static
* @since 0.4.1
*/
s.playEmptySound = function() {
if (s.context == null) {return;}
var source = s.context.createBufferSource();
source.buffer = s._scratchBuffer;
source.connect(s.context.destination);
source.start(0, 0, 0);
};
 
 
// Static Private Methods
/**
* Determine if XHR is supported, which is necessary for web audio.
* @method _isFileXHRSupported
* @return {Boolean} If XHR is supported.
* @since 0.4.2
* @protected
* @static
*/
s._isFileXHRSupported = function() {
// it's much easier to detect when something goes wrong, so let's start optimistically
var supported = true;
 
var xhr = new XMLHttpRequest();
try {
xhr.open("GET", "WebAudioPluginTest.fail", false); // loading non-existant file triggers 404 only if it could load (synchronous call)
} catch (error) {
// catch errors in cases where the onerror is passed by
supported = false;
return supported;
}
xhr.onerror = function() { supported = false; }; // cause irrelevant
// with security turned off, we can get empty success results, which is actually a failed read (status code 0?)
xhr.onload = function() { supported = this.status == 404 || (this.status == 200 || (this.status == 0 && this.response != "")); };
try {
xhr.send();
} catch (error) {
// catch errors in cases where the onerror is passed by
supported = false;
}
 
return supported;
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
// Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section
var t = document.createElement("audio");
if (t.canPlayType == null) {return null;}
 
if (s.context == null) {
if (window.AudioContext) {
s.context = new AudioContext();
} else if (window.webkitAudioContext) {
s.context = new webkitAudioContext();
} else {
return null;
}
}
if (s._scratchBuffer == null) {
s._scratchBuffer = s.context.createBuffer(1, 1, 22050);
}
 
s._compatibilitySetUp();
 
// 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("touchend", s._unlock, true);
}
 
 
s._capabilities = {
panning:true,
volume:true,
tracks:-1
};
 
// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
var extensionMap = createjs.Sound.EXTENSION_MAP;
for (var i = 0, l = supportedExtensions.length; i < l; i++) {
var ext = supportedExtensions[i];
var playType = extensionMap[ext] || ext;
s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
} // OJR another way to do this might be canPlayType:"m4a", codex: mp4
 
// 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround.
// See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels.
if (s.context.destination.numberOfChannels < 2) {
s._capabilities.panning = false;
}
};
 
/**
* Set up compatibility if only deprecated web audio calls are supported.
* See http://www.w3.org/TR/webaudio/#DeprecationNotes
* Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that
* don't support new calls.
*
* @method _compatibilitySetUp
* @static
* @protected
* @since 0.4.2
*/
s._compatibilitySetUp = function() {
s._panningModel = "equalpower";
//assume that if one new call is supported, they all are
if (s.context.createGain) { return; }
 
// simple name change, functionality the same
s.context.createGain = s.context.createGainNode;
 
// source node, add to prototype
var audioNode = s.context.createBufferSource();
audioNode.__proto__.start = audioNode.__proto__.noteGrainOn; // note that noteGrainOn requires all 3 parameters
audioNode.__proto__.stop = audioNode.__proto__.noteOff;
 
// panningModel
s._panningModel = 0;
};
 
/**
* 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);
s._unlocked = true;
}
};
 
 
// Public Methods
p.toString = function () {
return "[WebAudioPlugin]";
};
 
 
// Private Methods
/**
* Set up needed properties on supported classes WebAudioSoundInstance and WebAudioLoader.
* @method _addPropsToClasses
* @static
* @protected
* @since 0.6.0
*/
p._addPropsToClasses = function() {
var c = this._soundInstanceClass;
c.context = this.context;
c._scratchBuffer = s._scratchBuffer;
c.destinationNode = this.gainNode;
c._panningModel = this._panningModel;
 
this._loaderClass.context = this.context;
};
 
 
/**
* Set the gain value for master audio. Should not be called externally.
* @method _updateVolume
* @protected
*/
p._updateVolume = function () {
var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
if (newVolume != this.gainNode.gain.value) {
this.gainNode.gain.value = newVolume;
}
};
 
createjs.WebAudioPlugin = createjs.promote(WebAudioPlugin, "AbstractPlugin");
}());
 
//##############################################################################
// HTMLAudioTagPool.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
* @class HTMLAudioTagPool
* @param {String} src The source of the channel.
* @protected
*/
function HTMLAudioTagPool() {
throw "HTMLAudioTagPool cannot be instantiated";
}
 
var s = HTMLAudioTagPool;
 
// Static Properties
/**
* A hash lookup of each base audio tag, indexed by the audio source.
* @property _tags
* @type {{}}
* @static
* @protected
*/
s._tags = {};
 
/**
* An object pool for html audio tags
* @property _tagPool
* @type {TagPool}
* @static
* @protected
*/
s._tagPool = new TagPool();
 
/**
* A hash lookup of if a base audio tag is available, indexed by the audio source
* @property _tagsUsed
* @type {{}}
* @protected
* @static
*/
s._tagUsed = {};
 
// Static Methods
/**
* Get an audio tag with the given source.
* @method get
* @param {String} src The source file used by the audio tag.
* @static
*/
s.get = function (src) {
var t = s._tags[src];
if (t == null) {
// create new base tag
t = s._tags[src] = s._tagPool.get();
t.src = src;
} else {
// get base or pool
if (s._tagUsed[src]) {
t = s._tagPool.get();
t.src = src;
} else {
s._tagUsed[src] = true;
}
}
return t;
};
 
/**
* Return an audio tag to the pool.
* @method set
* @param {String} src The source file used by the audio tag.
* @param {HTMLElement} tag Audio tag to set.
* @static
*/
s.set = function (src, tag) {
// check if this is base, if yes set boolean if not return to pool
if(tag == s._tags[src]) {
s._tagUsed[src] = false;
} else {
s._tagPool.set(tag);
}
};
 
/**
* Delete stored tag reference and return them to pool. Note that if the tag reference does not exist, this will fail.
* @method remove
* @param {String} src The source for the tag
* @return {Boolean} If the TagPool was deleted.
* @static
*/
s.remove = function (src) {
var tag = s._tags[src];
if (tag == null) {return false;}
s._tagPool.set(tag);
delete(s._tags[src]);
delete(s._tagUsed[src]);
return true;
};
 
/**
* Gets the duration of the src audio in milliseconds
* @method getDuration
* @param {String} src The source file used by the audio tag.
* @return {Number} Duration of src in milliseconds
* @static
*/
s.getDuration= function (src) {
var t = s._tags[src];
if (t == null || !t.duration) {return 0;} // OJR duration is NaN if loading has not completed
return t.duration * 1000;
};
 
createjs.HTMLAudioTagPool = HTMLAudioTagPool;
 
 
// ************************************************************************************************************
/**
* The TagPool is an object pool for HTMLAudio tag instances.
* #class TagPool
* @param {String} src The source of the channel.
* @protected
*/
function TagPool(src) {
 
// Public Properties
/**
* A list of all available tags in the pool.
* #property tags
* @type {Array}
* @protected
*/
this._tags = [];
};
 
var p = TagPool.prototype;
p.constructor = TagPool;
 
 
// Public Methods
/**
* Get an HTMLAudioElement for immediate playback. This takes it out of the pool.
* #method get
* @return {HTMLAudioElement} An HTML audio tag.
*/
p.get = function () {
var tag;
if (this._tags.length == 0) {
tag = this._createTag();
} else {
tag = this._tags.pop();
}
if (tag.parentNode == null) {document.body.appendChild(tag);}
return tag;
};
 
/**
* Put an HTMLAudioElement back in the pool for use.
* #method set
* @param {HTMLAudioElement} tag HTML audio tag
*/
p.set = function (tag) {
// OJR this first step seems unnecessary
var index = createjs.indexOf(this._tags, tag);
if (index == -1) {
this._tags.src = null;
this._tags.push(tag);
}
};
 
p.toString = function () {
return "[TagPool]";
};
 
 
// Private Methods
/**
* Create an HTML audio tag.
* #method _createTag
* @param {String} src The source file to set for the audio tag.
* @return {HTMLElement} Returns an HTML audio tag.
* @protected
*/
p._createTag = function () {
var tag = document.createElement("audio");
tag.autoplay = false;
tag.preload = "none";
//LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
return tag;
};
 
}());
 
//##############################################################################
// HTMLAudioSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* HTMLAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
* {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
*
* @param {String} src The path to and file name of the sound.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @param {Object} playbackResource Any resource needed by plugin to support audio playback.
* @class HTMLAudioSoundInstance
* @extends AbstractSoundInstance
* @constructor
*/
function HTMLAudioSoundInstance(src, startTime, duration, playbackResource) {
this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
 
 
// Private Properties
this._audioSpriteStopTime = null;
this._delayTimeoutId = null;
 
// Proxies, make removing listeners easier.
this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
this._readyHandler = createjs.proxy(this._handleTagReady, this);
this._stalledHandler = createjs.proxy(this._playFailed, this);
this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
 
if (duration) {
this._audioSpriteStopTime = (startTime + duration) * 0.001;
} else {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
}
}
var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Public Methods
/**
* Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
* undoc'd because it is not meant to be used outside of Sound
* #method setMasterVolume
* @param value
*/
p.setMasterVolume = function (value) {
this._updateVolume();
};
 
/**
* Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master mute.
* undoc'd because it is not meant to be used outside of Sound
* #method setMasterMute
* @param value
*/
p.setMasterMute = function (isMuted) {
this._updateVolume();
};
 
p.toString = function () {
return "[HTMLAudioSoundInstance]";
};
 
//Private Methods
p._removeLooping = function() {
if(this._playbackResource == null) {return;}
this._playbackResource.loop = false;
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
};
 
p._addLooping = function() {
if(this._playbackResource == null || this._audioSpriteStopTime) {return;}
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
this._playbackResource.loop = true;
};
 
p._handleCleanUp = function () {
var tag = this._playbackResource;
if (tag != null) {
tag.pause();
tag.loop = false;
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
 
try {
tag.currentTime = this._startTime;
} catch (e) {
} // Reset Position
createjs.HTMLAudioTagPool.set(this.src, tag);
this._playbackResource = null;
}
};
 
p._beginPlaying = function (playProps) {
this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
return this.AbstractSoundInstance__beginPlaying(playProps);
};
 
p._handleSoundReady = function (event) {
if (this._playbackResource.readyState !== 4) {
var tag = this._playbackResource;
tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set.
tag.load();
return;
}
 
this._updateVolume();
this._playbackResource.currentTime = (this._startTime + this._position) * 0.001;
if (this._audioSpriteStopTime) {
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
} else {
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
if(this._loop != 0) {
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
this._playbackResource.loop = true;
}
}
 
this._playbackResource.play();
};
 
/**
* Used to handle when a tag is not ready for immediate playback when it is returned from the HTMLAudioTagPool.
* @method _handleTagReady
* @param event
* @protected
*/
p._handleTagReady = function (event) {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
 
this._handleSoundReady();
};
 
p._pause = function () {
this._playbackResource.pause();
};
 
p._resume = function () {
this._playbackResource.play();
};
 
p._updateVolume = function () {
if (this._playbackResource != null) {
var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
if (newVolume != this._playbackResource.volume) {this._playbackResource.volume = newVolume;}
}
};
 
p._calculateCurrentPosition = function() {
return (this._playbackResource.currentTime * 1000) - this._startTime;
};
 
p._updatePosition = function() {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
try {
this._playbackResource.currentTime = (this._position + this._startTime) * 0.001;
} catch (error) { // Out of range
this._handleSetPositionSeek(null);
}
};
 
/**
* Used to enable setting position, as we need to wait for that seek to be done before we add back our loop handling seek listener
* @method _handleSetPositionSeek
* @param event
* @protected
*/
p._handleSetPositionSeek = function(event) {
if (this._playbackResource == null) { return; }
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
};
 
/**
* Timer used to loop audio sprites.
* NOTE because of the inaccuracies in the timeupdate event (15 - 250ms) and in setting the tag to the desired timed
* (up to 300ms), it is strongly recommended not to loop audio sprites with HTML Audio if smooth looping is desired
*
* @method _handleAudioSpriteLoop
* @param event
* @private
*/
p._handleAudioSpriteLoop = function (event) {
if(this._playbackResource.currentTime <= this._audioSpriteStopTime) {return;}
this._playbackResource.pause();
if(this._loop == 0) {
this._handleSoundComplete(null);
} else {
this._position = 0;
this._loop--;
this._playbackResource.currentTime = this._startTime * 0.001;
if(!this._paused) {this._playbackResource.play();}
this._sendEvent("loop");
}
};
 
// NOTE with this approach audio will loop as reliably as the browser allows
// but we could end up sending the loop event after next loop playback begins
p._handleLoop = function (event) {
if(this._loop == 0) {
this._playbackResource.loop = false;
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
}
};
 
p._updateStartTime = function () {
this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
 
if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
}
};
 
p._updateDuration = function () {
this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
 
if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
}
};
 
p._setDurationFromSource = function () {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
this._playbackResource = null;
};
 
createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());
 
//##############################################################################
// HTMLAudioPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
"use strict";
 
/**
* Play sounds using HTML &lt;audio&gt; tags in the browser. This plugin is the second priority plugin installed
* by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. For older browsers that do not support html
* audio, include and install the {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
*
* <h4>Known Browser and OS issues for HTML Audio</h4>
* <b>All browsers</b><br />
* Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed
* this limit, you can expect to see unpredictable results. 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.
*
* <b>IE html limitations</b><br />
* <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
* muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
* when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
* <li>MP3 encoding will not always work for audio tags if it's not default. We've found default encoding with
* 64kbps works.</li>
* <li>Occasionally very short samples will get cut off.</li>
* <li>There is a limit to how many audio tags you can load 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.</li></ul>
*
* <b>Safari limitations</b><br />
* <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
*
* <b>iOS 6 limitations</b><br />
* <ul><li>can only have one &lt;audio&gt; tag</li>
* <li>can not preload or autoplay the audio</li>
* <li>can not cache the audio</li>
* <li>can not play the audio except inside a user initiated event.</li>
* <li>Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)</li>
* <li>audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS</li>
* </ul>
*
* <b>Android Native Browser limitations</b><br />
* <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
* <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.</li></ul>
* <b> Android Chrome 26.0.1410.58 specific limitations</b><br />
* <ul> <li>Can only play 1 sound at a time.</li>
* <li>Sound is not cached.</li>
* <li>Sound can only be loaded in a user initiated touch/click event.</li>
* <li>There is a delay before a sound is played, presumably while the src is loaded.</li>
* </ul>
*
* See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues.
*
* @class HTMLAudioPlugin
* @extends AbstractPlugin
* @constructor
*/
function HTMLAudioPlugin() {
this.AbstractPlugin_constructor();
 
 
// Public Properties
/**
* This is no longer needed as we are now using object pooling for tags.
*
* <b>NOTE this property only exists as a limitation of HTML audio.</b>
* @property defaultNumChannels
* @type {Number}
* @default 2
* @since 0.4.0
* @deprecated
*/
this.defaultNumChannels = 2;
 
this._capabilities = s._capabilities;
 
this._loaderClass = createjs.SoundLoader;
this._soundInstanceClass = createjs.HTMLAudioSoundInstance;
}
 
var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
var s = HTMLAudioPlugin;
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Static Properties
/**
* The maximum number of instances that can be loaded or played. This is a browser limitation, primarily limited to IE9.
* The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate.
* Audio sprites work around this limitation.
* @property MAX_INSTANCES
* @type {Number}
* @default 30
* @static
*/
s.MAX_INSTANCES = 30;
 
/**
* Event constant for the "canPlayThrough" event for cleaner code.
* @property _AUDIO_READY
* @type {String}
* @default canplaythrough
* @static
* @protected
*/
s._AUDIO_READY = "canplaythrough";
 
/**
* Event constant for the "ended" event for cleaner code.
* @property _AUDIO_ENDED
* @type {String}
* @default ended
* @static
* @protected
*/
s._AUDIO_ENDED = "ended";
 
/**
* Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events.
* @property _AUDIO_SEEKED
* @type {String}
* @default seeked
* @static
* @protected
*/
s._AUDIO_SEEKED = "seeked";
 
/**
* Event constant for the "stalled" event for cleaner code.
* @property _AUDIO_STALLED
* @type {String}
* @default stalled
* @static
* @protected
*/
s._AUDIO_STALLED = "stalled";
 
/**
* Event constant for the "timeupdate" event for cleaner code. Utilized for looping audio sprites.
* This event callsback ever 15 to 250ms and can be dropped by the browser for performance.
* @property _TIME_UPDATE
* @type {String}
* @default timeupdate
* @static
* @protected
*/
s._TIME_UPDATE = "timeupdate";
 
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}}
* method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
* @protected
* @static
*/
s._capabilities = null;
 
 
// Static Methods
/**
* Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
* browsers, but is disabled in iOS because of its limitations.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
s._generateCapabilities();
return (s._capabilities != null);
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
var t = document.createElement("audio");
if (t.canPlayType == null) {return null;}
 
s._capabilities = {
panning:false,
volume:true,
tracks:-1
};
 
// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
var extensionMap = createjs.Sound.EXTENSION_MAP;
for (var i = 0, l = supportedExtensions.length; i < l; i++) {
var ext = supportedExtensions[i];
var playType = extensionMap[ext] || ext;
s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
} // OJR another way to do this might be canPlayType:"m4a", codex: mp4
};
 
 
// public methods
p.register = function (loadItem) {
var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
var loader = this.AbstractPlugin_register(loadItem);
loader.setTag(tag);
 
return loader;
};
 
p.removeSound = function (src) {
this.AbstractPlugin_removeSound(src);
createjs.HTMLAudioTagPool.remove(src);
};
 
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
si.setPlaybackResource(null);
return si;
};
 
p.toString = function () {
return "[HTMLAudioPlugin]";
};
 
// plugin does not support these
p.setVolume = p.getVolume = p.setMute = null;
 
 
createjs.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
}());
/bower_components/SoundJS/lib/soundjs-0.6.2.min.js
@@ -0,0 +1,18 @@
/*!
* @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="0.6.2",a.buildDate="Thu, 26 Nov 2015 20:44:31 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.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=1==b?this._captureListeners:this._listeners;if(a&&d){var e=d[a.type];if(!e||!(c=e.length))return;try{a.currentTarget=this}catch(f){}try{a.eventPhase=b}catch(f){}a.removed=!1,e=e.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=e[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}},createjs.EventDispatcher=EventDispatcher}(),this.createjs=this.createjs||{},function(){"use strict";function Event(a,b,c){this.type=a,this.target=null,this.currentTarget=null,this.eventPhase=0,this.bubbles=!!b,this.cancelable=!!c,this.timeStamp=(new Date).getTime(),this.defaultPrevented=!1,this.propagationStopped=!1,this.immediatePropagationStopped=!1,this.removed=!1}var a=Event.prototype;a.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},a.stopPropagation=function(){this.propagationStopped=!0},a.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},a.remove=function(){this.removed=!0},a.clone=function(){return new Event(this.type,this.bubbles,this.cancelable)},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=Event}(),this.createjs=this.createjs||{},function(){"use strict";function ErrorEvent(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var a=createjs.extend(ErrorEvent,createjs.Event);a.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(ErrorEvent,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function ProgressEvent(a,b){this.Event_constructor("progress"),this.loaded=a,this.total=null==b?1:b,this.progress=0==b?0:this.loaded/this.total}var a=createjs.extend(ProgressEvent,createjs.Event);a.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(ProgressEvent,"Event")}(window),this.createjs=this.createjs||{},function(){"use strict";function LoadItem(){this.src=null,this.type=null,this.id=null,this.maintainOrder=!1,this.callback=null,this.data=null,this.method=createjs.LoadItem.GET,this.values=null,this.headers=null,this.withCredentials=!1,this.mimeType=null,this.crossOrigin=null,this.loadTimeout=b.LOAD_TIMEOUT_DEFAULT}var a=LoadItem.prototype={},b=LoadItem;b.LOAD_TIMEOUT_DEFAULT=8e3,b.create=function(a){if("string"==typeof a){var c=new LoadItem;return c.src=a,c}if(a instanceof b)return a;if(a instanceof Object&&a.src)return null==a.loadTimeout&&(a.loadTimeout=b.LOAD_TIMEOUT_DEFAULT),a;throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=b}(),function(){var a={};a.ABSOLUTE_PATT=/^(?:\w+:)?\/{2}/i,a.RELATIVE_PATT=/^[.\/]*?\//i,a.EXTENSION_PATT=/\/?[^\/]+\.(\w{1,5})$/i,a.parseURI=function(b){var c={absolute:!1,relative:!1};if(null==b)return c;var d=b.indexOf("?");d>-1&&(b=b.substr(0,d));var e;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(e=b.match(a.EXTENSION_PATT))&&(c.extension=e[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildPath=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=document.createElement("a");b.href=a.src;var c=document.createElement("a");c.href=location.href;var d=""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname);return d},a.isLocal=function(a){var b=document.createElement("a");return b.href=a.src,""==b.hostname&&"file:"==b.protocol},a.isBinary=function(a){switch(a){case createjs.AbstractLoader.IMAGE:case createjs.AbstractLoader.BINARY:return!0;default:return!1}},a.isImageTag=function(a){return a instanceof HTMLImageElement},a.isAudioTag=function(a){return window.HTMLAudioElement?a instanceof HTMLAudioElement:!1},a.isVideoTag=function(a){return window.HTMLVideoElement?a instanceof HTMLVideoElement:!1},a.isText=function(a){switch(a){case createjs.AbstractLoader.TEXT:case createjs.AbstractLoader.JSON:case createjs.AbstractLoader.MANIFEST:case createjs.AbstractLoader.XML:case createjs.AbstractLoader.CSS:case createjs.AbstractLoader.SVG:case createjs.AbstractLoader.JAVASCRIPT:case createjs.AbstractLoader.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.AbstractLoader.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.AbstractLoader.IMAGE;case"ogg":case"mp3":case"webm":return createjs.AbstractLoader.SOUND;case"mp4":case"webm":case"ts":return createjs.AbstractLoader.VIDEO;case"json":return createjs.AbstractLoader.JSON;case"xml":return createjs.AbstractLoader.XML;case"css":return createjs.AbstractLoader.CSS;case"js":return createjs.AbstractLoader.JAVASCRIPT;case"svg":return createjs.AbstractLoader.SVG;default:return createjs.AbstractLoader.TEXT}},createjs.RequestUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;b.POST="POST",b.GET="GET",b.BINARY="binary",b.CSS="css",b.IMAGE="image",b.JAVASCRIPT="javascript",b.JSON="json",b.JSONP="jsonp",b.MANIFEST="manifest",b.SOUND="sound",b.VIDEO="video",b.SPRITESHEET="spritesheet",b.SVG="svg",b.TEXT="text",b.XML="xml",a.getItem=function(){return this._item},a.getResult=function(a){return a?this._rawResult:this._result},a.getTag=function(){return this._tag},a.setTag=function(a){this._tag=a},a.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},a.cancel=function(){this.canceled=!0,this.destroy()},a.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},a.getLoadedItems=function(){return this._loadedItems},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._createTag=function(){return null},a._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},a._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||1/0==this.progress)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},a._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},a._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},a._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},a.resultFormatter=null,a.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this);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.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",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,this._startTagVisibility=null}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},a._showTag=function(){this._tag.style.visibility=this._startTagVisibility},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();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._item.method!=createjs.AbstractLoader.GET?this._item.method==createjs.AbstractLoader.POST&&this._request.send(createjs.RequestUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){"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);switch(a){case 404:case 0:return new Error(a)}return null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.RequestUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){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.AbstractLoader.GET?createjs.RequestUtils.buildPath(a.src,a.values):a.src,d.open(a.method||createjs.AbstractLoader.GET,i,!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 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.AbstractLoader.SOUND),createjs.RequestUtils.isAudioTag(a)?this._tag=a:createjs.RequestUtils.isAudioTag(a.src)?this._tag=a:createjs.RequestUtils.isAudioTag(a.tag)&&(this._tag=createjs.RequestUtils.isAudioTag(a)?a:a.src),null!=this._tag&&(this._preferXHR=!1)}var a=createjs.extend(SoundLoader,createjs.AbstractMediaLoader),b=SoundLoader;b.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},a._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(SoundLoader,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var PlayPropsConfig=function(){this.interrupt=null,this.delay=null,this.offset=null,this.loop=null,this.volume=null,this.pan=null,this.startTime=null,this.duration=null},a=PlayPropsConfig.prototype={},b=PlayPropsConfig;b.create=function(a){if(a instanceof b||a instanceof Object){var c=new createjs.PlayPropsConfig;return c.set(a),c}throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[PlayPropsConfig]"},createjs.PlayPropsConfig=b}(),this.createjs=this.createjs||{},function(){"use strict";function Sound(){throw"Sound cannot be instantiated"}function a(a,b){this.init(a,b)}var b=Sound;b.INTERRUPT_ANY="any",b.INTERRUPT_EARLY="early",b.INTERRUPT_LATE="late",b.INTERRUPT_NONE="none",b.PLAY_INITED="playInited",b.PLAY_SUCCEEDED="playSucceeded",b.PLAY_INTERRUPTED="playInterrupted",b.PLAY_FINISHED="playFinished",b.PLAY_FAILED="playFailed",b.SUPPORTED_EXTENSIONS=["mp3","ogg","opus","mpeg","wav","m4a","mp4","aiff","wma","mid"],b.EXTENSION_MAP={m4a:"mp4"},b.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([\/.]*?(?:[^?]+)?\/)?((?:[^\/?]+)\.(\w+))(?:\?(\S+)?)?$/,b.defaultInterruptBehavior=b.INTERRUPT_NONE,b.alternateExtensions=[],b.activePlugin=null,b._masterVolume=1,Object.defineProperty(b,"volume",{get:function(){return this._masterVolume},set:function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)}}),b._masterMute=!1,Object.defineProperty(b,"muted",{get:function(){return this._masterMute},set:function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0}}),Object.defineProperty(b,"capabilities",{get:function(){return null==b.activePlugin?null:b.activePlugin._capabilities},set:function(){return!1}}),b._pluginsRegistered=!1,b._lastID=0,b._instances=[],b._idHash={},b._preloadHash={},b._defaultPlayPropsHash={},b.addEventListener=null,b.removeEventListener=null,b.removeAllEventListeners=null,b.dispatchEvent=null,b.hasEventListener=null,b._listeners=null,createjs.EventDispatcher.initialize(b),b.getPreloadHandlers=function(){return{callback:createjs.proxy(b.initLoad,b),types:["sound"],extensions:b.SUPPORTED_EXTENSIONS}},b._handleLoadComplete=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!0,b.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._handleLoadError=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!1,b.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._registerPlugin=function(a){return a.isSupported()?(b.activePlugin=new a,!0):!1},b.registerPlugins=function(a){b._pluginsRegistered=!0;for(var c=0,d=a.length;d>c;c++)if(b._registerPlugin(a[c]))return!0;return!1},b.initializeDefaultPlugins=function(){return null!=b.activePlugin?!0:b._pluginsRegistered?!1:b.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},b.isReady=function(){return null!=b.activePlugin},b.getCapabilities=function(){return null==b.activePlugin?null:b.activePlugin._capabilities},b.getCapability=function(a){return null==b.activePlugin?null:b.activePlugin._capabilities[a]},b.initLoad=function(a){return b._registerSound(a)},b._registerSound=function(c){if(!b.initializeDefaultPlugins())return!1;var d;if(c.src instanceof Object?(d=b._parseSrc(c.src),d.src=c.path+d.src):d=b._parsePath(c.src),null==d)return!1;c.src=d.src,c.type="sound";var e=c.data,f=null;if(null!=e&&(isNaN(e.channels)?isNaN(e)||(f=parseInt(e)):f=parseInt(e.channels),e.audioSprite))for(var g,h=e.audioSprite.length;h--;)g=e.audioSprite[h],b._idHash[g.id]={src:c.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)},g.defaultPlayProps&&(b._defaultPlayPropsHash[g.id]=createjs.PlayPropsConfig.create(g.defaultPlayProps));null!=c.id&&(b._idHash[c.id]={src:c.src});var i=b.activePlugin.register(c);return a.create(c.src,f),null!=e&&isNaN(e)?c.data.channels=f||a.maxPerChannel():c.data=f||a.maxPerChannel(),i.type&&(c.type=i.type),c.defaultPlayProps&&(b._defaultPlayPropsHash[c.src]=createjs.PlayPropsConfig.create(c.defaultPlayProps)),i},b.registerSound=function(a,c,d,e,f){var g={src:a,id:c,data:d,defaultPlayProps:f};a instanceof Object&&a.src&&(e=c,g=a),g=createjs.LoadItem.create(g),g.path=e,null==e||g.src instanceof Object||(g.src=e+a);var h=b._registerSound(g);if(!h)return!1;if(b._preloadHash[g.src]||(b._preloadHash[g.src]=[]),b._preloadHash[g.src].push(g),1==b._preloadHash[g.src].length)h.on("complete",createjs.proxy(this._handleLoadComplete,this)),h.on("error",createjs.proxy(this._handleLoadError,this)),b.activePlugin.preload(h);else if(1==b._preloadHash[g.src][0])return!0;return g},b.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.registerSound(a[d].src,a[d].id,a[d].data,b,a[d].defaultPlayProps);return c},b.removeSound=function(c,d){if(null==b.activePlugin)return!1;c instanceof Object&&c.src&&(c=c.src);var e;if(c instanceof Object?e=b._parseSrc(c):(c=b._getSrcById(c).src,e=b._parsePath(c)),null==e)return!1;c=e.src,null!=d&&(c=d+c);for(var f in b._idHash)b._idHash[f].src==c&&delete b._idHash[f];return a.removeSrc(c),delete b._preloadHash[c],b.activePlugin.removeSound(c),!0},b.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},b.removeAllSounds=function(){b._idHash={},b._preloadHash={},a.removeAll(),b.activePlugin&&b.activePlugin.removeAllSounds()},b.loadComplete=function(a){if(!b.isReady())return!1;var c=b._parsePath(a);return a=c?b._getSrcById(c.src).src:b._getSrcById(a).src,void 0==b._preloadHash[a]?!1:1==b._preloadHash[a][0]},b._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var c=a.match(b.FILE_PATTERN);if(null==c)return!1;for(var d=c[4],e=c[5],f=b.capabilities,g=0;!f[e];)if(e=b.alternateExtensions[g++],g>b.alternateExtensions.length)return null;a=a.replace("."+c[5],"."+e);var h={name:d,src:a,extension:e};return h},b._parseSrc=function(a){var c={name:void 0,src:void 0,extension:void 0},d=b.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){c.src=a[e],c.extension=e;break}if(!c.src)return!1;var f=c.src.lastIndexOf("/");return c.name=-1!=f?c.src.slice(f+1):c.src,c},b.play=function(a,c,d,e,f,g,h,i,j){var k;k=createjs.PlayPropsConfig.create(c instanceof Object||c instanceof createjs.PlayPropsConfig?c:{interrupt:c,delay:d,offset:e,loop:f,volume:g,pan:h,startTime:i,duration:j});var l=b.createInstance(a,k.startTime,k.duration),m=b._playInstance(l,k);return m||l._playFailed(),l},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;
return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),f=f||b._defaultPlayPropsHash[g.src],f&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(c,d,e),h.uniqueId=b._lastID++,h},b.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},b.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)},b.getVolume=function(){return this._masterVolume},b.setMute=function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0},b.getMute=function(){return this._masterMute},b.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.getPosition()),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){var e=b._beginPlaying(a,c);if(!e)return!1}else{var f=setTimeout(function(){b._beginPlaying(a,c)},c.delay);a.delayTimeoutId=f}return this._instances.push(a),!0},b._beginPlaying=function(b,c){if(!a.add(b,c.interrupt))return!1;var d=b._beginPlaying(c);if(!d){var e=createjs.indexOf(this._instances,b);return e>-1&&this._instances.splice(e,1),!1}return!0},b._getSrcById=function(a){return b._idHash[a]||{src:a}},b._playFinished=function(b){a.remove(b);var c=createjs.indexOf(this._instances,b);c>-1&&this._instances.splice(c,1)},createjs.Sound=Sound,a.channels={},a.create=function(b,c){var d=a.get(b);return null==d?(a.channels[b]=new a(b,c),!0):!1},a.removeSrc=function(b){var c=a.get(b);return null==c?!1:(c._removeAll(),delete a.channels[b],!0)},a.removeAll=function(){for(var b in a.channels)a.channels[b]._removeAll();a.channels={}},a.add=function(b,c){var d=a.get(b.src);return null==d?!1:d._add(b,c)},a.remove=function(b){var c=a.get(b.src);return null==c?!1:(c._remove(b),!0)},a.maxPerChannel=function(){return c.maxDefault},a.get=function(b){return a.channels[b]};var c=a.prototype;c.constructor=a,c.src=null,c.max=null,c.maxDefault=100,c.length=0,c.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},c._get=function(a){return this._instances[a]},c._add=function(a,b){return this._getSlot(b,a)?(this._instances.push(a),this.length++,!0):!1},c._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1==b?!1:(this._instances.splice(b,1),this.length--,!0)},c._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},c._getSlot=function(a){var b,c;if(a!=Sound.INTERRUPT_NONE&&(c=this._get(0),null==c))return!0;for(var d=0,e=this.max;e>d;d++){if(b=this._get(d),null==b)return!0;if(b.playState==Sound.PLAY_FINISHED||b.playState==Sound.PLAY_INTERRUPTED||b.playState==Sound.PLAY_FAILED){c=b;break}a!=Sound.INTERRUPT_NONE&&(a==Sound.INTERRUPT_EARLY&&b.getPosition()<c.getPosition()||a==Sound.INTERRUPT_LATE&&b.getPosition()>c.getPosition())&&(c=b)}return null!=c?(c._interrupt(),this._remove(c),!0):!1},c.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractSoundInstance=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._volume=1,Object.defineProperty(this,"volume",{get:this.getVolume,set:this.setVolume}),this._pan=0,Object.defineProperty(this,"pan",{get:this.getPan,set:this.setPan}),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this.getStartTime,set:this.setStartTime}),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this.getDuration,set:this.setDuration}),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this.getPlaybackResource,set:this.setPlaybackResource}),d!==!1&&d!==!0&&this.setPlaybackResource(d),this._position=0,Object.defineProperty(this,"position",{get:this.getPosition,set:this.setPosition}),this._loop=0,Object.defineProperty(this,"loop",{get:this.getLoop,set:this.setLoop}),this._muted=!1,Object.defineProperty(this,"muted",{get:this.getMuted,set:this.setMuted}),this._paused=!1,Object.defineProperty(this,"paused",{get:this.getPaused,set:this.setPaused})},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a,b,c,d,e,f){var g;return g=createjs.PlayPropsConfig.create(a instanceof Object||a instanceof createjs.PlayPropsConfig?a:{interrupt:a,delay:b,offset:c,loop:d,volume:e,pan:f}),this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(g),void(this._paused&&this.setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,g),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this.setPosition(a.offset),null!=a.loop&&this.setLoop(a.loop),null!=a.volume&&this.setVolume(a.volume),null!=a.pan&&this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),this},a.toString=function(){return"[AbstractSoundInstance]"},a.getPaused=function(){return this._paused},a.setPaused=function(a){return a!==!0&&a!==!1||this._paused==a||1==a&&this.playState!=createjs.Sound.PLAY_SUCCEEDED?void 0:(this._paused=a,a?this._pause():this._resume(),clearTimeout(this.delayTimeoutId),this)},a.setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},a.getVolume=function(){return this._volume},a.setMuted=function(a){return a===!0||a===!1?(this._muted=a,this._updateVolume(),this):void 0},a.getMuted=function(){return this._muted},a.setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},a.getPan=function(){return this._pan},a.getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED||(this._position=this._calculateCurrentPosition()),this._position},a.setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},a.getStartTime=function(){return this._startTime},a.setStartTime=function(a){return a==this._startTime?this:(this._startTime=Math.max(0,a||0),this._updateStartTime(),this)},a.getDuration=function(){return this._duration},a.setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},a.setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._setDurationFromSource(),this},a.getPlaybackResource=function(){return this._playbackResource},a.getLoop=function(){return this._loop},a.setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this.setPosition(a.offset),this.setLoop(a.loop),this.setVolume(a.volume),this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),null!=this._playbackResource&&this._position<this._duration?(this._paused=!1,this._handleSoundReady(),this.playState=createjs.Sound.PLAY_SUCCEEDED,this._sendEvent("succeeded"),!0):(this._playFailed(),!1)},a._playFailed=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_FAILED,this._sendEvent("failed")},a._handleSoundComplete=function(){return this._position=0,0!=this._loop?(this._loop--,this._handleLoop(),void this._sendEvent("loop")):(this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,void this._sendEvent("complete"))},a._handleSoundReady=function(){},a._updateVolume=function(){},a._updatePan=function(){},a._updateStartTime=function(){},a._updateDuration=function(){},a._setDurationFromSource=function(){},a._calculateCurrentPosition=function(){},a._updatePosition=function(){},a._removeLooping=function(){},a._addLooping=function(){},a._pause=function(){},a._resume=function(){},a._handleStop=function(){},a._handleCleanUp=function(){},a._handleLoop=function(){},createjs.AbstractSoundInstance=createjs.promote(AbstractSoundInstance,"EventDispatcher"),createjs.DefaultSoundInstance=createjs.AbstractSoundInstance}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractPlugin=function(){this._capabilities=null,this._loaders={},this._audioSources={},this._soundInstances={},this._volume=1,this._loaderClass,this._soundInstanceClass},a=AbstractPlugin.prototype;AbstractPlugin._capabilities=null,AbstractPlugin.isSupported=function(){return!0},a.register=function(a){var b=this._loaders[a.src];return b&&!b.canceled?this._loaders[a.src]:(this._audioSources[a.src]=!0,this._soundInstances[a.src]=[],b=new this._loaderClass(a),b.on("complete",this._handlePreloadComplete,this),this._loaders[a.src]=b,b)},a.preload=function(a){a.on("error",this._handlePreloadError,this),a.load()},a.isPreloadStarted=function(a){return null!=this._audioSources[a]},a.isPreloadComplete=function(a){return!(null==this._audioSources[a]||1==this._audioSources[a])},a.removeSound=function(a){if(this._soundInstances[a]){for(var b=this._soundInstances[a].length;b--;){var c=this._soundInstances[a][b];c.destroy()}delete this._soundInstances[a],delete this._audioSources[a],this._loaders[a]&&this._loaders[a].destroy(),delete this._loaders[a]}},a.removeAllSounds=function(){for(var a in this._audioSources)this.removeSound(a)},a.create=function(a,b,c){this.isPreloadStarted(a)||this.preload(this.register(a));var d=new this._soundInstanceClass(a,b,c,this._audioSources[a]);return this._soundInstances[a].push(d),d},a.setVolume=function(a){return this._volume=a,this._updateVolume(),!0},a.getVolume=function(){return this._volume},a.setMute=function(){return this._updateVolume(),!0},a.toString=function(){return"[AbstractPlugin]"},a._handlePreloadComplete=function(a){var b=a.target.getItem().src;this._audioSources[b]=a.result;for(var c=0,d=this._soundInstances[b].length;d>c;c++){var e=this._soundInstances[b][c];e.setPlaybackResource(this._audioSources[b])}},a._handlePreloadError=function(){},a._updateVolume=function(){},createjs.AbstractPlugin=AbstractPlugin}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.SOUND)}var b=createjs.extend(a,createjs.AbstractLoader);a.context=null,b.toString=function(){return"[WebAudioLoader]"},b._createRequest=function(){this._request=new createjs.XHRRequest(this._item,!1),this._request.setResponseType("arraybuffer")},b._sendComplete=function(){a.context.decodeAudioData(this._rawResult,createjs.proxy(this._handleAudioDecoded,this),createjs.proxy(this._sendError,this))},b._handleAudioDecoded=function(a){this._result=a,this.AbstractLoader__sendComplete()},createjs.WebAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioSoundInstance(a,c,d,e){this.AbstractSoundInstance_constructor(a,c,d,e),this.gainNode=b.context.createGain(),this.panNode=b.context.createPanner(),this.panNode.panningModel=b._panningModel,this.panNode.connect(this.gainNode),this._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){a.stop(0),a.disconnect(0);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=.001*this._position;c>a&&(c=a),this.sourceNode=this._createAndPlayAudioNode(b.context.currentTime-a,c),this._playbackStartTime=this.sourceNode.startTime-c,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(a-c)),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},a._createAndPlayAudioNode=function(a,c){var d=b.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,c+.001*this._startTime,e-c),d},a._pause=function(){this._position=1e3*(b.context.currentTime-this._playbackStartTime),this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout)},a._resume=function(){this._handleSoundReady()},a._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},a._calculateCurrentPosition=function(){return 1e3*(b.context.currentTime-this._playbackStartTime)},a._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},a._handleLoop=function(){this._cleanUpAudioNode(this.sourceNode),this.sourceNode=this._sourceNodeNext,this._playbackStartTime=this.sourceNode.startTime,this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0),this._soundCompleteTimeout=setTimeout(this._endedHandler,this._duration)},a._updateDuration=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._pause(),this._resume())},createjs.WebAudioSoundInstance=createjs.promote(WebAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioPlugin(){this.AbstractPlugin_constructor(),this._panningModel=b._panningModel,this.context=b.context,this.dynamicsCompressorNode=this.context.createDynamicsCompressor(),this.dynamicsCompressorNode.connect(this.context.destination),this.gainNode=this.context.createGain(),this.gainNode.connect(this.dynamicsCompressorNode),createjs.WebAudioSoundInstance.destinationNode=this.gainNode,this._capabilities=b._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var a=createjs.extend(WebAudioPlugin,createjs.AbstractPlugin),b=WebAudioPlugin;b._capabilities=null,b._panningModel="equalpower",b.context=null,b._scratchBuffer=null,b._unlocked=!1,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)if(window.AudioContext)b.context=new AudioContext;else{if(!window.webkitAudioContext)return null;b.context=new webkitAudioContext}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("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._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),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.defaultNumChannels=2,this._capabilities=b._capabilities,this._loaderClass=createjs.SoundLoader,this._soundInstanceClass=createjs.HTMLAudioSoundInstance}var a=createjs.extend(HTMLAudioPlugin,createjs.AbstractPlugin),b=HTMLAudioPlugin;b.MAX_INSTANCES=30,b._AUDIO_READY="canplaythrough",b._AUDIO_ENDED="ended",b._AUDIO_SEEKED="seeked",b._AUDIO_STALLED="stalled",b._TIME_UPDATE="timeupdate",b._capabilities=null,b.isSupported=function(){return b._generateCapabilities(),null!=b._capabilities},b._generateCapabilities=function(){if(null==b._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;b._capabilities={panning:!1,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},a.register=function(a){var b=createjs.HTMLAudioTagPool.get(a.src),c=this.AbstractPlugin_register(a);return c.setTag(b),c},a.removeSound=function(a){this.AbstractPlugin_removeSound(a),createjs.HTMLAudioTagPool.remove(a)},a.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.setPlaybackResource(null),d},a.toString=function(){return"[HTMLAudioPlugin]"},a.setVolume=a.getVolume=a.setMute=null,createjs.HTMLAudioPlugin=createjs.promote(HTMLAudioPlugin,"AbstractPlugin")}();
/bower_components/SoundJS/lib/soundjs-NEXT.combined.js
@@ -0,0 +1,7949 @@
/*!
* SoundJS
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
 
 
//##############################################################################
// version.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
/**
* Static class holding library specific information such as the version and buildDate of the library.
* The SoundJS class has been renamed {{#crossLink "Sound"}}{{/crossLink}}. Please see {{#crossLink "Sound"}}{{/crossLink}}
* for information on using sound.
* @class SoundJS
**/
var s = createjs.SoundJS = createjs.SoundJS || {};
 
/**
* The version string for this release.
* @property version
* @type String
* @static
**/
s.version = /*=version*/"NEXT"; // injected by build process
 
/**
* The build date for this release in UTC format.
* @property buildDate
* @type String
* @static
**/
s.buildDate = /*=date*/"Fri, 04 Dec 2015 17:24:04 GMT"; // injected by build process
 
})();
 
//##############################################################################
// extend.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
 
/**
* Sets up the prototype chain and constructor property for a new class.
*
* This should be called right after creating the class constructor.
*
* function MySubClass() {}
* createjs.extend(MySubClass, MySuperClass);
* MySubClass.prototype.doSomething = function() { }
*
* var foo = new MySubClass();
* console.log(foo instanceof MySuperClass); // true
* console.log(foo.prototype.constructor === MySubClass); // true
*
* @method extend
* @param {Function} subclass The subclass.
* @param {Function} superclass The superclass to extend.
* @return {Function} Returns the subclass's new prototype.
*/
createjs.extend = function(subclass, superclass) {
"use strict";
 
function o() { this.constructor = subclass; }
o.prototype = superclass.prototype;
return (subclass.prototype = new o());
};
 
//##############################################################################
// promote.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
 
/**
* Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
* It is recommended to use the super class's name as the prefix.
* An alias to the super class's constructor is always added in the format `prefix_constructor`.
* This allows the subclass to call super class methods without using `function.call`, providing better performance.
*
* For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
* would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
* prototype of `MySubClass` as `MySuperClass_draw`.
*
* This should be called after the class's prototype is fully defined.
*
* function ClassA(name) {
* this.name = name;
* }
* ClassA.prototype.greet = function() {
* return "Hello "+this.name;
* }
*
* function ClassB(name, punctuation) {
* this.ClassA_constructor(name);
* this.punctuation = punctuation;
* }
* createjs.extend(ClassB, ClassA);
* ClassB.prototype.greet = function() {
* return this.ClassA_greet()+this.punctuation;
* }
* createjs.promote(ClassB, "ClassA");
*
* var foo = new ClassB("World", "!?!");
* console.log(foo.greet()); // Hello World!?!
*
* @method promote
* @param {Function} subclass The class to promote super class methods on.
* @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
* @return {Function} Returns the subclass.
*/
createjs.promote = function(subclass, prefix) {
"use strict";
 
var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
if (supP) {
subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
for (var n in supP) {
if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
}
}
return subclass;
};
 
//##############################################################################
// IndexOf.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
 
/**
* Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
* that value. Returns -1 if value is not found.
*
* var i = createjs.indexOf(myArray, myElementToFind);
*
* @method indexOf
* @param {Array} array Array to search for searchElement
* @param searchElement Element to find in array.
* @return {Number} The first index of searchElement in array.
*/
createjs.indexOf = function (array, searchElement){
"use strict";
 
for (var i = 0,l=array.length; i < l; i++) {
if (searchElement === array[i]) {
return i;
}
}
return -1;
};
 
//##############################################################################
// Proxy.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the
* createjs namespace directly.
*
* <h4>Example</h4>
*
* myObject.addEventListener("change", createjs.proxy(myMethod, scope));
*
* @class Utility Methods
* @main Utility Methods
*/
 
(function() {
"use strict";
 
/**
* A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a
* callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the
* method gets called in the correct scope.
*
* Additional arguments can be passed that will be applied to the function when it is called.
*
* <h4>Example</h4>
*
* myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2));
*
* function myHandler(arg1, arg2) {
* // This gets called when myObject.myCallback is executed.
* }
*
* @method proxy
* @param {Function} method The function to call
* @param {Object} scope The scope to call the method name on
* @param {mixed} [arg] * Arguments that are appended to the callback for additional params.
* @public
* @static
*/
createjs.proxy = function (method, scope) {
var aArgs = Array.prototype.slice.call(arguments, 2);
return function () {
return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs));
};
}
 
}());
 
//##############################################################################
// BrowserDetect.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
/**
* @class Utility Methods
*/
(function() {
"use strict";
 
/**
* An object that determines the current browser, version, operating system, and other environment
* variables via user agent string.
*
* Used for audio because feature detection is unable to detect the many limitations of mobile devices.
*
* <h4>Example</h4>
*
* if (createjs.BrowserDetect.isIOS) { // do stuff }
*
* @property BrowserDetect
* @type {Object}
* @param {Boolean} isFirefox True if our browser is Firefox.
* @param {Boolean} isOpera True if our browser is opera.
* @param {Boolean} isChrome True if our browser is Chrome. Note that Chrome for Android returns true, but is a
* completely different browser with different abilities.
* @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPod).
* @param {Boolean} isAndroid True if our browser is Android.
* @param {Boolean} isBlackberry True if our browser is Blackberry.
* @constructor
* @static
*/
function BrowserDetect() {
throw "BrowserDetect cannot be instantiated";
};
 
var agent = BrowserDetect.agent = window.navigator.userAgent;
BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1);
BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1);
BrowserDetect.isOpera = (window.opera != null);
BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1); // NOTE that Chrome on Android returns true but is a completely different browser with different abilities
BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone;
BrowserDetect.isAndroid = (agent.indexOf("Android") > -1) && !BrowserDetect.isWindowPhone;
BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1);
 
createjs.BrowserDetect = BrowserDetect;
 
}());
 
//##############################################################################
// 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.
*
* <h4>Example</h4>
* 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");
* }
*
* <b>Maintaining proper scope</b><br />
* 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.
*
* <b>Browser support</b>
* 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;
 
/**
* <strong>REMOVED</strong>. 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.
*
* 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.
*
* <h4>Example</h4>
*
* 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`).
*
* <b>IMPORTANT:</b> 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.
*
* <h4>Example</h4>
*
* 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.
*
* <b>Important Note:</b> 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.
*
* <h4>Example</h4>
*
* 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; i<l; i++) {
if (arr[i] == listener) {
if (l==1) { delete(listeners[type]); } // allows for faster checks.
else { arr.splice(i,1); }
break;
}
}
};
/**
* A shortcut to the removeEventListener method, with the same parameters and return value. This is a companion to the
* .on method.
*
* <b>IMPORTANT:</b> 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.
*
* <h4>Example</h4>
*
* // 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.
*
* <h4>Example</h4>
*
* // 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; i<l && !eventObj.propagationStopped; i++) {
list[i]._dispatchEvent(eventObj, 3);
}
}
return !eventObj.defaultPrevented;
};
 
/**
* Indicates whether there is at least one listener for the specified event type.
* @method hasEventListener
* @param {String} type The string type of the event.
* @return {Boolean} Returns true if there is at least one listener for the specified event.
**/
p.hasEventListener = function(type) {
var listeners = this._listeners, captureListeners = this._captureListeners;
return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type]));
};
/**
* Indicates whether there is at least one listener for the specified event type on this object or any of its
* ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the
* specified type is dispatched from this object, it will trigger at least one listener.
*
* This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire
* event flow for a listener, not just this object.
* @method willTrigger
* @param {String} type The string type of the event.
* @return {Boolean} Returns `true` if there is at least one listener for the specified event.
**/
p.willTrigger = function(type) {
var o = this;
while (o) {
if (o.hasEventListener(type)) { return true; }
o = o.parent;
}
return false;
};
 
/**
* @method toString
* @return {String} a string representation of the instance.
**/
p.toString = function() {
return "[EventDispatcher]";
};
 
 
// private methods:
/**
* @method _dispatchEvent
* @param {Object | String | 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; }
try { eventObj.currentTarget = this; } catch (e) {}
try { eventObj.eventPhase = eventPhase; } catch (e) {}
eventObj.removed = false;
arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) {
var o = arr[i];
if (o.handleEvent) { o.handleEvent(eventObj); }
else { o(eventObj); }
if (eventObj.removed) {
this.off(eventObj.type, o, eventPhase==1);
eventObj.removed = false;
}
}
}
};
 
 
createjs.EventDispatcher = EventDispatcher;
}());
 
//##############################################################################
// Event.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
(function() {
"use strict";
 
// constructor:
/**
* Contains properties and methods shared by all events for use with
* {{#crossLink "EventDispatcher"}}{{/crossLink}}.
*
* Note that Event objects are often reused, so you should never
* rely on an event object's state outside of the call stack it was received in.
* @class Event
* @param {String} type The event type.
* @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
* @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
* @constructor
**/
function Event(type, bubbles, cancelable) {
// public properties:
/**
* The type of event.
* @property type
* @type String
**/
this.type = type;
/**
* The object that generated an event.
* @property target
* @type Object
* @default null
* @readonly
*/
this.target = null;
/**
* The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
* always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
* is generated from childObj, then a listener on parentObj would receive the event with
* target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
* @property currentTarget
* @type Object
* @default null
* @readonly
*/
this.currentTarget = null;
/**
* For bubbling events, this indicates the current event phase:<OL>
* <LI> capture phase: starting from the top parent to the target</LI>
* <LI> at target phase: currently being dispatched from the target</LI>
* <LI> bubbling phase: from the target to the top parent</LI>
* </OL>
* @property eventPhase
* @type Number
* @default 0
* @readonly
*/
this.eventPhase = 0;
/**
* Indicates whether the event will bubble through the display list.
* @property bubbles
* @type Boolean
* @default false
* @readonly
*/
this.bubbles = !!bubbles;
/**
* Indicates whether the default behaviour of this event can be cancelled via
* {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
* @property cancelable
* @type Boolean
* @default false
* @readonly
*/
this.cancelable = !!cancelable;
/**
* The epoch time at which this event was created.
* @property timeStamp
* @type Number
* @default 0
* @readonly
*/
this.timeStamp = (new Date()).getTime();
/**
* Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
* on this event.
* @property defaultPrevented
* @type Boolean
* @default false
* @readonly
*/
this.defaultPrevented = false;
/**
* Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
* {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
* @property propagationStopped
* @type Boolean
* @default false
* @readonly
*/
this.propagationStopped = false;
/**
* Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
* on this event.
* @property immediatePropagationStopped
* @type Boolean
* @default false
* @readonly
*/
this.immediatePropagationStopped = false;
/**
* Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
* @property removed
* @type Boolean
* @default false
* @readonly
*/
this.removed = false;
}
var p = Event.prototype;
 
/**
* <strong>REMOVED</strong>. 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 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() {
this.defaultPrevented = this.cancelable&&true;
};
 
/**
* Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
* Mirrors the DOM event standard.
* @method stopPropagation
**/
p.stopPropagation = function() {
this.propagationStopped = true;
};
 
/**
* Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
* {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
* Mirrors the DOM event standard.
* @method stopImmediatePropagation
**/
p.stopImmediatePropagation = function() {
this.immediatePropagationStopped = this.propagationStopped = true;
};
/**
* Causes the active listener to be removed via removeEventListener();
*
* myBtn.addEventListener("click", function(evt) {
* // do stuff...
* evt.remove(); // removes this listener.
* });
*
* @method remove
**/
p.remove = function() {
this.removed = true;
};
/**
* Returns a clone of the Event instance.
* @method clone
* @return {Event} a clone of the Event instance.
**/
p.clone = function() {
return new Event(this.type, this.bubbles, this.cancelable);
};
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
*
* @method set
* @param {Object} props A generic object containing properties to copy to the instance.
* @return {Event} Returns the instance the method is called on (useful for chaining calls.)
* @chainable
*/
p.set = function(props) {
for (var n in props) { this[n] = props[n]; }
return this;
};
 
/**
* Returns a string representation of this object.
* @method toString
* @return {String} a string representation of the instance.
**/
p.toString = function() {
return "[Event (type="+this.type+")]";
};
 
createjs.Event = Event;
}());
 
//##############################################################################
// ErrorEvent.js
//##############################################################################
 
this.createjs = this.createjs||{};
 
(function() {
"use strict";
 
/**
* A general error {{#crossLink "Event"}}{{/crossLink}}, that describes an error that occurred, as well as any details.
* @class ErrorEvent
* @param {String} [title] The error title
* @param {String} [message] The error description
* @param {Object} [data] Additional error data
* @constructor
*/
function ErrorEvent(title, message, data) {
this.Event_constructor("error");
 
/**
* The short error title, which indicates the type of error that occurred.
* @property title
* @type String
*/
this.title = title;
 
/**
* The verbose error message, containing details about the error.
* @property message
* @type String
*/
this.message = message;
 
/**
* Additional data attached to an error.
* @property data
* @type {Object}
*/
this.data = data;
}
 
var p = createjs.extend(ErrorEvent, createjs.Event);
 
p.clone = function() {
return new createjs.ErrorEvent(this.title, this.message, this.data);
};
 
createjs.ErrorEvent = createjs.promote(ErrorEvent, "Event");
 
}());
 
//##############################################################################
// ProgressEvent.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function (scope) {
"use strict";
 
// constructor
/**
* A CreateJS {{#crossLink "Event"}}{{/crossLink}} that is dispatched when progress changes.
* @class ProgressEvent
* @param {Number} loaded The amount that has been loaded. This can be any number relative to the total.
* @param {Number} [total=1] The total amount that will load. This will default to 1, so if the `loaded` value is
* a percentage (between 0 and 1), it can be omitted.
* @todo Consider having this event be a "fileprogress" event as well
* @constructor
*/
function ProgressEvent(loaded, total) {
this.Event_constructor("progress");
 
/**
* The amount that has been loaded (out of a total amount)
* @property loaded
* @type {Number}
*/
this.loaded = loaded;
 
/**
* The total "size" of the load.
* @property total
* @type {Number}
* @default 1
*/
this.total = (total == null) ? 1 : total;
 
/**
* The percentage (out of 1) that the load has been completed. This is calculated using `loaded/total`.
* @property progress
* @type {Number}
* @default 0
*/
this.progress = (total == 0) ? 0 : this.loaded / this.total;
};
 
var p = createjs.extend(ProgressEvent, createjs.Event);
 
/**
* Returns a clone of the ProgressEvent instance.
* @method clone
* @return {ProgressEvent} a clone of the Event instance.
**/
p.clone = function() {
return new createjs.ProgressEvent(this.loaded, this.total);
};
 
createjs.ProgressEvent = createjs.promote(ProgressEvent, "Event");
 
}(window));
 
//##############################################################################
// LoadItem.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* All loaders accept an item containing the properties defined in this class. If a raw object is passed instead,
* it will not be affected, but it must contain at least a {{#crossLink "src:property"}}{{/crossLink}} property. A
* string path or HTML tag is also acceptable, but it will be automatically converted to a LoadItem using the
* {{#crossLink "create"}}{{/crossLink}} method by {{#crossLink "AbstractLoader"}}{{/crossLink}}
* @class LoadItem
* @constructor
* @since 0.6.0
*/
function LoadItem() {
/**
* The source of the file that is being loaded. This property is <b>required</b>. The source can either be a
* string (recommended), or an HTML tag.
* This can also be an object, but in that case it has to include a type and be handled by a plugin.
* @property src
* @type {String}
* @default null
*/
this.src = null;
 
/**
* The type file that is being loaded. The type of the file is usually inferred by the extension, but can also
* be set manually. This is helpful in cases where a file does not have an extension.
* @property type
* @type {String}
* @default null
*/
this.type = null;
 
/**
* A string identifier which can be used to reference the loaded object. If none is provided, this will be
* automatically set to the {{#crossLink "src:property"}}{{/crossLink}}.
* @property id
* @type {String}
* @default null
*/
this.id = null;
 
/**
* Determines if a manifest will maintain the order of this item, in relation to other items in the manifest
* that have also set the `maintainOrder` property to `true`. This only applies when the max connections has
* been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}). Everything with this
* property set to `false` will finish as it is loaded. Ordered items are combined with script tags loading in
* order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} is set to `true`.
* @property maintainOrder
* @type {Boolean}
* @default false
*/
this.maintainOrder = false;
 
/**
* A callback used by JSONP requests that defines what global method to call when the JSONP content is loaded.
* @property callback
* @type {String}
* @default null
*/
this.callback = null;
 
/**
* An arbitrary data object, which is included with the loaded object.
* @property data
* @type {Object}
* @default null
*/
this.data = null;
 
/**
* The request method used for HTTP calls. Both {{#crossLink "AbstractLoader/GET:property"}}{{/crossLink}} or
* {{#crossLink "AbstractLoader/POST:property"}}{{/crossLink}} request types are supported, and are defined as
* constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @property method
* @type {String}
* @default get
*/
this.method = createjs.LoadItem.GET;
 
/**
* An object hash of name/value pairs to send to the server.
* @property values
* @type {Object}
* @default null
*/
this.values = null;
 
/**
* An object hash of headers to attach to an XHR request. PreloadJS will automatically attach some default
* headers when required, including "Origin", "Content-Type", and "X-Requested-With". You may override the
* default headers by including them in your headers object.
* @property headers
* @type {Object}
* @default null
*/
this.headers = null;
 
/**
* Enable credentials for XHR requests.
* @property withCredentials
* @type {Boolean}
* @default false
*/
this.withCredentials = false;
 
/**
* Set the mime type of XHR-based requests. This is automatically set to "text/plain; charset=utf-8" for text
* based files (json, xml, text, css, js).
* @property mimeType
* @type {String}
* @default null
*/
this.mimeType = null;
 
/**
* Sets the crossOrigin attribute for CORS-enabled images loading cross-domain.
* @property crossOrigin
* @type {boolean}
* @default Anonymous
*/
this.crossOrigin = null;
 
/**
* The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
* (level one) loading, as XHR (level 2) provides its own timeout event.
* @property loadTimeout
* @type {Number}
* @default 8000 (8 seconds)
*/
this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
};
 
var p = LoadItem.prototype = {};
var s = LoadItem;
 
/**
* Default duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
* (level one) loading, as XHR (level 2) provides its own timeout event.
* @property LOAD_TIMEOUT_DEFAULT
* @type {number}
* @static
*/
s.LOAD_TIMEOUT_DEFAULT = 8000;
 
/**
* Create a LoadItem.
* <ul>
* <li>String-based items are converted to a LoadItem with a populated {{#crossLink "src:property"}}{{/crossLink}}.</li>
* <li>LoadItem instances are returned as-is</li>
* <li>Objects are returned with any needed properties added</li>
* </ul>
* @method create
* @param {LoadItem|String|Object} value The load item value
* @returns {LoadItem|Object}
* @static
*/
s.create = function (value) {
if (typeof value == "string") {
var item = new LoadItem();
item.src = value;
return item;
} else if (value instanceof s) {
return value;
} else if (value instanceof Object && value.src) {
if (value.loadTimeout == null) {
value.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
}
return value;
} else {
throw new Error("Type not recognized.");
}
};
 
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
*
* <h4>Example</h4>
*
* var loadItem = new createjs.LoadItem().set({src:"image.png", maintainOrder:true});
*
* @method set
* @param {Object} props A generic object containing properties to copy to the LoadItem instance.
* @return {LoadItem} Returns the instance the method is called on (useful for chaining calls.)
*/
p.set = function(props) {
for (var n in props) { this[n] = props[n]; }
return this;
};
 
createjs.LoadItem = s;
 
}());
 
//##############################################################################
// RequestUtils.js
//##############################################################################
 
(function () {
 
/**
* Utilities that assist with parsing load items, and determining file types, etc.
* @class RequestUtils
*/
var s = {};
 
/**
* The Regular Expression used to test file URLS for an absolute path.
* @property ABSOLUTE_PATH
* @type {RegExp}
* @static
*/
s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
 
/**
* The Regular Expression used to test file URLS for a relative path.
* @property RELATIVE_PATH
* @type {RegExp}
* @static
*/
s.RELATIVE_PATT = (/^[./]*?\//i);
 
/**
* The Regular Expression used to test file URLS for an extension. Note that URIs must already have the query string
* removed.
* @property EXTENSION_PATT
* @type {RegExp}
* @static
*/
s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
 
/**
* Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
* <ul>
* <li>If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
* `//networkPath`)</li>
* <li>If the path is relative. Relative paths start with `../` or `/path` (or similar)</li>
* <li>The file extension. This is determined by the filename with an extension. Query strings are dropped, and
* the file path is expected to follow the format `name.ext`.</li>
* </ul>
* @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 <b>not</b> a binary type, as we can not play
* back using an audio tag if it is loaded as binary. Plugins can change the item type to binary to ensure they get
* a binary result to work with. Binary files are loaded using XHR2. Types are defined as static constants on
* {{#crossLink "AbstractLoader"}}{{/crossLink}}.
* @method isBinary
* @param {String} type The item type.
* @return {Boolean} If the specified type is binary.
* @static
*/
s.isBinary = function (type) {
switch (type) {
case createjs.AbstractLoader.IMAGE:
case createjs.AbstractLoader.BINARY:
return true;
default:
return false;
}
};
 
/**
* Check if item is a valid HTMLImageElement
* @method isImageTag
* @param {Object} item
* @returns {Boolean}
* @static
*/
s.isImageTag = function(item) {
return item instanceof HTMLImageElement;
};
 
/**
* Check if item is a valid HTMLAudioElement
* @method isAudioTag
* @param {Object} item
* @returns {Boolean}
* @static
*/
s.isAudioTag = function(item) {
if (window.HTMLAudioElement) {
return item instanceof HTMLAudioElement;
} else {
return false;
}
};
 
/**
* Check if item is a valid HTMLVideoElement
* @method isVideoTag
* @param {Object} item
* @returns {Boolean}
* @static
*/
s.isVideoTag = function(item) {
if (window.HTMLVideoElement) {
return item instanceof HTMLVideoElement;
} else {
return false;
}
};
 
/**
* Determine if a specific type is a text-based asset, and should be loaded as UTF-8.
* @method isText
* @param {String} type The item type.
* @return {Boolean} If the specified type is text.
* @static
*/
s.isText = function (type) {
switch (type) {
case createjs.AbstractLoader.TEXT:
case createjs.AbstractLoader.JSON:
case createjs.AbstractLoader.MANIFEST:
case createjs.AbstractLoader.XML:
case createjs.AbstractLoader.CSS:
case createjs.AbstractLoader.SVG:
case createjs.AbstractLoader.JAVASCRIPT:
case createjs.AbstractLoader.SPRITESHEET:
return true;
default:
return false;
}
};
 
/**
* Determine the type of the object using common extensions. Note that the type can be passed in with the load item
* if it is an unusual extension.
* @method getTypeByExtension
* @param {String} extension The file extension to use to determine the load type.
* @return {String} The determined load type (for example, <code>AbstractLoader.IMAGE</code>). Will return `null` if
* the type can not be determined by the extension.
* @static
*/
s.getTypeByExtension = function (extension) {
if (extension == null) {
return createjs.AbstractLoader.TEXT;
}
 
switch (extension.toLowerCase()) {
case "jpeg":
case "jpg":
case "gif":
case "png":
case "webp":
case "bmp":
return createjs.AbstractLoader.IMAGE;
case "ogg":
case "mp3":
case "webm":
return createjs.AbstractLoader.SOUND;
case "mp4":
case "webm":
case "ts":
return createjs.AbstractLoader.VIDEO;
case "json":
return createjs.AbstractLoader.JSON;
case "xml":
return createjs.AbstractLoader.XML;
case "css":
return createjs.AbstractLoader.CSS;
case "js":
return createjs.AbstractLoader.JAVASCRIPT;
case 'svg':
return createjs.AbstractLoader.SVG;
default:
return createjs.AbstractLoader.TEXT;
}
};
 
createjs.RequestUtils = s;
 
}());
 
//##############################################################################
// AbstractLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* The base loader, which defines all the generic methods, properties, and events. All loaders extend this class,
* including the {{#crossLink "LoadQueue"}}{{/crossLink}}.
* @class AbstractLoader
* @param {LoadItem|object|string} loadItem The item to be loaded.
* @param {Boolean} [preferXHR] Determines if the LoadItem should <em>try</em> and load using XHR, or take a
* tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the
* other, so this is a suggested directive.
* @param {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
* such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc.
* @extends EventDispatcher
*/
function AbstractLoader(loadItem, preferXHR, type) {
this.EventDispatcher_constructor();
 
// public properties
/**
* If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
* used for loading do not pile up resulting in more than one `complete` {{#crossLink "Event"}}{{/crossLink}}.
* @property loaded
* @type {Boolean}
* @default false
*/
this.loaded = false;
 
/**
* Determine if the loader was canceled. Canceled loads will not fire complete events. Note that this property
* is readonly, so {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "LoadQueue/close"}}{{/crossLink}}
* instead.
* @property canceled
* @type {Boolean}
* @default false
* @readonly
*/
this.canceled = false;
 
/**
* The current load progress (percentage) for this item. This will be a number between 0 and 1.
*
* <h4>Example</h4>
*
* var queue = new createjs.LoadQueue();
* queue.loadFile("largeImage.png");
* queue.on("progress", function() {
* console.log("Progress:", queue.progress, event.progress);
* });
*
* @property progress
* @type {Number}
* @default 0
*/
this.progress = 0;
 
/**
* The type of item this loader will load. See {{#crossLink "AbstractLoader"}}{{/crossLink}} for a full list of
* supported types.
* @property type
* @type {String}
*/
this.type = type;
 
/**
* A formatter function that converts the loaded raw result into the final result. For example, the JSONLoader
* converts a string of text into a JavaScript object. Not all loaders have a resultFormatter, and this property
* can be overridden to provide custom formatting.
*
* Optionally, a resultFormatter can return a callback function in cases where the formatting needs to be
* asynchronous, such as creating a new image. 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.
*
* <h4>Example asynchronous resultFormatter</h4>
*
* function _formatResult(loader) {
* return function(success, error) {
* if (errorCondition) { error(errorDetailEvent); }
* success(result);
* }
* }
* @property resultFormatter
* @type {Function}
* @default null
*/
this.resultFormatter = null;
 
// protected properties
/**
* The {{#crossLink "LoadItem"}}{{/crossLink}} this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}},
* but will be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}.
* @property _item
* @type {LoadItem|Object}
* @private
*/
if (loadItem) {
this._item = createjs.LoadItem.create(loadItem);
} else {
this._item = null;
}
 
/**
* Whether the loader will try and load content using XHR (true) or HTML tags (false).
* @property _preferXHR
* @type {Boolean}
* @private
*/
this._preferXHR = preferXHR;
 
/**
* The loaded result after it is formatted by an optional {{#crossLink "resultFormatter"}}{{/crossLink}}. For
* items that are not formatted, this will be the same as the {{#crossLink "_rawResult:property"}}{{/crossLink}}.
* The result is accessed using the {{#crossLink "getResult"}}{{/crossLink}} method.
* @property _result
* @type {Object|String}
* @private
*/
this._result = null;
 
/**
* The loaded result before it is formatted. The rawResult is accessed using the {{#crossLink "getResult"}}{{/crossLink}}
* method, and passing `true`.
* @property _rawResult
* @type {Object|String}
* @private
*/
this._rawResult = null;
 
/**
* A list of items that loaders load behind the scenes. This does not include the main item the loader is
* responsible for loading. Examples of loaders that have sub-items include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and
* {{#crossLink "ManifestLoader"}}{{/crossLink}}.
* @property _loadItems
* @type {null}
* @protected
*/
this._loadedItems = null;
 
/**
* The attribute the items loaded using tags use for the source.
* @type {string}
* @default null
* @private
*/
this._tagSrcAttribute = null;
 
/**
* An HTML tag (or similar) that a loader may use to load HTML content, such as images, scripts, etc.
* @property _tag
* @type {Object}
* @private
*/
this._tag = null;
};
 
var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
var s = AbstractLoader;
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
/**
* Defines a POST request, use for a method value when loading data.
* @property POST
* @type {string}
* @default post
* @static
*/
s.POST = "POST";
 
/**
* Defines a GET request, use for a method value when loading data.
* @property GET
* @type {string}
* @default get
* @static
*/
s.GET = "GET";
 
/**
* The preload type for generic binary types. Note that images are loaded as binary files when using XHR.
* @property BINARY
* @type {String}
* @default binary
* @static
* @since 0.6.0
*/
s.BINARY = "binary";
 
/**
* The preload type for css files. CSS files are loaded using a &lt;link&gt; when loaded with XHR, or a
* &lt;style&gt; 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 &lt;image&gt; 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
* &lt;script&gt; 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
* &lt;audio&gt; 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
* &lt;video&gt; tag.
* @property VIDEO
* @type {String}
* @default video
* @static
* @since 0.6.0
*/
s.VIDEO = "video";
 
/**
* The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths.
* @property SPRITESHEET
* @type {String}
* @default spritesheet
* @static
* @since 0.6.0
*/
s.SPRITESHEET = "spritesheet";
 
/**
* The preload type for SVG files.
* @property SVG
* @type {String}
* @default svg
* @static
* @since 0.6.0
*/
s.SVG = "svg";
 
/**
* The preload type for text files, which is also the default file type if the type can not be determined. Text is
* loaded as raw text.
* @property TEXT
* @type {String}
* @default text
* @static
* @since 0.6.0
*/
s.TEXT = "text";
 
/**
* The preload type for xml files. XML is loaded into an XML document.
* @property XML
* @type {String}
* @default xml
* @static
* @since 0.6.0
*/
s.XML = "xml";
 
// Events
/**
* The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to
* version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}.
* @event progress
* @since 0.3.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts.
* @event loadstart
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.3.1
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded.
* @event complete
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.3.0
*/
 
/**
* The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was
* encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was
* just a regular {{#crossLink "Event"}}{{/crossLink}}.
* @event error
* @since 0.3.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error.
* This enables loaders to maintain internal queues, and surface file load errors.
* @event fileerror
* @param {Object} target The object that dispatched the event.
* @param {String} type The even type ("fileerror")
* @param {LoadItem|object} The item that encountered the error
* @since 0.6.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables
* loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s
* and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a
* slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event.
* @event fileload
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type ("fileload")
* @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
* object will contain that value as a `src` property.
* @param {Object} result The HTML tag or parsed result of the loaded item.
* @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
* to a usable object.
* @since 0.6.0
*/
 
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load.
* This allows updates to the loader for specific loading needs, such as binary or XHR image loading.
* @event initialize
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type ("initialize")
* @param {AbstractLoader} loader The loader that has been initialized.
*/
 
 
/**
* Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was
* passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
* {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will
* be a {{#crossLink "LoadItem"}}{{/crossLink}}.
* @method getItem
* @return {Object} The manifest item that this loader is responsible for loading.
* @since 0.6.0
*/
p.getItem = function () {
return this._item;
};
 
/**
* Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}}
* event is dispatched.
* @method getResult
* @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded
* data (if it exists).
* @return {Object}
* @since 0.6.0
*/
p.getResult = function (raw) {
return raw ? this._rawResult : this._result;
};
 
/**
* Return the `tag` this object creates or uses for loading.
* @method getTag
* @return {Object} The tag instance
* @since 0.6.0
*/
p.getTag = function () {
return this._tag;
};
 
/**
* Set the `tag` this item uses for loading.
* @method setTag
* @param {Object} tag The tag instance
* @since 0.6.0
*/
p.setTag = function(tag) {
this._tag = tag;
};
 
/**
* Begin loading the item. This method is required when using a loader by itself.
*
* <h4>Example</h4>
*
* var queue = new createjs.LoadQueue();
* queue.on("complete", handleComplete);
* queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet
* queue.load();
*
* @method load
*/
p.load = function () {
this._createRequest();
 
this._request.on("complete", this, this);
this._request.on("progress", this, this);
this._request.on("loadStart", this, this);
this._request.on("abort", this, this);
this._request.on("timeout", this, this);
this._request.on("error", this, this);
 
var evt = new createjs.Event("initialize");
evt.loader = this._request;
this.dispatchEvent(evt);
 
this._request.load();
};
 
/**
* Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in
* the background), but events will not longer be dispatched.
* @method cancel
*/
p.cancel = function () {
this.canceled = true;
this.destroy();
};
 
/**
* Clean up the loader.
* @method destroy
*/
p.destroy = function() {
if (this._request) {
this._request.removeAllEventListeners();
this._request.destroy();
}
 
this._request = null;
 
this._item = null;
this._rawResult = null;
this._result = null;
 
this._loadItems = null;
 
this.removeAllEventListeners();
};
 
/**
* Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}}
* to expose items it loads internally.
* @method getLoadedItems
* @return {Array} A list of the items loaded by the loader.
* @since 0.6.0
*/
p.getLoadedItems = function () {
return this._loadedItems;
};
 
 
// Private methods
/**
* Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or
* {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}.
* Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}},
* which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood.
* @method _createRequest
* @protected
*/
p._createRequest = function() {
if (!this._preferXHR) {
this._request = new createjs.TagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
} else {
this._request = new createjs.XHRRequest(this._item);
}
};
 
/**
* Create the HTML tag used for loading. This method does nothing by default, and needs to be implemented
* by loaders that require tag loading.
* @method _createTag
* @param {String} src The tag source
* @return {HTMLElement} The tag that was created
* @protected
*/
p._createTag = function(src) { return null; };
 
/**
* Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendLoadStart
* @protected
*/
p._sendLoadStart = function () {
if (this._isCanceled()) { return; }
this.dispatchEvent("loadstart");
};
 
/**
* Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}.
* @method _sendProgress
* @param {Number | Object} value The progress of the loaded item, or an object containing <code>loaded</code>
* and <code>total</code> properties.
* @protected
*/
p._sendProgress = function (value) {
if (this._isCanceled()) { return; }
var event = null;
if (typeof(value) == "number") {
this.progress = value;
event = new createjs.ProgressEvent(this.progress);
} else {
event = value;
this.progress = value.loaded / value.total;
event.progress = this.progress;
if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
}
this.hasEventListener("progress") && this.dispatchEvent(event);
};
 
/**
* Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
* @method _sendComplete
* @protected
*/
p._sendComplete = function () {
if (this._isCanceled()) { return; }
 
this.loaded = true;
 
var event = new createjs.Event("complete");
event.rawResult = this._rawResult;
 
if (this._result != null) {
event.result = this._result;
}
 
this.dispatchEvent(event);
};
 
/**
* Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendError
* @param {ErrorEvent} event The event object containing specific error properties.
* @protected
*/
p._sendError = function (event) {
if (this._isCanceled() || !this.hasEventListener("error")) { return; }
if (event == null) {
event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
}
this.dispatchEvent(event);
};
 
/**
* Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
* do not cause issues after the queue has been cleaned up.
* @method _isCanceled
* @return {Boolean} If the loader has been canceled.
* @protected
*/
p._isCanceled = function () {
if (window.createjs == null || this.canceled) {
return true;
}
return false;
};
 
/**
* A custom result formatter function, which is called just before a request dispatches its complete event. Most
* loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
* formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
* @property resultFormatter
* @type Function
* @return {Object} The formatted result
* @since 0.6.0
*/
p.resultFormatter = null;
 
/**
* Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but
* this method can be overridden for custom behaviours.
* @method handleEvent
* @param {Event} event The event that the internal request dispatches.
* @protected
* @since 0.6.0
*/
p.handleEvent = function (event) {
switch (event.type) {
case "complete":
this._rawResult = event.target._response;
var result = this.resultFormatter && this.resultFormatter(this);
if (result instanceof Function) {
result.call(this,
createjs.proxy(this._resultFormatSuccess, this),
createjs.proxy(this._resultFormatFailed, this)
);
} else {
this._result = result || this._rawResult;
this._sendComplete();
}
break;
case "progress":
this._sendProgress(event);
break;
case "error":
this._sendError(event);
break;
case "loadstart":
this._sendLoadStart();
break;
case "abort":
case "timeout":
if (!this._isCanceled()) {
this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_" + event.type.toUpperCase() + "_ERROR"));
}
break;
}
};
 
/**
* 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._resultFormatFailed = function (event) {
this._sendError(event);
};
 
/**
* @method buildPath
* @protected
* @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
* instead.
*/
p.buildPath = function (src, data) {
return createjs.RequestUtils.buildPath(src, data);
};
 
/**
* @method toString
* @return {String} a string representation of the instance.
*/
p.toString = function () {
return "[PreloadJS AbstractLoader]";
};
 
createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher");
 
}());
 
//##############################################################################
// AbstractMediaLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* The AbstractMediaLoader is a base class that handles some of the shared methods and properties of loaders that
* handle HTML media elements, such as Video and Audio.
* @class AbstractMediaLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @param {String} type The type of media to load. Usually "video" or "audio".
* @extends AbstractLoader
* @constructor
*/
function AbstractMediaLoader(loadItem, preferXHR, type) {
this.AbstractLoader_constructor(loadItem, preferXHR, type);
 
// public properties
this.resultFormatter = this._formatResult;
 
// protected properties
this._tagSrcAttribute = "src";
 
this.on("initialize", this._updateXHR, this);
};
 
var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);
 
// static properties
// public methods
p.load = function () {
// TagRequest will handle most of this, but Sound / Video need a few custom properties, so just handle them here.
if (!this._tag) {
this._tag = this._createTag(this._item.src);
}
 
this._tag.preload = "auto";
this._tag.load();
 
this.AbstractLoader_load();
};
 
// protected methods
/**
* Creates a new tag for loading if it doesn't exist yet.
* @method _createTag
* @private
*/
p._createTag = function () {};
 
 
p._createRequest = function() {
if (!this._preferXHR) {
this._request = new createjs.MediaTagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
} else {
this._request = new createjs.XHRRequest(this._item);
}
};
 
// 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
* @param {AbstractLoader} loader
* @returns {HTMLVideoElement|HTMLAudioElement}
* @private
*/
p._formatResult = function (loader) {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.onstalled = null;
if (this._preferXHR) {
var URL = window.URL || window.webkitURL;
var result = loader.getResult(true);
 
loader.getTag().src = URL.createObjectURL(result);
}
return loader.getTag();
};
 
createjs.AbstractMediaLoader = createjs.promote(AbstractMediaLoader, "AbstractLoader");
 
}());
 
//##############################################################################
// AbstractRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* A base class for actual data requests, such as {{#crossLink "XHRRequest"}}{{/crossLink}}, {{#crossLink "TagRequest"}}{{/crossLink}},
* and {{#crossLink "MediaRequest"}}{{/crossLink}}. PreloadJS loaders will typically use a data loader under the
* hood to get data.
* @class AbstractRequest
* @param {LoadItem} item
* @constructor
*/
var AbstractRequest = function (item) {
this._item = item;
};
 
var p = createjs.extend(AbstractRequest, createjs.EventDispatcher);
 
// public methods
/**
* Begin a load.
* @method load
*/
p.load = function() {};
 
/**
* Clean up a request.
* @method destroy
*/
p.destroy = function() {};
 
/**
* Cancel an in-progress request.
* @method cancel
*/
p.cancel = function() {};
 
createjs.AbstractRequest = createjs.promote(AbstractRequest, "EventDispatcher");
 
}());
 
//##############################################################################
// TagRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* An {{#crossLink "AbstractRequest"}}{{/crossLink}} that loads HTML tags, such as images and scripts.
* @class TagRequest
* @param {LoadItem} loadItem
* @param {HTMLElement} tag
* @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
*/
function TagRequest(loadItem, tag, srcAttribute) {
this.AbstractRequest_constructor(loadItem);
 
// protected properties
/**
* The HTML tag instance that is used to load.
* @property _tag
* @type {HTMLElement}
* @protected
*/
this._tag = tag;
 
/**
* The tag attribute that specifies the source, such as "src", "href", etc.
* @property _tagSrcAttribute
* @type {String}
* @protected
*/
this._tagSrcAttribute = srcAttribute;
 
/**
* A method closure used for handling the tag load event.
* @property _loadedHandler
* @type {Function}
* @private
*/
this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
 
/**
* Determines if the element was added to the DOM automatically by PreloadJS, so it can be cleaned up after.
* @property _addedToDOM
* @type {Boolean}
* @private
*/
this._addedToDOM = false;
 
/**
* Determines what the tags initial style.visibility was, so we can set it correctly after a load.
*
* @type {null}
* @private
*/
this._startTagVisibility = null;
};
 
var p = createjs.extend(TagRequest, createjs.AbstractRequest);
 
// public methods
p.load = function () {
this._tag.onload = createjs.proxy(this._handleTagComplete, this);
this._tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this);
this._tag.onerror = createjs.proxy(this._handleError, this);
 
var evt = new createjs.Event("initialize");
evt.loader = this._tag;
 
this.dispatchEvent(evt);
 
this._hideTag();
 
this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
 
this._tag[this._tagSrcAttribute] = this._item.src;
 
// wdg:: Append the tag AFTER setting the src, or SVG loading on iOS will fail.
if (this._tag.parentNode == null) {
window.document.body.appendChild(this._tag);
this._addedToDOM = true;
}
};
 
p.destroy = function() {
this._clean();
this._tag = null;
 
this.AbstractRequest_destroy();
};
 
// private methods
/**
* Handle the readyStateChange event from a tag. We need this in place of the `onload` callback (mainly SCRIPT
* and LINK tags), but other cases may exist.
* @method _handleReadyStateChange
* @private
*/
p._handleReadyStateChange = function () {
clearTimeout(this._loadTimeout);
// This is strictly for tags in browsers that do not support onload.
var tag = this._tag;
 
// Complete is for old IE support.
if (tag.readyState == "loaded" || tag.readyState == "complete") {
this._handleTagComplete();
}
};
 
/**
* Handle any error events from the tag.
* @method _handleError
* @protected
*/
p._handleError = function() {
this._clean();
this.dispatchEvent("error");
};
 
/**
* Handle the tag's onload callback.
* @method _handleTagComplete
* @private
*/
p._handleTagComplete = function () {
this._rawResult = this._tag;
this._result = this.resultFormatter && this.resultFormatter(this) || this._rawResult;
 
this._clean();
this._showTag();
 
this.dispatchEvent("complete");
};
 
/**
* The tag request has not loaded within the time specified in loadTimeout.
* @method _handleError
* @param {Object} event The XHR error event.
* @private
*/
p._handleTimeout = function () {
this._clean();
this.dispatchEvent(new createjs.Event("timeout"));
};
 
/**
* Remove event listeners, but don't destroy the request object
* @method _clean
* @private
*/
p._clean = function() {
this._tag.onload = null;
this._tag.onreadystatechange = null;
this._tag.onerror = null;
if (this._addedToDOM && this._tag.parentNode != null) {
this._tag.parentNode.removeChild(this._tag);
}
clearTimeout(this._loadTimeout);
};
 
p._hideTag = function() {
this._startTagVisibility = this._tag.style.visibility;
this._tag.style.visibility = "hidden";
};
 
p._showTag = function() {
this._tag.style.visibility = this._startTagVisibility;
};
 
/**
* Handle a stalled audio event. The main place this happens is with HTMLAudio in Chrome when playing back audio
* that is already in a load, but not complete.
* @method _handleStalled
* @private
*/
p._handleStalled = function () {
//Ignore, let the timeout take care of it. Sometimes its not really stopped.
};
 
createjs.TagRequest = createjs.promote(TagRequest, "AbstractRequest");
 
}());
 
//##############################################################################
// MediaTagRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* An {{#crossLink "TagRequest"}}{{/crossLink}} that loads HTML tags for video and audio.
* @class MediaTagRequest
* @param {LoadItem} loadItem
* @param {HTMLAudioElement|HTMLVideoElement} tag
* @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
* @constructor
*/
function MediaTagRequest(loadItem, tag, srcAttribute) {
this.AbstractRequest_constructor(loadItem);
 
// protected properties
this._tag = tag;
this._tagSrcAttribute = srcAttribute;
this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
};
 
var p = createjs.extend(MediaTagRequest, createjs.TagRequest);
var s = MediaTagRequest;
 
// public methods
p.load = function () {
var sc = createjs.proxy(this._handleStalled, this);
this._stalledCallback = sc;
 
var pc = createjs.proxy(this._handleProgress, this);
this._handleProgress = pc;
 
this._tag.addEventListener("stalled", sc);
this._tag.addEventListener("progress", pc);
 
// This will tell us when audio is buffered enough to play through, but not when its loaded.
// The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient.
this._tag.addEventListener && this._tag.addEventListener("canplaythrough", this._loadedHandler, false); // canplaythrough callback doesn't work in Chrome, so we use an event.
 
this.TagRequest_load();
};
 
// private methods
p._handleReadyStateChange = function () {
clearTimeout(this._loadTimeout);
// This is strictly for tags in browsers that do not support onload.
var tag = this._tag;
 
// Complete is for old IE support.
if (tag.readyState == "loaded" || tag.readyState == "complete") {
this._handleTagComplete();
}
};
 
p._handleStalled = function () {
//Ignore, let the timeout take care of it. Sometimes its not really stopped.
};
 
/**
* An XHR request has reported progress.
* @method _handleProgress
* @param {Object} event The XHR progress event.
* @private
*/
p._handleProgress = function (event) {
if (!event || event.loaded > 0 && event.total == 0) {
return; // Sometimes we get no "total", so just ignore the progress event.
}
 
var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
this.dispatchEvent(newEvent);
};
 
// protected methods
p._clean = function () {
this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
this._tag.removeEventListener("stalled", this._stalledCallback);
this._tag.removeEventListener("progress", this._progressCallback);
 
this.TagRequest__clean();
};
 
createjs.MediaTagRequest = createjs.promote(MediaTagRequest, "TagRequest");
 
}());
 
//##############################################################################
// XHRRequest.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* A preloader that loads items using XHR requests, usually XMLHttpRequest. However XDomainRequests will be used
* for cross-domain requests if possible, and older versions of IE fall back on to ActiveX objects when necessary.
* XHR requests load the content as text or binary data, provide progress and consistent completion events, and
* can be canceled during load. Note that XHR is not supported in IE 6 or earlier, and is not recommended for
* cross-domain loading.
* @class XHRRequest
* @constructor
* @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* for an overview of supported file properties.
* @extends AbstractLoader
*/
function XHRRequest (item) {
this.AbstractRequest_constructor(item);
 
// protected properties
/**
* A reference to the XHR request used to load the content.
* @property _request
* @type {XMLHttpRequest | XDomainRequest | ActiveX.XMLHTTP}
* @private
*/
this._request = null;
 
/**
* A manual load timeout that is used for browsers that do not support the onTimeout event on XHR (XHR level 1,
* typically IE9).
* @property _loadTimeout
* @type {Number}
* @private
*/
this._loadTimeout = null;
 
/**
* The browser's XHR (XMLHTTPRequest) version. Supported versions are 1 and 2. There is no official way to detect
* the version, so we use capabilities to make a best guess.
* @property _xhrLevel
* @type {Number}
* @default 1
* @private
*/
this._xhrLevel = 1;
 
/**
* The response of a loaded file. This is set because it is expensive to look up constantly. This property will be
* null until the file is loaded.
* @property _response
* @type {mixed}
* @private
*/
this._response = null;
 
/**
* The response of the loaded file before it is modified. In most cases, content is converted from raw text to
* an HTML tag or a formatted object which is set to the <code>result</code> property, but the developer may still
* want to access the raw content as it was loaded.
* @property _rawResponse
* @type {String|Object}
* @private
*/
this._rawResponse = null;
 
this._canceled = false;
 
// Setup our event handlers now.
this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this);
this._handleProgressProxy = createjs.proxy(this._handleProgress, this);
this._handleAbortProxy = createjs.proxy(this._handleAbort, this);
this._handleErrorProxy = createjs.proxy(this._handleError, this);
this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this);
this._handleLoadProxy = createjs.proxy(this._handleLoad, this);
this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this);
 
if (!this._createXHR(item)) {
//TODO: Throw error?
}
};
 
var p = createjs.extend(XHRRequest, createjs.AbstractRequest);
 
// static properties
/**
* A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE.
* @property ACTIVEX_VERSIONS
* @type {Array}
* @since 0.4.2
* @private
*/
XHRRequest.ACTIVEX_VERSIONS = [
"Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP",
"Microsoft.XMLHTTP"
];
 
// Public methods
/**
* Look up the loaded result.
* @method getResult
* @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content
* loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
* returned instead.
* @return {Object} A result object containing the content that was loaded, such as:
* <ul>
* <li>An image tag (&lt;image /&gt;) for images</li>
* <li>A script tag for JavaScript (&lt;script /&gt;). Note that scripts loaded with tags may be added to the
* HTML head.</li>
* <li>A style tag for CSS (&lt;style /&gt;)</li>
* <li>Raw text for TEXT</li>
* <li>A formatted JavaScript object defined by JSON</li>
* <li>An XML document</li>
* <li>An binary arraybuffer loaded by XHR</li>
* </ul>
* Note that if a raw result is requested, but not found, the result will be returned instead.
*/
p.getResult = function (raw) {
if (raw && this._rawResponse) {
return this._rawResponse;
}
return this._response;
};
 
// Overrides abstract method in AbstractRequest
p.cancel = function () {
this.canceled = true;
this._clean();
this._request.abort();
};
 
// Overrides abstract method in AbstractLoader
p.load = function () {
if (this._request == null) {
this._handleError();
return;
}
 
//Events
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) {
this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
}
 
// Sometimes we get back 404s immediately, particularly when there is a cross origin request. // note this does not catch in Chrome
try {
if (!this._item.values || this._item.method == createjs.AbstractLoader.GET) {
this._request.send();
} else if (this._item.method == createjs.AbstractLoader.POST) {
this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
}
} catch (error) {
this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
}
};
 
p.setResponseType = function (type) {
// 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;
};
 
/**
* Get all the response headers from the XmlHttpRequest.
*
* <strong>From the docs:</strong> Return all the HTTP headers, excluding headers that are a case-insensitive match
* for Set-Cookie or Set-Cookie2, as a single string, with each header line separated by a U+000D CR U+000A LF pair,
* excluding the status line, and with each header name and header value separated by a U+003A COLON U+0020 SPACE
* pair.
* @method getAllResponseHeaders
* @return {String}
* @since 0.4.1
*/
p.getAllResponseHeaders = function () {
if (this._request.getAllResponseHeaders instanceof Function) {
return this._request.getAllResponseHeaders();
} else {
return null;
}
};
 
/**
* Get a specific response header from the XmlHttpRequest.
*
* <strong>From the docs:</strong> Returns the header field value from the response of which the field name matches
* header, unless the field name is Set-Cookie or Set-Cookie2.
* @method getResponseHeader
* @param {String} header The header name to retrieve.
* @return {String}
* @since 0.4.1
*/
p.getResponseHeader = function (header) {
if (this._request.getResponseHeader instanceof Function) {
return this._request.getResponseHeader(header);
} else {
return null;
}
};
 
// protected methods
/**
* The XHR request has reported progress.
* @method _handleProgress
* @param {Object} event The XHR progress event.
* @private
*/
p._handleProgress = function (event) {
if (!event || event.loaded > 0 && event.total == 0) {
return; // Sometimes we get no "total", so just ignore the progress event.
}
 
var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
this.dispatchEvent(newEvent);
};
 
/**
* The XHR request has reported a load start.
* @method _handleLoadStart
* @param {Object} event The XHR loadStart event.
* @private
*/
p._handleLoadStart = function (event) {
clearTimeout(this._loadTimeout);
this.dispatchEvent("loadstart");
};
 
/**
* The XHR request has reported an abort event.
* @method handleAbort
* @param {Object} event The XHR abort event.
* @private
*/
p._handleAbort = function (event) {
this._clean();
this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED", null, event));
};
 
/**
* The XHR request has reported an error event.
* @method _handleError
* @param {Object} event The XHR error event.
* @private
*/
p._handleError = function (event) {
this._clean();
this.dispatchEvent(new createjs.ErrorEvent(event.message));
};
 
/**
* The XHR request has reported a readyState change. Note that older browsers (IE 7 & 8) do not provide an onload
* event, so we must monitor the readyStateChange to determine if the file is loaded.
* @method _handleReadyStateChange
* @param {Object} event The XHR readyStateChange event.
* @private
*/
p._handleReadyStateChange = function (event) {
if (this._request.readyState == 4) {
this._handleLoad();
}
};
 
/**
* The XHR request has completed. This is called by the XHR request directly, or by a readyStateChange that has
* <code>request.readyState == 4</code>. Only the first call to this method will be processed.
* @method _handleLoad
* @param {Object} event The XHR load event.
* @private
*/
p._handleLoad = function (event) {
if (this.loaded) {
return;
}
this.loaded = true;
 
var error = this._checkError();
if (error) {
this._handleError(error);
return;
}
 
this._response = this._getResponse();
// 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"));
};
 
/**
* The XHR request has timed out. This is called by the XHR request directly, or via a <code>setTimeout</code>
* callback.
* @method _handleTimeout
* @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout.
* @private
*/
p._handleTimeout = function (event) {
this._clean();
 
this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
};
 
// Protected
/**
* Determine if there is an error in the current load. This checks the status of the request for problem codes. Note
* that this does not check for an actual response. Currently, it only checks for 404 or 0 error code.
* @method _checkError
* @return {int} If the request status returns an error code.
* @private
*/
p._checkError = function () {
//LM: Probably need additional handlers here, maybe 501
var status = parseInt(this._request.status);
 
switch (status) {
case 404: // Not Found
case 0: // Not Loaded
return new Error(status);
}
return null;
};
 
/**
* Validate the response. Different browsers have different approaches, some of which throw errors when accessed
* in other browsers. If there is no response, the <code>_response</code> property will remain null.
* @method _getResponse
* @private
*/
p._getResponse = function () {
if (this._response != null) {
return this._response;
}
 
if (this._request.response != null) {
return this._request.response;
}
 
// Android 2.2 uses .responseText
try {
if (this._request.responseText != null) {
return this._request.responseText;
}
} catch (e) {
}
 
// When loading XML, IE9 does not return .response, instead it returns responseXML.xml
try {
if (this._request.responseXML != null) {
return this._request.responseXML;
}
} catch (e) {
}
 
return null;
};
 
/**
* Create an XHR request. Depending on a number of factors, we get totally different results.
* <ol><li>Some browsers get an <code>XDomainRequest</code> when loading cross-domain.</li>
* <li>XMLHttpRequest are created when available.</li>
* <li>ActiveX.XMLHTTP objects are used in older IE browsers.</li>
* <li>Text requests override the mime type if possible</li>
* <li>Origin headers are sent for crossdomain requests in some browsers.</li>
* <li>Binary loads set the response type to "arraybuffer"</li></ol>
* @method _createXHR
* @param {Object} item The requested item that is being loaded.
* @return {Boolean} If an XHR request or equivalent was successfully created.
* @private
*/
p._createXHR = function (item) {
// Check for cross-domain loads. We can't fully support them, but we can try.
var crossdomain = createjs.RequestUtils.isCrossDomain(item);
var headers = {};
 
// Create the request. Fallback to whatever support we have.
var req = null;
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
// This is 8 or 9, so use XDomainRequest instead.
if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) {
req = new XDomainRequest();
}
} else { // Old IE versions use a different approach
for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
var axVersion = s.ACTIVEX_VERSIONS[i];
try {
req = new ActiveXObject(axVersion);
break;
} catch (e) {
}
}
if (req == null) {
return false;
}
}
 
// Default to utf-8 for Text requests.
if (item.mimeType == null && createjs.RequestUtils.isText(item.type)) {
item.mimeType = "text/plain; charset=utf-8";
}
 
// IE9 doesn't support overrideMimeType(), so we need to check for it.
if (item.mimeType && req.overrideMimeType) {
req.overrideMimeType(item.mimeType);
}
 
// Determine the XHR level
this._xhrLevel = (typeof req.responseType === "string") ? 2 : 1;
 
var src = null;
if (item.method == createjs.AbstractLoader.GET) {
src = createjs.RequestUtils.buildPath(item.src, item.values);
} else {
src = item.src;
}
 
// Open the request. Set cross-domain flags if it is supported (XHR level 1 only)
req.open(item.method || createjs.AbstractLoader.GET, src, true);
 
if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) {
headers["Origin"] = location.origin;
}
 
// To send data we need to set the Content-type header)
if (item.values && item.method == createjs.AbstractLoader.POST) {
headers["Content-Type"] = "application/x-www-form-urlencoded";
}
 
if (!crossdomain && !headers["X-Requested-With"]) {
headers["X-Requested-With"] = "XMLHttpRequest";
}
 
if (item.headers) {
for (var n in item.headers) {
headers[n] = item.headers[n];
}
}
 
for (n in headers) {
req.setRequestHeader(n, headers[n])
}
 
if (req instanceof XMLHttpRequest && item.withCredentials !== undefined) {
req.withCredentials = item.withCredentials;
}
 
this._request = req;
 
return true;
};
 
/**
* A request has completed (or failed or canceled), and needs to be disposed.
* @method _clean
* @private
*/
p._clean = function () {
clearTimeout(this._loadTimeout);
 
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 () {
return "[PreloadJS XHRRequest]";
};
 
createjs.XHRRequest = createjs.promote(XHRRequest, "AbstractRequest");
 
}());
 
//##############################################################################
// SoundLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
// constructor
/**
* A loader for HTML audio files. PreloadJS can not load WebAudio files, as a WebAudio context is required, which
* should be created by either a library playing the sound (such as <a href="http://soundjs.com">SoundJS</a>, or an
* external framework that handles audio playback. To load content that can be played by WebAudio, use the
* {{#crossLink "BinaryLoader"}}{{/crossLink}}, and handle the audio context decoding manually.
* @class SoundLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @extends AbstractMediaLoader
* @constructor
*/
function SoundLoader(loadItem, preferXHR) {
this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);
 
// protected properties
if (createjs.RequestUtils.isAudioTag(loadItem)) {
this._tag = loadItem;
} else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
this._tag = loadItem;
} else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
}
 
if (this._tag != null) {
this._preferXHR = false;
}
};
 
var p = createjs.extend(SoundLoader, createjs.AbstractMediaLoader);
var s = SoundLoader;
 
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "AbstractLoader/SOUND:property"}}{{/crossLink}}.
* @method canLoadItem
* @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
* @returns {Boolean} Whether the loader can load the item.
* @static
*/
s.canLoadItem = function (item) {
return item.type == createjs.AbstractLoader.SOUND;
};
 
// protected methods
p._createTag = function (src) {
var tag = document.createElement("audio");
tag.autoplay = false;
tag.preload = "none";
 
//LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
tag.src = src;
return tag;
};
 
createjs.SoundLoader = createjs.promote(SoundLoader, "AbstractMediaLoader");
 
}());
 
//##############################################################################
// AudioSprite.js
//##############################################################################
 
// NOTE this is "Class" is purely to document audioSprite Setup and usage.
 
 
/**
* <strong>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.</strong>
*
* 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.
*
* <strong>Benefits of Audio Sprites:</strong>
* <ul>
* <li>More robust support for older browsers and devices that only allow a single audio instance, such as iOS 5.</li>
* <li>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.</li>
* <li>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.</li>
* </ul>
*
* <strong>Drawbacks of Audio Sprites</strong>
* <ul>
* <li>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.</li>
* <li>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.</li>
* <li>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.</li>
* </ul>
*
* <h4>Example</h4>
*
* 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
* <a href="https://github.com/tonistiigi/audiosprite" target="_blank">https://github.com/tonistiigi/audiosprite</a>,
* as well as a <a href="http://jsfiddle.net/bharat_battu/g8fFP/12/" target="_blank">jsfiddle</a> 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:
* <ul>
* <li>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 <code>INTERRUPT_TYPE</code>
* constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.</li>
* <li>delay - The amount of time to delay the start of audio playback, in milliseconds.</li>
* <li>offset - The offset from the start of the audio to begin playback, in milliseconds.</li>
* <li>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.</li>
* <li>volume - The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.</li>
* <li>pan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).</li>
* <li>startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.</li>
* <li>duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.</li>
* </ul>
*
* <h4>Example</h4>
*
* var ppc = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
* createjs.Sound.play("mySound", ppc);
* mySoundInstance.play(ppc);
*
* @class PlayPropsConfig
* @constructor
* @since 0.6.1
*/
// TODO think of a better name for this class
var PlayPropsConfig = function () {
// Public Properties
/**
* How to interrupt any currently playing instances of audio with the same source,
* if the maximum number of instances of the sound are already playing. Values are defined as
* <code>INTERRUPT_TYPE</code> constants on the Sound class, with the default defined by
* {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
* @property interrupt
* @type {string}
* @default null
*/
this.interrupt = null;
 
/**
* The amount of time to delay the start of audio playback, in milliseconds.
* @property delay
* @type {Number}
* @default null
*/
this.delay = null;
 
/**
* The offset from the start of the audio to begin playback, in milliseconds.
* @property offset
* @type {number}
* @default null
*/
this.offset = null;
 
/**
* How many times the audio loops when it reaches the end of playback. The default is 0 (no
* loops), and -1 can be used for infinite playback.
* @property loop
* @type {number}
* @default null
*/
this.loop = null;
 
/**
* The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.
* @property volume
* @type {number}
* @default null
*/
this.volume = null;
 
/**
* The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* @property pan
* @type {number}
* @default null
*/
this.pan = null;
 
/**
* Used to create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* @property startTime
* @type {number}
* @default null
*/
this.startTime = null;
 
/**
* Used to create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
* @property duration
* @type {number}
* @default null
*/
this.duration = null;
};
var p = PlayPropsConfig.prototype = {};
var s = PlayPropsConfig;
 
 
// Static Methods
/**
* Creates a PlayPropsConfig from another PlayPropsConfig or an Object.
*
* @method create
* @param {PlayPropsConfig|Object} value The play properties
* @returns {PlayPropsConfig}
* @static
*/
s.create = function (value) {
if (value instanceof s || value instanceof Object) {
var ppc = new createjs.PlayPropsConfig();
ppc.set(value);
return ppc;
} else {
throw new Error("Type not recognized.");
}
};
 
// Public Methods
/**
* Provides a chainable shortcut method for setting a number of properties on the instance.
*
* <h4>Example</h4>
*
* var PlayPropsConfig = new createjs.PlayPropsConfig().set({loop:-1, volume:0.7});
*
* @method set
* @param {Object} props A generic object containing properties to copy to the PlayPropsConfig instance.
* @return {PlayPropsConfig} Returns the instance the method is called on (useful for chaining calls.)
*/
p.set = function(props) {
for (var n in props) { this[n] = props[n]; }
return this;
};
 
p.toString = function() {
return "[PlayPropsConfig]";
};
 
createjs.PlayPropsConfig = s;
 
}());
 
//##############################################################################
// Sound.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
 
 
(function () {
"use strict";
 
/**
* The Sound class is the public API for creating sounds, controlling the overall sound levels, and managing plugins.
* All Sound APIs on this class are static.
*
* <b>Registering and Preloading</b><br />
* Before you can play a sound, it <b>must</b> be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}},
* or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. If you don't register a
* sound prior to attempting to play it using {{#crossLink "Sound/play"}}{{/crossLink}} or create it using {{#crossLink "Sound/createInstance"}}{{/crossLink}},
* the sound source will be automatically registered but playback will fail as the source will not be ready. If you use
* <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, registration is handled for you when the sound is
* preloaded. It is recommended to preload sounds either internally using the register functions or externally using
* PreloadJS so they are ready when you want to use them.
*
* <b>Playback</b><br />
* To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method.
* This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc.
* Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs.
*
* <b>Plugins</b><br />
* By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
* are used (when available), although developers can change plugin priority or add new plugins (such as the
* provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API
* methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see
* {{#crossLink "Sound/installPlugins"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]);
* createjs.Sound.alternateExtensions = ["mp3"];
* 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", this.handleComplete, this);
* instance.volume = 0.5;
* }
*
* The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument
* of {{#crossLink "Sound/registerSound"}}{{/crossLink}}. Note that if not specified, the active plugin will apply
* a default limit. Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a
* default limit of 100.
*
* createjs.Sound.registerSound("sound.mp3", "soundId", 4);
*
* Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
* automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
* load. As a result, it may fail to play the first time play is called if the audio is not finished loading. Use
* the {{#crossLink "Sound/fileload: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);
*
* <b>Audio Sprites</b><br />
* 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.
*
* <h4>Example</h4>
*
* 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");
*
* <b>Mobile Playback</b><br />
* 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.
* <ul>
* <li>
* 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.
* </li>
* <li>
* 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")
* </li>
* <li>
* 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.
* </li>
* <li>
* When using the <a href="http://www.createjs.com/docs/easeljs/classes/Touch.html">EaselJS Touch class</a>,
* 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:
* <ol>
* <li>Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).</li>
* <li>Set the `preventSelection` property on the EaselJS `Stage` to `false`.</li>
* </ol>
* These settings may change how your application behaves, and are not recommended.
* </li>
* </ul>
*
* <b>Loading Alternate Paths and Extension-less Files</b><br />
* 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.
*
* Priority is determined by the property order (first property is tried first). This is supported by both internal loading
* and loading with PreloadJS.
*
* <em>Note: an id is required for playback.</em>
*
* <h4>Example</h4>
*
* 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);
*
* <h3>Known Browser and OS issues</h3>
* <b>IE 9 HTML Audio limitations</b><br />
* <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
* muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
* when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
* <li>MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
* encoding with 64kbps works.</li>
* <li>Occasionally very short samples will get cut off.</li>
* <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by
* hardware and browser settings. See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe
* estimate.</li></ul>
*
* <b>Firefox 25 Web Audio limitations</b>
* <ul><li>mp3 audio files do not load properly on all windows machines, reported
* <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br>
* For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if
* possible.</li></ul>
 
* <b>Safari limitations</b><br />
* <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
*
* <b>iOS 6 Web Audio limitations</b><br />
* <ul><li>Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
* Mobile Playback above.</li>
* <li>A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio
* at a different sampleRate.</li>
* </ul>
*
* <b>Android HTML Audio limitations</b><br />
* <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
* <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use
* a delay.</li></ul>
*
* <b>Web Audio and PreloadJS</b><br />
* <ul><li>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.</li><ul>
*
* @class Sound
* @static
* @uses EventDispatcher
*/
function Sound() {
throw "Sound cannot be instantiated";
}
 
var s = Sound;
 
 
// Static Properties
/**
* The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
* instances of the sound are already playing.
* @property INTERRUPT_ANY
* @type {String}
* @default any
* @static
*/
s.INTERRUPT_ANY = "any";
 
/**
* The interrupt value to interrupt the earliest currently playing instance with the same source that progressed the
* least distance in the audio track, if the maximum number of instances of the sound are already playing.
* @property INTERRUPT_EARLY
* @type {String}
* @default early
* @static
*/
s.INTERRUPT_EARLY = "early";
 
/**
* The interrupt value to interrupt the currently playing instance with the same source that progressed the most
* distance in the audio track, if the maximum number of instances of the sound are already playing.
* @property INTERRUPT_LATE
* @type {String}
* @default late
* @static
*/
s.INTERRUPT_LATE = "late";
 
/**
* The interrupt value to not interrupt any currently playing instances with the same source, if the maximum number of
* instances of the sound are already playing.
* @property INTERRUPT_NONE
* @type {String}
* @default none
* @static
*/
s.INTERRUPT_NONE = "none";
 
/**
* Defines the playState of an instance that is still initializing.
* @property PLAY_INITED
* @type {String}
* @default playInited
* @static
*/
s.PLAY_INITED = "playInited";
 
/**
* Defines the playState of an instance that is currently playing or paused.
* @property PLAY_SUCCEEDED
* @type {String}
* @default playSucceeded
* @static
*/
s.PLAY_SUCCEEDED = "playSucceeded";
 
/**
* Defines the playState of an instance that was interrupted by another instance.
* @property PLAY_INTERRUPTED
* @type {String}
* @default playInterrupted
* @static
*/
s.PLAY_INTERRUPTED = "playInterrupted";
 
/**
* Defines the playState of an instance that completed playback.
* @property PLAY_FINISHED
* @type {String}
* @default playFinished
* @static
*/
s.PLAY_FINISHED = "playFinished";
 
/**
* Defines the playState of an instance that failed to play. This is usually caused by a lack of available channels
* when the interrupt mode was "INTERRUPT_NONE", the playback stalled, or the sound could not be found.
* @property PLAY_FAILED
* @type {String}
* @default playFailed
* @static
*/
s.PLAY_FAILED = "playFailed";
 
/**
* A list of the default supported extensions that Sound will <i>try</i> to play. Plugins will check if the browser
* can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to
* support additional media types.
*
* NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
*
* More details on file formats can be found at <a href="http://en.wikipedia.org/wiki/Audio_file_format" target="_blank">http://en.wikipedia.org/wiki/Audio_file_format</a>.<br />
* A very detailed list of file formats can be found at <a href="http://www.fileinfo.com/filetypes/audio" target="_blank">http://www.fileinfo.com/filetypes/audio</a>.
* @property SUPPORTED_EXTENSIONS
* @type {Array[String]}
* @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
* @since 0.4.0
* @static
*/
s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];
 
/**
* Some extensions use another type of extension support to play (one of them is a codex). This allows you to map
* that support so plugins can accurately determine if an extension is supported. Adding to this list can help
* plugins determine more accurately if an extension is supported.
*
* A useful list of extensions for each format can be found at <a href="http://html5doctor.com/html5-audio-the-state-of-play/" target="_blank">http://html5doctor.com/html5-audio-the-state-of-play/</a>.
* @property EXTENSION_MAP
* @type {Object}
* @since 0.4.0
* @default {m4a:"mp4"}
* @static
*/
s.EXTENSION_MAP = {
m4a:"mp4"
};
 
/**
* The RegExp pattern used to parse file URIs. This supports simple file names, as well as full domain URIs with
* query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6.
* @property FILE_PATTERN
* @type {RegExp}
* @static
* @protected
*/
s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
 
 
// Class Public properties
/**
* Determines the default behavior for interrupting other currently playing instances with the same source, if the
* maximum number of instances of the sound are already playing. Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}
* but this can be set and will change playback behavior accordingly. This is only used when {{#crossLink "Sound/play"}}{{/crossLink}}
* is called without passing a value for interrupt.
* @property defaultInterruptBehavior
* @type {String}
* @default Sound.INTERRUPT_NONE, or "none"
* @static
* @since 0.4.0
*/
s.defaultInterruptBehavior = s.INTERRUPT_NONE; // OJR does s.INTERRUPT_ANY make more sense as default? Needs game dev testing to see which case makes more sense.
 
/**
* An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin.
* These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your
* extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need
* to exist in the same location, as only the extension is altered.
*
* Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}}
* and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading.
*
* <h4>Example</h4>
*
* 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.
*
* <h4>Example</h4>
*
* createjs.Sound.volume = 0.5;
*
*
* @property volume
* @type {Number}
* @default 1
* @since 0.6.1
*/
s._masterVolume = 1;
Object.defineProperty(s, "volume", {
get: function () {return this._masterVolume;},
set: function (value) {
if (Number(value) == null) {return false;}
value = Math.max(0, Math.min(1, value));
s._masterVolume = value;
if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterVolume(value);
}
}
}
});
 
/**
* Mute/Unmute all audio. Note that muted audio still plays at 0 volume. This global mute value is maintained
* separately and when set will override, but not change the mute property of individual instances. To mute an individual
* instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
*
* <h4>Example</h4>
*
* createjs.Sound.muted = true;
*
*
* @property muted
* @type {Boolean}
* @default false
* @since 0.6.1
*/
s._masterMute = false;
// OJR references to the methods were not working, so the code had to be duplicated here
Object.defineProperty(s, "muted", {
get: function () {return this._masterMute;},
set: function (value) {
if (value == null) {return false;}
 
this._masterMute = value;
if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterMute(value);
}
}
return true;
}
});
 
/**
* Get the active plugins capabilities, which help determine if a plugin can be used in the current environment,
* or if the plugin supports a specific feature. Capabilities include:
* <ul>
* <li><b>panning:</b> If the plugin can pan audio from left to right</li>
* <li><b>volume;</b> If the plugin can control audio volume.</li>
* <li><b>tracks:</b> The maximum number of audio tracks that can be played back at a time. This will be -1
* if there is no known limit.</li>
* <br />An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
* <li><b>mp3:</b> If MP3 audio is supported.</li>
* <li><b>ogg:</b> If OGG audio is supported.</li>
* <li><b>wav:</b> If WAV audio is supported.</li>
* <li><b>mpeg:</b> If MPEG audio is supported.</li>
* <li><b>m4a:</b> If M4A audio is supported.</li>
* <li><b>mp4:</b> If MP4 audio is supported.</li>
* <li><b>aiff:</b> If aiff audio is supported.</li>
* <li><b>wma:</b> If wma audio is supported.</li>
* <li><b>mid:</b> If mid audio is supported.</li>
* </ul>
*
* You can get a specific capability of the active plugin using standard object notation
*
* <h4>Example</h4>
*
* var mp3 = createjs.Sound.capabilities.mp3;
*
* Note this property is read only.
*
* @property capabilities
* @type {Object}
* @static
* @readOnly
* @since 0.6.1
*/
Object.defineProperty(s, "capabilities", {
get: function () {
if (s.activePlugin == null) {return null;}
return s.activePlugin._capabilities;
},
set: function (value) { return false;}
});
 
 
// Class Private properties
/**
* Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
* plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}).
* If plugins have been registered, but none are applicable, then sound playback will fail.
* @property _pluginsRegistered
* @type {Boolean}
* @default false
* @static
* @protected
*/
s._pluginsRegistered = false;
 
/**
* Used internally to assign unique IDs to each AbstractSoundInstance.
* @property _lastID
* @type {Number}
* @static
* @protected
*/
s._lastID = 0;
 
/**
* An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
* all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
* When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}}
* method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}}
* method.
* @property _instances
* @type {Array}
* @protected
* @static
*/
s._instances = [];
 
/**
* An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
* @property _idHash
* @type {Object}
* @protected
* @static
*/
s._idHash = {};
 
/**
* An object hash that stores preloading sound sources via the parsed source that is passed to the plugin. Contains the
* source, id, and data that was passed in by the user. Parsed sources can contain multiple instances of source, id,
* and data.
* @property _preloadHash
* @type {Object}
* @protected
* @static
*/
s._preloadHash = {};
 
/**
* An object hash storing {{#crossLink "PlayPropsConfig"}}{{/crossLink}} via the parsed source that is passed as defaultPlayProps in
* {{#crossLink "Sound/registerSound"}}{{/crossLink}} and {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* @property _defaultPlayPropsHash
* @type {Object}
* @protected
* @static
* @since 0.6.1
*/
s._defaultPlayPropsHash = {};
 
 
// EventDispatcher methods:
s.addEventListener = null;
s.removeEventListener = null;
s.removeAllEventListeners = null;
s.dispatchEvent = null;
s.hasEventListener = null;
s._listeners = null;
 
createjs.EventDispatcher.initialize(s); // inject EventDispatcher methods.
 
 
// Events
/**
* This event is fired when a file finishes loading internally. This event is fired for each loaded sound,
* so any handler methods should look up the <code>event.src</code> to handle a particular sound.
* @event fileload
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @param {String} src The source of the sound that was loaded.
* @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
* @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
* @since 0.4.1
*/
 
/**
* This event is fired when a file fails loading internally. This event is fired for each loaded sound,
* so any handler methods should look up the <code>event.src</code> to handle a particular sound.
* @event fileerror
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @param {String} src The source of the sound that was loaded.
* @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
* @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
* @since 0.6.0
*/
 
 
// Class Public Methods
/**
* Get the preload rules to allow Sound to be used as a plugin by <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
* Any load calls that have the matching type or extension will fire the callback method, and use the resulting
* object, which is potentially modified by Sound. This helps when determining the correct path, as well as
* registering the audio instance(s) with Sound. This method should not be called, except by PreloadJS.
* @method getPreloadHandlers
* @return {Object} An object containing:
* <ul><li>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.</li>
* <li>types: A list of file types that are supported by Sound (currently supports "sound").</li>
* <li>extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).</li></ul>
* @static
* @protected
*/
s.getPreloadHandlers = function () {
return {
callback:createjs.proxy(s.initLoad, s),
types:["sound"],
extensions:s.SUPPORTED_EXTENSIONS
};
};
 
/**
* Used to dispatch fileload events from internal loading.
* @method _handleLoadComplete
* @param event A loader event.
* @protected
* @static
* @since 0.6.0
*/
s._handleLoadComplete = function(event) {
var src = event.target.getItem().src;
if (!s._preloadHash[src]) {return;}
 
for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
var item = s._preloadHash[src][i];
s._preloadHash[src][i] = true;
 
if (!s.hasEventListener("fileload")) { continue; }
 
var event = new createjs.Event("fileload");
event.src = item.src;
event.id = item.id;
event.data = item.data;
event.sprite = item.sprite;
 
s.dispatchEvent(event);
}
};
 
/**
* Used to dispatch error events from internal preloading.
* @param event
* @protected
* @since 0.6.0
* @static
*/
s._handleLoadError = function(event) {
var src = event.target.getItem().src;
if (!s._preloadHash[src]) {return;}
 
for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
var item = s._preloadHash[src][i];
s._preloadHash[src][i] = false;
 
if (!s.hasEventListener("fileerror")) { continue; }
 
var event = new createjs.Event("fileerror");
event.src = item.src;
event.id = item.id;
event.data = item.data;
event.sprite = item.sprite;
 
s.dispatchEvent(event);
}
};
 
/**
* Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
*
* @method _registerPlugin
* @param {Object} plugin The plugin class to install.
* @return {Boolean} Whether the plugin was successfully initialized.
* @static
* @private
*/
s._registerPlugin = function (plugin) {
// Note: Each plugin is passed in as a class reference, but we store the activePlugin as an instance
if (plugin.isSupported()) {
s.activePlugin = new plugin();
return true;
}
return false;
};
 
/**
* Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
*
* <h4>Example</h4>
*
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
*
* @method registerPlugins
* @param {Array} plugins An array of plugins classes to install.
* @return {Boolean} Whether a plugin was successfully initialized.
* @static
*/
s.registerPlugins = function (plugins) {
s._pluginsRegistered = true;
for (var i = 0, l = plugins.length; i < l; i++) {
if (s._registerPlugin(plugins[i])) {
return true;
}
}
return false;
};
 
/**
* Initialize the default plugins. This method is automatically called when any audio is played or registered before
* the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the
* default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* if (!createjs.initializeDefaultPlugins()) { return; }
*
* @method initializeDefaultPlugins
* @returns {Boolean} True if a plugin was initialized, false otherwise.
* @since 0.4.0
* @static
*/
s.initializeDefaultPlugins = function () {
if (s.activePlugin != null) {return true;}
if (s._pluginsRegistered) {return false;}
if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
return false;
};
 
/**
* Determines if Sound has been initialized, and a plugin has been activated.
*
* <h4>Example</h4>
* This example sets up a Flash fallback, but only if there is no plugin specified yet.
*
* if (!createjs.Sound.isReady()) {
* createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
* createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
* }
*
* @method isReady
* @return {Boolean} If Sound has initialized a plugin.
* @static
*/
s.isReady = function () {
return (s.activePlugin != null);
};
 
/**
* Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
*
* @method getCapabilities
* @return {Object} An object containing the capabilities of the active plugin.
* @static
* @deprecated
*/
s.getCapabilities = function () {
if (s.activePlugin == null) {return null;}
return s.activePlugin._capabilities;
};
 
/**
* Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
*
* @method getCapability
* @param {String} key The capability to retrieve
* @return {Number|Boolean} The value of the capability.
* @static
* @see getCapabilities
* @deprecated
*/
s.getCapability = function (key) {
if (s.activePlugin == null) {return null;}
return s.activePlugin._capabilities[key];
};
 
/**
* Process manifest items from <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. This method is intended
* for usage by a plugin, and not for direct interaction.
* @method initLoad
* @param {Object} src The object to load.
* @return {Object|AbstractLoader} An instance of AbstractLoader.
* @protected
* @static
*/
s.initLoad = function (loadItem) {
return s._registerSound(loadItem);
};
 
/**
* Internal method for loading sounds. This should not be called directly.
*
* @method _registerSound
* @param {Object} src The object to load, containing src property and optionally containing id and data.
* @return {Object} An object with the modified values that were passed in, which defines the sound.
* Returns false if the source cannot be parsed or no plugins can be initialized.
* Returns true if the source is already loaded.
* @static
* @private
* @since 0.6.0
*/
 
s._registerSound = function (loadItem) {
if (!s.initializeDefaultPlugins()) {return false;}
 
var details;
if (loadItem.src instanceof Object) {
details = s._parseSrc(loadItem.src);
details.src = loadItem.path + details.src;
} else {
details = s._parsePath(loadItem.src);
}
if (details == null) {return false;}
loadItem.src = details.src;
loadItem.type = "sound";
 
var data = loadItem.data;
var numChannels = null;
if (data != null) {
if (!isNaN(data.channels)) {
numChannels = parseInt(data.channels);
} else if (!isNaN(data)) {
numChannels = parseInt(data);
}
 
if(data.audioSprite) {
var sp;
for(var i = data.audioSprite.length; i--; ) {
sp = data.audioSprite[i];
s._idHash[sp.id] = {src: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};
 
if (sp.defaultPlayProps) {
s._defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
}
}
}
}
if (loadItem.id != null) {s._idHash[loadItem.id] = {src: loadItem.src}};
var loader = s.activePlugin.register(loadItem);
 
SoundChannel.create(loadItem.src, numChannels);
 
// return the number of instances to the user. This will also be returned in the load event.
if (data == null || !isNaN(data)) {
loadItem.data = numChannels || SoundChannel.maxPerChannel();
} else {
loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
}
 
if (loader.type) {loadItem.type = loader.type;}
 
if (loadItem.defaultPlayProps) {
s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
}
return loader;
};
 
/**
* Register an audio file for loading and future playback in Sound. This is automatically called when using
* <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. It is recommended to register all sounds that
* need to be played back in order to properly prepare and preload them. Sound does internal preloading when required.
*
* <h4>Example</h4>
*
* createjs.Sound.alternateExtensions = ["mp3"];
* createjs.Sound.on("fileload", handleLoad); // add an event listener for when load is completed
* createjs.Sound.registerSound("myAudioPath/mySound.ogg", "myID", 3);
* createjs.Sound.registerSound({ogg:"path1/mySound.ogg", mp3:"path2/mySoundNoExtension"}, "myID", 3);
*
*
* @method registerSound
* @param {String | Object} src The source or an Object with a "src" property or an Object with multiple extension labeled src properties.
* @param {String} [id] An id specified by the user to play the sound later. Note id is required for when src is multiple extension labeled src properties.
* @param {Number | Object} [data] Data associated with the item. Sound uses the data parameter as the number of
* channels for an audio instance, however a "channels" property can be appended to the data object if it is used
* for other information. The audio channels will set a default based on plugin if no value is found.
* Sound also uses the data property to hold an {{#crossLink "AudioSprite"}}{{/crossLink}} array of objects in the following format {id, startTime, duration}.<br/>
* id used to play the sound later, in the same manner as a sound src with an id.<br/>
* startTime is the initial offset to start playback and loop from, in milliseconds.<br/>
* duration is the amount of time to play the clip for, in milliseconds.<br/>
* This allows Sound to support audio sprites that are played back by id.
* @param {string} basePath Set a path that will be prepended to src for loading.
* @param {Object | PlayPropsConfig} defaultPlayProps Optional Playback properties that will be set as the defaults on any new AbstractSoundInstance.
* See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for options.
* @return {Object} An object with the modified values that were passed in, which defines the sound.
* Returns false if the source cannot be parsed or no plugins can be initialized.
* Returns true if the source is already loaded.
* @static
* @since 0.4.0
*/
s.registerSound = function (src, id, data, basePath, defaultPlayProps) {
var loadItem = {src: src, id: id, data:data, defaultPlayProps:defaultPlayProps};
if (src instanceof Object && src.src) {
basePath = id;
loadItem = src;
}
loadItem = createjs.LoadItem.create(loadItem);
loadItem.path = basePath;
 
if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + src;}
 
var loader = s._registerSound(loadItem);
if(!loader) {return false;}
 
if (!s._preloadHash[loadItem.src]) { s._preloadHash[loadItem.src] = [];}
s._preloadHash[loadItem.src].push(loadItem);
if (s._preloadHash[loadItem.src].length == 1) {
// OJR note this will disallow reloading a sound if loading fails or the source changes
loader.on("complete", createjs.proxy(this._handleLoadComplete, this));
loader.on("error", createjs.proxy(this._handleLoadError, this));
s.activePlugin.preload(loader);
} else {
if (s._preloadHash[loadItem.src][0] == true) {return true;}
}
 
return loadItem;
};
 
/**
* Register an array of audio files for loading and future playback in Sound. It is recommended to register all
* sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading
* when required.
*
* <h4>Example</h4>
*
* var assetPath = "./myAudioPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
* {src:"asset2.mp3", id:"works"}
* {src:{mp3:"path1/asset3.mp3", ogg:"path2/asset3NoExtension"}, id:"better"}
* ];
* createjs.Sound.alternateExtensions = ["mp3"]; // if the passed extension is not supported, try this extension
* createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
* createjs.Sound.registerSounds(sounds, assetPath);
*
* @method registerSounds
* @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for
* {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code>
* with "id" and "data" being optional.
* You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to load.
* Note id is required if src is an object with extension labeled src properties.
* @param {string} basePath Set a path that will be prepended to each src when loading. When creating, playing, or removing
* audio that was loaded with a basePath by src, the basePath must be included.
* @return {Object} An array of objects with the modified values that were passed in, which defines each sound.
* Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized.
* Also, it will return true for any values when the source is already loaded.
* @static
* @since 0.6.0
*/
s.registerSounds = function (sounds, basePath) {
var returnValues = [];
if (sounds.path) {
if (!basePath) {
basePath = sounds.path;
} else {
basePath = basePath + sounds.path;
}
sounds = sounds.manifest;
// TODO document this feature
}
for (var i = 0, l = sounds.length; i < l; i++) {
returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
}
return returnValues;
};
 
/**
* Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* <br />Note this will stop playback on active instances playing this sound before deleting them.
* <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
*
* <h4>Example</h4>
*
* createjs.Sound.removeSound("myID");
* createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
* createjs.Sound.removeSound("myPath/myOtherSound.mp3", "myBasePath/");
* createjs.Sound.removeSound({mp3:"musicNoExtension", ogg:"music.ogg"}, "myBasePath/");
*
* @method removeSound
* @param {String | Object} src The src or ID of the audio, or an Object with a "src" property, or an Object with multiple extension labeled src properties.
* @param {string} basePath Set a path that will be prepended to each src when removing.
* @return {Boolean} True if sound is successfully removed.
* @static
* @since 0.4.1
*/
s.removeSound = function(src, basePath) {
if (s.activePlugin == null) {return false;}
 
if (src instanceof Object && src.src) {src = src.src;}
 
var details;
if (src instanceof Object) {
details = s._parseSrc(src);
} else {
src = s._getSrcById(src).src;
details = s._parsePath(src);
}
if (details == null) {return false;}
src = details.src;
if (basePath != null) {src = basePath + src;}
 
for(var prop in s._idHash){
if(s._idHash[prop].src == src) {
delete(s._idHash[prop]);
}
}
 
// clear from SoundChannel, which also stops and deletes all instances
SoundChannel.removeSrc(src);
 
delete(s._preloadHash[src]);
 
s.activePlugin.removeSound(src);
 
return true;
};
 
/**
* Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* <br />Note this will stop playback on active instances playing this audio before deleting them.
* <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
*
* <h4>Example</h4>
*
* assetPath = "./myPath/";
* var sounds = [
* {src:"asset0.ogg", id:"example"},
* {src:"asset1.ogg", id:"1", data:6},
* {src:"asset2.mp3", id:"works"}
* ];
* createjs.Sound.removeSounds(sounds, assetPath);
*
* @method removeSounds
* @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for
* {{#crossLink "Sound/removeSound"}}{{/crossLink}}: <code>{srcOrID:srcURIorID}</code>.
* You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to remove.
* @param {string} basePath Set a path that will be prepended to each src when removing.
* @return {Object} An array of Boolean values representing if the sounds with the same array index were
* successfully removed.
* @static
* @since 0.4.1
*/
s.removeSounds = function (sounds, basePath) {
var returnValues = [];
if (sounds.path) {
if (!basePath) {
basePath = sounds.path;
} else {
basePath = basePath + sounds.path;
}
sounds = sounds.manifest;
}
for (var i = 0, l = sounds.length; i < l; i++) {
returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath);
}
return returnValues;
};
 
/**
* Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
* {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
* <br />Note this will stop playback on all active sound instances before deleting them.
*
* <h4>Example</h4>
*
* createjs.Sound.removeAllSounds();
*
* @method removeAllSounds
* @static
* @since 0.4.1
*/
s.removeAllSounds = function() {
s._idHash = {};
s._preloadHash = {};
SoundChannel.removeAll();
if (s.activePlugin) {s.activePlugin.removeAllSounds();}
};
 
/**
* Check if a source has been loaded by internal preloaders. This is necessary to ensure that sounds that are
* not completed preloading will not kick off a new internal preload if they are played.
*
* <h4>Example</h4>
*
* var mySound = "assetPath/asset0.ogg";
* if(createjs.Sound.loadComplete(mySound) {
* createjs.Sound.play(mySound);
* }
*
* @method loadComplete
* @param {String} src The src or id that is being loaded.
* @return {Boolean} If the src is already loaded.
* @since 0.4.0
* @static
*/
s.loadComplete = function (src) {
if (!s.isReady()) { return false; }
var details = s._parsePath(src);
if (details) {
src = s._getSrcById(details.src).src;
} else {
src = s._getSrcById(src).src;
}
if(s._preloadHash[src] == undefined) {return false;}
return (s._preloadHash[src][0] == true); // src only loads once, so if it's true for the first it's true for all
};
 
/**
* Parse the path of a sound. Alternate extensions will be attempted in order if the
* current extension is not supported
* @method _parsePath
* @param {String} value The path to an audio source.
* @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
* and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
* @protected
* @static
*/
s._parsePath = function (value) {
if (typeof(value) != "string") {value = value.toString();}
 
var match = value.match(s.FILE_PATTERN);
if (match == null) {return false;}
 
var name = match[4];
var ext = match[5];
var c = s.capabilities;
var i = 0;
while (!c[ext]) {
ext = s.alternateExtensions[i++];
if (i > s.alternateExtensions.length) { return null;} // no extensions are supported
}
value = value.replace("."+match[5], "."+ext);
 
var ret = {name:name, src:value, extension:ext};
return ret;
};
 
/**
* Parse the path of a sound based on properties of src matching with supported extensions.
* Returns false if none of the properties are supported
* @method _parseSrc
* @param {Object} value The paths to an audio source, indexed by extension type.
* @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
* and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
* @protected
* @static
*/
s._parseSrc = function (value) {
var ret = {name:undefined, src:undefined, extension:undefined};
var c = s.capabilities;
 
for (var prop in value) {
if(value.hasOwnProperty(prop) && c[prop]) {
ret.src = value[prop];
ret.extension = prop;
break;
}
}
if (!ret.src) {return false;} // no matches
 
var i = ret.src.lastIndexOf("/");
if (i != -1) {
ret.name = ret.src.slice(i+1);
} else {
ret.name = ret.src;
}
 
return ret;
};
 
/* ---------------
Static API.
--------------- */
/**
* Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a
* AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}.
* Note that even on sounds with failed playback, you may still be able to call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
* since the failure could be due to lack of available channels. If the src does not have a supported extension or
* if there is no available plugin, a default AbstractSoundInstance will be returned which will not play any audio, but will not generate errors.
*
* <h4>Example</h4>
*
* createjs.Sound.on("fileload", handleLoad);
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* createjs.Sound.play("myID");
* // store off AbstractSoundInstance for controlling
* var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
* }
*
* NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* <b>Parameters Deprecated</b><br />
* The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
*
* @method play
* @param {String} src The src or ID of the audio.
* @param {String | Object} [interrupt="none"|options] <b>This parameter will be renamed playProps in the next release.</b><br />
* This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
* including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
* <br /><strong>OR</strong><br />
* <b>Deprecated</b> How to interrupt any currently playing instances of audio with the same source,
* if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
* constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
* @param {Number} [delay=0] <b>Deprecated</b> The amount of time to delay the start of audio playback, in milliseconds.
* @param {Number} [offset=0] <b>Deprecated</b> The offset from the start of the audio to begin playback, in milliseconds.
* @param {Number} [loop=0] <b>Deprecated</b> 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] <b>Deprecated</b> The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.
* @param {Number} [pan=0] <b>Deprecated</b> The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* @param {Number} [startTime=null] <b>Deprecated</b> To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* @param {Number} [duration=null] <b>Deprecated</b> To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
* @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
* @static
*/
s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) {
var playProps;
if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
playProps = createjs.PlayPropsConfig.create(interrupt);
} else {
playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan, startTime:startTime, duration:duration});
}
var instance = s.createInstance(src, playProps.startTime, playProps.duration);
var ok = s._playInstance(instance, playProps);
if (!ok) {instance._playFailed();}
return instance;
};
 
/**
* Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a
* supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be
* called safely but does nothing.
*
* <h4>Example</h4>
*
* var myInstance = null;
* createjs.Sound.on("fileload", handleLoad);
* createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
* function handleLoad(event) {
* myInstance = createjs.Sound.createInstance("myID");
* // alternately we could call the following
* myInstance = createjs.Sound.createInstance("myAudioPath/mySound.mp3");
* }
*
* NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
* This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
*
* @method createInstance
* @param {String} src The src or ID of the audio.
* @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
* @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
* @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
* Unsupported extensions will return the default AbstractSoundInstance.
* @since 0.4.0
* @static
*/
s.createInstance = function (src, startTime, duration) {
if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}
 
var defaultPlayProps = s._defaultPlayPropsHash[src]; // for audio sprites, which create and store defaults by id
src = s._getSrcById(src);
 
var details = s._parsePath(src.src);
 
var instance = null;
if (details != null && details.src != null) {
SoundChannel.create(details.src);
if (startTime == null) {startTime = src.startTime;}
instance = s.activePlugin.create(details.src, startTime, duration || src.duration);
 
defaultPlayProps = defaultPlayProps || s._defaultPlayPropsHash[details.src];
if(defaultPlayProps) {
instance.applyPlayProps(defaultPlayProps);
}
} else {
instance = new createjs.DefaultSoundInstance(src, startTime, duration);
}
 
instance.uniqueId = s._lastID++;
 
return instance;
};
 
/**
* Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped,
* call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* createjs.Sound.stop();
*
* @method stop
* @static
*/
s.stop = function () {
var instances = this._instances;
for (var i = instances.length; i--; ) {
instances[i].stop(); // NOTE stop removes instance from this._instances
}
};
 
/**
* Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
*
* @method setVolume
* @param {Number} value The master volume value. The acceptable range is 0-1.
* @static
* @deprecated
*/
s.setVolume = function (value) {
if (Number(value) == null) {return false;}
value = Math.max(0, Math.min(1, value));
s._masterVolume = value;
if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterVolume(value);
}
}
};
 
/**
* Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
*
* @method getVolume
* @return {Number} The master volume, in a range of 0-1.
* @static
* @deprecated
*/
s.getVolume = function () {
return this._masterVolume;
};
 
/**
* Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
*
* @method setMute
* @param {Boolean} value Whether the audio should be muted or not.
* @return {Boolean} If the mute was set.
* @static
* @since 0.4.0
* @deprecated
*/
s.setMute = function (value) {
if (value == null) {return false;}
 
this._masterMute = value;
if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
var instances = this._instances;
for (var i = 0, l = instances.length; i < l; i++) {
instances[i].setMasterMute(value);
}
}
return true;
};
 
/**
* Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
*
* @method getMute
* @return {Boolean} The mute value of Sound.
* @static
* @since 0.4.0
* @deprecated
*/
s.getMute = function () {
return this._masterMute;
};
 
/**
* Set the default playback properties for all new SoundInstances of the passed in src or ID.
* See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
*
* @method setDefaultPlayProps
* @param {String} src The src or ID used to register the audio.
* @param {Object | PlayPropsConfig} playProps The playback properties you would like to set.
* @since 0.6.1
*/
s.setDefaultPlayProps = function(src, playProps) {
src = s._getSrcById(src);
s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
};
 
/**
* Get the default playback properties for the passed in src or ID. These properties are applied to all
* new SoundInstances. Returns null if default does not exist.
*
* @method getDefaultPlayProps
* @param {String} src The src or ID used to register the audio.
* @returns {PlayPropsConfig} returns an existing PlayPropsConfig or null if one does not exist
* @since 0.6.1
*/
s.getDefaultPlayProps = function(src) {
src = s._getSrcById(src);
return s._defaultPlayPropsHash[s._parsePath(src.src).src];
};
 
 
/* ---------------
Internal methods
--------------- */
/**
* Play an instance. This is called by the static API, as well as from plugins. This allows the core class to
* control delays.
* @method _playInstance
* @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
* @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If the sound can start playing. Sounds that fail immediately will return false. Sounds that
* have a delay will return true, but may still fail to play.
* @protected
* @static
*/
s._playInstance = function (instance, playProps) {
var defaultPlayProps = s._defaultPlayPropsHash[instance.src] || {};
if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || s.defaultInterruptBehavior};
if (playProps.delay == null) {playProps.delay = defaultPlayProps.delay || 0;}
if (playProps.offset == null) {playProps.offset = instance.getPosition();}
if (playProps.loop == null) {playProps.loop = instance.loop;}
if (playProps.volume == null) {playProps.volume = instance.volume;}
if (playProps.pan == null) {playProps.pan = instance.pan;}
 
if (playProps.delay == 0) {
var ok = s._beginPlaying(instance, playProps);
if (!ok) {return false;}
} else {
//Note that we can't pass arguments to proxy OR setTimeout (IE only), so just wrap the function call.
// OJR WebAudio may want to handle this differently, so it might make sense to move this functionality into the plugins in the future
var delayTimeoutId = setTimeout(function () {
s._beginPlaying(instance, playProps);
}, playProps.delay);
instance.delayTimeoutId = delayTimeoutId;
}
 
this._instances.push(instance);
 
return true;
};
 
/**
* Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}.
* @method _beginPlaying
* @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback.
* @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If the sound can start playing. If there are no available channels, or the instance fails to
* start, this will return false.
* @protected
* @static
*/
s._beginPlaying = function (instance, playProps) {
if (!SoundChannel.add(instance, playProps.interrupt)) {
return false;
}
var result = instance._beginPlaying(playProps);
if (!result) {
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);}
return false;
}
return true;
};
 
/**
* Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned
* instead.
* @method _getSrcById
* @param {String} value The ID the sound was registered with.
* @return {String} The source of the sound if it has been registered with this ID or the value that was passed in.
* @protected
* @static
*/
s._getSrcById = function (value) {
return s._idHash[value] || {src: value};
};
 
/**
* A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from
* Sound management. It will be added again, if the sound re-plays. Note that this method is called from the
* instances themselves.
* @method _playFinished
* @param {AbstractSoundInstance} instance The instance that finished playback.
* @protected
* @static
*/
s._playFinished = function (instance) {
SoundChannel.remove(instance);
var index = createjs.indexOf(this._instances, instance);
if (index > -1) {this._instances.splice(index, 1);} // OJR this will always be > -1, there is no way for an instance to exist without being added to this._instances
};
 
createjs.Sound = Sound;
 
/**
* An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for
* each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class.
*
* The number of sounds is artificially limited by Sound in order to prevent over-saturation of a
* single sound, as well as to stay within hardware limitations, although the latter may disappear with better
* browser support.
*
* When a sound is played, this class ensures that there is an available instance, or interrupts an appropriate
* sound that is already playing.
* #class SoundChannel
* @param {String} src The source of the instances
* @param {Number} [max=1] The number of instances allowed
* @constructor
* @protected
*/
function SoundChannel(src, max) {
this.init(src, max);
}
 
/* ------------
Static API
------------ */
/**
* A hash of channel instances indexed by source.
* #property channels
* @type {Object}
* @static
*/
SoundChannel.channels = {};
 
/**
* Create a sound channel. Note that if the sound channel already exists, this will fail.
* #method create
* @param {String} src The source for the channel
* @param {Number} max The maximum amount this channel holds. The default is {{#crossLink "SoundChannel.maxDefault"}}{{/crossLink}}.
* @return {Boolean} If the channels were created.
* @static
*/
SoundChannel.create = function (src, max) {
var channel = SoundChannel.get(src);
if (channel == null) {
SoundChannel.channels[src] = new SoundChannel(src, max);
return true;
}
return false;
};
/**
* Delete a sound channel, stop and delete all related instances. Note that if the sound channel does not exist, this will fail.
* #method remove
* @param {String} src The source for the channel
* @return {Boolean} If the channels were deleted.
* @static
*/
SoundChannel.removeSrc = function (src) {
var channel = SoundChannel.get(src);
if (channel == null) {return false;}
channel._removeAll(); // this stops and removes all active instances
delete(SoundChannel.channels[src]);
return true;
};
/**
* Delete all sound channels, stop and delete all related instances.
* #method removeAll
* @static
*/
SoundChannel.removeAll = function () {
for(var channel in SoundChannel.channels) {
SoundChannel.channels[channel]._removeAll(); // this stops and removes all active instances
}
SoundChannel.channels = {};
};
/**
* Add an instance to a sound channel.
* #method add
* @param {AbstractSoundInstance} instance The instance to add to the channel
* @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}}
* for details on interrupt modes.
* @return {Boolean} The success of the method call. If the channel is full, it will return false.
* @static
*/
SoundChannel.add = function (instance, interrupt) {
var channel = SoundChannel.get(instance.src);
if (channel == null) {return false;}
return channel._add(instance, interrupt);
};
/**
* Remove an instance from the channel.
* #method remove
* @param {AbstractSoundInstance} instance The instance to remove from the channel
* @return The success of the method call. If there is no channel, it will return false.
* @static
*/
SoundChannel.remove = function (instance) {
var channel = SoundChannel.get(instance.src);
if (channel == null) {return false;}
channel._remove(instance);
return true;
};
/**
* Get the maximum number of sounds you can have in a channel.
* #method maxPerChannel
* @return {Number} The maximum number of sounds you can have in a channel.
*/
SoundChannel.maxPerChannel = function () {
return p.maxDefault;
};
/**
* Get a channel instance by its src.
* #method get
* @param {String} src The src to use to look up the channel
* @static
*/
SoundChannel.get = function (src) {
return SoundChannel.channels[src];
};
 
var p = SoundChannel.prototype;
p.constructor = SoundChannel;
 
/**
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
* for details.
*
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
*
* @method initialize
* @protected
* @deprecated
*/
// p.initialize = function() {}; // searchable for devs wondering where it is.
 
 
/**
* The source of the channel.
* #property src
* @type {String}
*/
p.src = null;
 
/**
* The maximum number of instances in this channel. -1 indicates no limit
* #property max
* @type {Number}
*/
p.max = null;
 
/**
* The default value to set for max, if it isn't passed in. Also used if -1 is passed.
* #property maxDefault
* @type {Number}
* @default 100
* @since 0.4.0
*/
p.maxDefault = 100;
 
/**
* The current number of active instances.
* #property length
* @type {Number}
*/
p.length = 0;
 
/**
* Initialize the channel.
* #method init
* @param {String} src The source of the channel
* @param {Number} max The maximum number of instances in the channel
* @protected
*/
p.init = function (src, max) {
this.src = src;
this.max = max || this.maxDefault;
if (this.max == -1) {this.max = this.maxDefault;}
this._instances = [];
};
 
/**
* Get an instance by index.
* #method get
* @param {Number} index The index to return.
* @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
*/
p._get = function (index) {
return this._instances[index];
};
 
/**
* Add a new instance to the channel.
* #method add
* @param {AbstractSoundInstance} instance The instance to add.
* @return {Boolean} The success of the method call. If the channel is full, it will return false.
*/
p._add = function (instance, interrupt) {
if (!this._getSlot(interrupt, instance)) {return false;}
this._instances.push(instance);
this.length++;
return true;
};
 
/**
* Remove an instance from the channel, either when it has finished playing, or it has been interrupted.
* #method remove
* @param {AbstractSoundInstance} instance The instance to remove
* @return {Boolean} The success of the remove call. If the instance is not found in this channel, it will
* return false.
*/
p._remove = function (instance) {
var index = createjs.indexOf(this._instances, instance);
if (index == -1) {return false;}
this._instances.splice(index, 1);
this.length--;
return true;
};
 
/**
* Stop playback and remove all instances from the channel. Usually in response to a delete call.
* #method removeAll
*/
p._removeAll = function () {
// Note that stop() removes the item from the list
for (var i=this.length-1; i>=0; i--) {
this._instances[i].stop();
}
};
 
/**
* Get an available slot depending on interrupt value and if slots are available.
* #method getSlot
* @param {String} interrupt The interrupt value to use.
* @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful.
* @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
* an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false.
*/
p._getSlot = function (interrupt, instance) {
var target, replacement;
 
if (interrupt != Sound.INTERRUPT_NONE) {
// First replacement candidate
replacement = this._get(0);
if (replacement == null) {
return true;
}
}
 
for (var i = 0, l = this.max; i < l; i++) {
target = this._get(i);
 
// Available Space
if (target == null) {
return true;
}
 
// Audio is complete or not playing
if (target.playState == Sound.PLAY_FINISHED ||
target.playState == Sound.PLAY_INTERRUPTED ||
target.playState == Sound.PLAY_FAILED) {
replacement = target;
break;
}
 
if (interrupt == Sound.INTERRUPT_NONE) {
continue;
}
 
// Audio is a better candidate than the current target, according to playhead
if ((interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) ||
(interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) {
replacement = target;
}
}
 
if (replacement != null) {
replacement._interrupt();
this._remove(replacement);
return true;
}
return false;
};
 
p.toString = function () {
return "[Sound SoundChannel]";
};
// do not add SoundChannel to namespace
 
}());
 
//##############################################################################
// AbstractSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
/**
* A AbstractSoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or
* {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The AbstractSoundInstance is returned by the active plugin
* for control by the user.
*
* <h4>Example</h4>
*
* var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
*
* A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound
* API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments.
*
* Once a AbstractSoundInstance is created, a reference can be stored that can be used to control the audio directly through
* the AbstractSoundInstance. If the reference is not stored, the AbstractSoundInstance will play out its audio (and any loops), and
* is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio
* playback has completed, a simple call to the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} instance method
* will rebuild the references the Sound class need to control it.
*
* var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2});
* myInstance.on("loop", handleLoop);
* function handleLoop(event) {
* myInstance.volume = myInstance.volume * 0.5;
* }
*
* Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails
*
* var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
* myInstance.on("complete", handleComplete);
* myInstance.on("loop", handleLoop);
* myInstance.on("failed", handleFailed);
*
*
* @class AbstractSoundInstance
* @param {String} src The path to and file name of the sound.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @param {Object} playbackResource Any resource needed by plugin to support audio playback.
* @extends EventDispatcher
* @constructor
*/
 
(function () {
"use strict";
 
 
// Constructor:
var AbstractSoundInstance = function (src, startTime, duration, playbackResource) {
this.EventDispatcher_constructor();
 
 
// public properties:
/**
* The source of the sound.
* @property src
* @type {String}
* @default null
*/
this.src = src;
 
/**
* The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}.
* @property uniqueId
* @type {String} | Number
* @default -1
*/
this.uniqueId = -1;
 
/**
* The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}.
* @property playState
* @type {String}
* @default null
*/
this.playState = null;
 
/**
* A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this AbstractSoundInstance is played with a delay.
* This allows AbstractSoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins.
* @property delayTimeoutId
* @type {timeoutVariable}
* @default null
* @protected
* @since 0.4.0
*/
this.delayTimeoutId = null;
// TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins
 
 
// private properties
// Getter / Setter Properties
// OJR TODO find original reason that we didn't use defined functions. I think it was performance related
/**
* The volume of the sound, between 0 and 1.
*
* The actual output volume of a sound can be calculated using:
* <code>myInstance.volume * createjs.Sound.getVolume();</code>
*
* @property volume
* @type {Number}
* @default 1
*/
this._volume = 1;
Object.defineProperty(this, "volume", {
get: this.getVolume,
set: this.setVolume
});
 
/**
* The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
*
* <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio.
*
* @property pan
* @type {Number}
* @default 0
*/
this._pan = 0;
Object.defineProperty(this, "pan", {
get: this.getPan,
set: this.setPan
});
 
/**
* Audio sprite property used to determine the starting offset.
* @property startTime
* @type {Number}
* @default 0
* @since 0.6.1
*/
this._startTime = Math.max(0, startTime || 0);
Object.defineProperty(this, "startTime", {
get: this.getStartTime,
set: this.setStartTime
});
 
/**
* Sets or gets the length of the audio clip, value is in milliseconds.
*
* @property duration
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._duration = Math.max(0, duration || 0);
Object.defineProperty(this, "duration", {
get: this.getDuration,
set: this.setDuration
});
 
/**
* Object that holds plugin specific resource need for audio playback.
* This is set internally by the plugin. For example, WebAudioPlugin will set an array buffer,
* HTMLAudioPlugin will set a tag, FlashAudioPlugin will set a flash reference.
*
* @property playbackResource
* @type {Object}
* @default null
*/
this._playbackResource = null;
Object.defineProperty(this, "playbackResource", {
get: this.getPlaybackResource,
set: this.setPlaybackResource
});
if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }
 
/**
* The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
*
* @property position
* @type {Number}
* @default 0
* @since 0.6.0
*/
this._position = 0;
Object.defineProperty(this, "position", {
get: this.getPosition,
set: this.setPosition
});
 
/**
* The number of play loops remaining. Negative values will loop infinitely.
*
* @property loop
* @type {Number}
* @default 0
* @public
* @since 0.6.0
*/
this._loop = 0;
Object.defineProperty(this, "loop", {
get: this.getLoop,
set: this.setLoop
});
 
/**
* Mutes or unmutes the current audio instance.
*
* @property muted
* @type {Boolean}
* @default false
* @since 0.6.0
*/
this._muted = false;
Object.defineProperty(this, "muted", {
get: this.getMuted,
set: this.setMuted
});
 
/**
* Pauses or resumes the current audio instance.
*
* @property paused
* @type {Boolean}
*/
this._paused = false;
Object.defineProperty(this, "paused", {
get: this.getPaused,
set: this.setPaused
});
 
 
// Events
/**
* The event that is fired when playback has started successfully.
* @event succeeded
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when playback is interrupted. This happens when another sound with the same
* src property is played using an interrupt value that causes this instance to stop playing.
* @event interrupted
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when playback has failed. This happens when there are too many channels with the same
* src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or
* the sound could not be played, perhaps due to a 404 error.
* @event failed
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when a sound has completed playing but has loops remaining.
* @event loop
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
 
/**
* The event that is fired when playback completes. This means that the sound has finished playing in its
* entirety, including its loop iterations.
* @event complete
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.4.0
*/
};
 
var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Public Methods:
/**
* Play an instance. This method is intended to be called on SoundInstances that already exist (created
* with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}).
*
* <h4>Example</h4>
*
* var myInstance = createjs.Sound.createInstance(mySrc);
* myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
*
* Note that if this sound is already playing, this call will still set the passed in parameters.
 
* <b>Parameters Deprecated</b><br />
* 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] <b>This parameter will be renamed playProps in the next release.</b><br />
* This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
* including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
* <br /><strong>OR</strong><br />
* <b>Deprecated</b> How to interrupt any currently playing instances of audio with the same source,
* if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
* constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
* @param {Number} [delay=0] <b>Deprecated</b> The amount of time to delay the start of audio playback, in milliseconds.
* @param {Number} [offset=0] <b>Deprecated</b> The offset from the start of the audio to begin playback, in milliseconds.
* @param {Number} [loop=0] <b>Deprecated</b> 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] <b>Deprecated</b> The volume of the sound, between 0 and 1. Note that the master volume is applied
* against the individual volume.
* @param {Number} [pan=0] <b>Deprecated</b> The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
* Note that pan is not supported for HTML Audio.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.play = function (interrupt, delay, offset, loop, volume, pan) {
var playProps;
if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
playProps = createjs.PlayPropsConfig.create(interrupt);
} else {
playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan});
}
 
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this.applyPlayProps(playProps);
if (this._paused) { this.setPaused(false); }
return;
}
this._cleanUp();
createjs.Sound._playInstance(this, playProps); // make this an event dispatch??
return this;
};
 
/**
* Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "AbstractSoundInstance/resume"}}{{/crossLink}}
* will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
*
* If you don't want to lose your position use yourSoundInstance.paused = true instead. {{#crossLink "AbstractSoundInstance/paused"}}{{/crossLink}}.
*
* <h4>Example</h4>
*
* myInstance.stop();
*
* @method stop
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.stop = function () {
this._position = 0;
this._paused = false;
this._handleStop();
this._cleanUp();
this.playState = createjs.Sound.PLAY_FINISHED;
return this;
};
 
/**
* Remove all external references and resources from AbstractSoundInstance. Note this is irreversible and AbstractSoundInstance will no longer work
* @method destroy
* @since 0.6.0
*/
p.destroy = function() {
this._cleanUp();
this.src = null;
this.playbackResource = null;
 
this.removeAllEventListeners();
};
 
/**
* Takes an PlayPropsConfig or Object with the same properties and sets them on this instance.
* @method applyPlayProps
* @param {PlayPropsConfig | Object} playProps A PlayPropsConfig or object containing the same properties.
* @since 0.6.1
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.applyPlayProps = function(playProps) {
if (playProps.offset != null) { this.setPosition(playProps.offset) }
if (playProps.loop != null) { this.setLoop(playProps.loop); }
if (playProps.volume != null) { this.setVolume(playProps.volume); }
if (playProps.pan != null) { this.setPan(playProps.pan); }
if (playProps.startTime != null) {
this.setStartTime(playProps.startTime);
this.setDuration(playProps.duration);
}
return this;
};
 
p.toString = function () {
return "[AbstractSoundInstance]";
};
 
// get/set methods that allow support for IE8
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property,
*
* @deprecated
* @method getPaused
* @returns {boolean} If the instance is currently paused
* @since 0.6.0
*/
p.getPaused = function() {
return this._paused;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPaused
* @param {boolean} value
* @since 0.6.0
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.setPaused = function (value) {
if ((value !== true && value !== false) || this._paused == value) {return;}
if (value == true && this.playState != createjs.Sound.PLAY_SUCCEEDED) {return;}
this._paused = value;
if(value) {
this._pause();
} else {
this._resume();
}
clearTimeout(this.delayTimeoutId);
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setVolume
* @param {Number} value The volume to set, between 0 and 1.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
*/
p.setVolume = function (value) {
if (value == this._volume) { return this; }
this._volume = Math.max(0, Math.min(1, value));
if (!this._muted) {
this._updateVolume();
}
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getVolume
* @return {Number} The current volume of the sound instance.
*/
p.getVolume = function () {
return this._volume;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setMuted
* @param {Boolean} value If the sound should be muted.
* @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
* @since 0.6.0
*/
p.setMuted = function (value) {
if (value !== true && value !== false) {return;}
this._muted = value;
this._updateVolume();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getMuted
* @return {Boolean} If the sound is muted.
* @since 0.6.0
*/
p.getMuted = function () {
return this._muted;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPan
* @param {Number} value The pan value, between -1 (left) and 1 (right).
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
p.setPan = function (value) {
if(value == this._pan) { return this; }
this._pan = Math.max(-1, Math.min(1, value));
this._updatePan();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getPan
* @return {Number} The value of the pan, between -1 (left) and 1 (right).
*/
p.getPan = function () {
return this._pan;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getPosition
* @return {Number} The position of the playhead in the sound, in milliseconds.
*/
p.getPosition = function () {
if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._position = this._calculateCurrentPosition();
}
return this._position;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPosition
* @param {Number} value The position to place the playhead, in milliseconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
p.setPosition = function (value) {
this._position = Math.max(0, value);
if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._updatePosition();
}
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getStartTime
* @return {Number} The startTime of the sound instance in milliseconds.
*/
p.getStartTime = function () {
return this._startTime;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setStartTime
* @param {number} value The new startTime time in milli seconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
*/
p.setStartTime = function (value) {
if (value == this._startTime) { return this; }
this._startTime = Math.max(0, value || 0);
this._updateStartTime();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getDuration
* @return {Number} The duration of the sound instance in milliseconds.
*/
p.getDuration = function () {
return this._duration;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setDuration
* @param {number} value The new duration time in milli seconds.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
*/
p.setDuration = function (value) {
if (value == this._duration) { return this; }
this._duration = Math.max(0, value || 0);
this._updateDuration();
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPlayback
* @param {Object} value The new playback resource.
* @return {AbstractSoundInstance} Returns reference to itself for chaining calls
* @since 0.6.0
**/
p.setPlaybackResource = function (value) {
this._playbackResource = value;
if (this._duration == 0) { this._setDurationFromSource(); }
return this;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method setPlayback
* @param {Object} value The new playback resource.
* @return {Object} playback resource used for playing audio
* @since 0.6.0
**/
p.getPlaybackResource = function () {
return this._playbackResource;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
*
* @deprecated
* @method getLoop
* @return {number}
* @since 0.6.0
**/
p.getLoop = function () {
return this._loop;
};
 
/**
* DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property,
*
* @deprecated
* @method setLoop
* @param {number} value The number of times to loop after play.
* @since 0.6.0
*/
p.setLoop = function (value) {
if(this._playbackResource != null) {
// remove looping
if (this._loop != 0 && value == 0) {
this._removeLooping(value);
}
// add looping
else if (this._loop == 0 && value != 0) {
this._addLooping(value);
}
}
this._loop = value;
};
 
 
// Private Methods:
/**
* A helper method that dispatches all events for AbstractSoundInstance.
* @method _sendEvent
* @param {String} type The event type
* @protected
*/
p._sendEvent = function (type) {
var event = new createjs.Event(type);
this.dispatchEvent(event);
};
 
/**
* Clean up the instance. Remove references and clean up any additional properties such as timers.
* @method _cleanUp
* @protected
*/
p._cleanUp = function () {
clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
this._handleCleanUp();
this._paused = false;
 
createjs.Sound._playFinished(this); // TODO change to an event
};
 
/**
* The sound has been interrupted.
* @method _interrupt
* @protected
*/
p._interrupt = function () {
this._cleanUp();
this.playState = createjs.Sound.PLAY_INTERRUPTED;
this._sendEvent("interrupted");
};
 
/**
* Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the
* src is loaded, otherwise playback will fail.
* @method _beginPlaying
* @param {PlayPropsConfig} playProps A PlayPropsConfig object.
* @return {Boolean} If playback succeeded.
* @protected
*/
// OJR FlashAudioSoundInstance overwrites
p._beginPlaying = function (playProps) {
this.setPosition(playProps.offset);
this.setLoop(playProps.loop);
this.setVolume(playProps.volume);
this.setPan(playProps.pan);
if (playProps.startTime != null) {
this.setStartTime(playProps.startTime);
this.setDuration(playProps.duration);
}
 
if (this._playbackResource != null && this._position < this._duration) {
this._paused = false;
this._handleSoundReady();
this.playState = createjs.Sound.PLAY_SUCCEEDED;
this._sendEvent("succeeded");
return true;
} else {
this._playFailed();
return false;
}
};
 
/**
* Play has failed, which can happen for a variety of reasons.
* Cleans up instance and dispatches failed event
* @method _playFailed
* @private
*/
p._playFailed = function () {
this._cleanUp();
this.playState = createjs.Sound.PLAY_FAILED;
this._sendEvent("failed");
};
 
/**
* Audio has finished playing. Manually loop it if required.
* @method _handleSoundComplete
* @param event
* @protected
*/
p._handleSoundComplete = function (event) {
this._position = 0; // have to set this as it can be set by pause during playback
 
if (this._loop != 0) {
this._loop--; // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1
this._handleLoop();
this._sendEvent("loop");
return;
}
 
this._cleanUp();
this.playState = createjs.Sound.PLAY_FINISHED;
this._sendEvent("complete");
};
 
// Plugin specific code
/**
* Handles starting playback when the sound is ready for playing.
* @method _handleSoundReady
* @protected
*/
p._handleSoundReady = function () {
// plugin specific code
};
 
/**
* Internal function used to update the volume based on the instance volume, master volume, instance mute value,
* and master mute value.
* @method _updateVolume
* @protected
*/
p._updateVolume = function () {
// plugin specific code
};
 
/**
* Internal function used to update the pan
* @method _updatePan
* @protected
* @since 0.6.0
*/
p._updatePan = function () {
// plugin specific code
};
 
/**
* Internal function used to update the startTime of the audio.
* @method _updateStartTime
* @protected
* @since 0.6.1
*/
p._updateStartTime = function () {
// plugin specific code
};
 
/**
* Internal function used to update the duration of the audio.
* @method _updateDuration
* @protected
* @since 0.6.0
*/
p._updateDuration = function () {
// plugin specific code
};
 
/**
* Internal function used to get the duration of the audio from the source we'll be playing.
* @method _updateDuration
* @protected
* @since 0.6.0
*/
p._setDurationFromSource = function () {
// plugin specific code
};
 
/**
* Internal function that calculates the current position of the playhead and sets this._position to that value
* @method _calculateCurrentPosition
* @protected
* @since 0.6.0
*/
p._calculateCurrentPosition = function () {
// plugin specific code that sets this.position
};
 
/**
* Internal function used to update the position of the playhead.
* @method _updatePosition
* @protected
* @since 0.6.0
*/
p._updatePosition = function () {
// plugin specific code
};
 
/**
* Internal function called when looping is removed during playback.
* @method _removeLooping
* @param {number} value The number of times to loop after play.
* @protected
* @since 0.6.0
*/
p._removeLooping = function (value) {
// plugin specific code
};
 
/**
* Internal function called when looping is added during playback.
* @method _addLooping
* @param {number} value The number of times to loop after play.
* @protected
* @since 0.6.0
*/
p._addLooping = function (value) {
// plugin specific code
};
 
/**
* Internal function called when pausing playback
* @method _pause
* @protected
* @since 0.6.0
*/
p._pause = function () {
// plugin specific code
};
 
/**
* Internal function called when resuming playback
* @method _resume
* @protected
* @since 0.6.0
*/
p._resume = function () {
// plugin specific code
};
 
/**
* Internal function called when stopping playback
* @method _handleStop
* @protected
* @since 0.6.0
*/
p._handleStop = function() {
// plugin specific code
};
 
/**
* Internal function called when AbstractSoundInstance is being cleaned up
* @method _handleCleanUp
* @protected
* @since 0.6.0
*/
p._handleCleanUp = function() {
// plugin specific code
};
 
/**
* Internal function called when AbstractSoundInstance has played to end and is looping
* @method _handleLoop
* @protected
* @since 0.6.0
*/
p._handleLoop = function () {
// plugin specific code
};
 
createjs.AbstractSoundInstance = createjs.promote(AbstractSoundInstance, "EventDispatcher");
createjs.DefaultSoundInstance = createjs.AbstractSoundInstance; // used when no plugin is supported
}());
 
//##############################################################################
// AbstractPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
 
// constructor:
/**
* A default plugin class used as a base for all other plugins.
* @class AbstractPlugin
* @constructor
* @since 0.6.0
*/
 
var AbstractPlugin = function () {
// private properties:
/**
* The capabilities of the plugin.
* method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
* @protected
* @static
*/
this._capabilities = null;
 
/**
* Object hash indexed by the source URI of all created loaders, used to properly destroy them if sources are removed.
* @type {Object}
* @protected
*/
this._loaders = {};
 
/**
* Object hash indexed by the source URI of each file to indicate if an audio source has begun loading,
* is currently loading, or has completed loading. Can be used to store non boolean data after loading
* is complete (for example arrayBuffers for web audio).
* @property _audioSources
* @type {Object}
* @protected
*/
this._audioSources = {};
 
/**
* Object hash indexed by the source URI of all created SoundInstances, updates the playbackResource if it loads after they are created,
* and properly destroy them if sources are removed
* @type {Object}
* @protected
*/
this._soundInstances = {};
 
/**
* The internal master volume value of the plugin.
* @property _volume
* @type {Number}
* @default 1
* @protected
*/
this._volume = 1;
 
/**
* A reference to a loader class used by a plugin that must be set.
* @type {Object}
* @protected
*/
this._loaderClass;
 
/**
* A reference to an AbstractSoundInstance class used by a plugin that must be set.
* @type {Object}
* @protected;
*/
this._soundInstanceClass;
};
var p = AbstractPlugin.prototype;
 
/**
* <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
* See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
* for details.
*
* There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
*
* @method initialize
* @protected
* @deprecated
*/
// p.initialize = function() {}; // searchable for devs wondering where it is.
 
 
// Static Properties:
// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
/**
* The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
* @protected
* @static
*/
AbstractPlugin._capabilities = null;
 
/**
* Determine if the plugin can be used in the current browser/OS.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
AbstractPlugin.isSupported = function () {
return true;
};
 
 
// public methods:
/**
* Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}.
* Note all plugins provide a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a>
* can use to assist with preloading.
* @method register
* @param {String} loadItem An Object containing the source of the audio
* Note that not every plugin will manage this value.
* @return {Object} A result object, containing a "tag" for preloading purposes.
*/
p.register = function (loadItem) {
var loader = this._loaders[loadItem.src];
if(loader && !loader.canceled) {return this._loaders[loadItem.src];} // already loading/loaded this, so don't load twice
// OJR potential issue that we won't be firing loaded event, might need to trigger if this is already loaded?
this._audioSources[loadItem.src] = true;
this._soundInstances[loadItem.src] = [];
loader = new this._loaderClass(loadItem);
loader.on("complete", this._handlePreloadComplete, this);
this._loaders[loadItem.src] = loader;
return loader;
};
 
// note sound calls register before calling preload
/**
* Internally preload a sound.
* @method preload
* @param {Loader} loader The sound URI to load.
*/
p.preload = function (loader) {
loader.on("error", this._handlePreloadError, this);
loader.load();
};
 
/**
* Checks if preloading has started for a specific source. If the source is found, we can assume it is loading,
* or has already finished loading.
* @method isPreloadStarted
* @param {String} src The sound URI to check.
* @return {Boolean}
*/
p.isPreloadStarted = function (src) {
return (this._audioSources[src] != null);
};
 
/**
* Checks if preloading has finished for a specific source.
* @method isPreloadComplete
* @param {String} src The sound URI to load.
* @return {Boolean}
*/
p.isPreloadComplete = function (src) {
return (!(this._audioSources[src] == null || this._audioSources[src] == true));
};
 
/**
* Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
* @method removeSound
* @param {String} src The sound URI to unload.
*/
p.removeSound = function (src) {
if (!this._soundInstances[src]) { return; }
for (var i = this._soundInstances[src].length; i--; ) {
var item = this._soundInstances[src][i];
item.destroy();
}
delete(this._soundInstances[src]);
delete(this._audioSources[src]);
if(this._loaders[src]) { this._loaders[src].destroy(); }
delete(this._loaders[src]);
};
 
/**
* Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
* @method removeAllSounds
* @param {String} src The sound URI to unload.
*/
p.removeAllSounds = function () {
for(var key in this._audioSources) {
this.removeSound(key);
}
};
 
/**
* Create a sound instance. If the sound has not been preloaded, it is internally preloaded here.
* @method create
* @param {String} src The sound source to use.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @return {AbstractSoundInstance} A sound instance for playback and control.
*/
p.create = function (src, startTime, duration) {
if (!this.isPreloadStarted(src)) {
this.preload(this.register(src));
}
var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
this._soundInstances[src].push(si);
return si;
};
 
// if a plugin does not support volume and mute, it should set these to null
/**
* Set the master volume of the plugin, which affects all SoundInstances.
* @method setVolume
* @param {Number} value The volume to set, between 0 and 1.
* @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the
* instances manually otherwise.
*/
p.setVolume = function (value) {
this._volume = value;
this._updateVolume();
return true;
};
 
/**
* Get the master volume of the plugin, which affects all SoundInstances.
* @method getVolume
* @return {Number} The volume level, between 0 and 1.
*/
p.getVolume = function () {
return this._volume;
};
 
/**
* Mute all sounds via the plugin.
* @method setMute
* @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up
* the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here.
* @return {Boolean} If the mute call succeeds.
*/
p.setMute = function (value) {
this._updateVolume();
return true;
};
 
// plugins should overwrite this method
p.toString = function () {
return "[AbstractPlugin]";
};
 
 
// private methods:
/**
* Handles internal preload completion.
* @method _handlePreloadComplete
* @protected
*/
p._handlePreloadComplete = function (event) {
var src = event.target.getItem().src;
this._audioSources[src] = event.result;
for (var i = 0, l = this._soundInstances[src].length; i < l; i++) {
var item = this._soundInstances[src][i];
item.setPlaybackResource(this._audioSources[src]);
// ToDo consider adding play call here if playstate == playfailed
}
};
 
/**
* Handles internal preload erros
* @method _handlePreloadError
* @param event
* @protected
*/
p._handlePreloadError = function(event) {
//delete(this._audioSources[src]);
};
 
/**
* Set the gain value for master audio. Should not be called externally.
* @method _updateVolume
* @protected
*/
p._updateVolume = function () {
// Plugin Specific code
};
 
createjs.AbstractPlugin = AbstractPlugin;
}());
 
//##############################################################################
// WebAudioLoader.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* Loader provides a mechanism to preload Web Audio content via PreloadJS or internally. Instances are returned to
* the preloader, and the load method is called when the asset needs to be requested.
*
* @class WebAudioLoader
* @param {String} loadItem The item to be loaded
* @extends XHRRequest
* @protected
*/
function Loader(loadItem) {
this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);
 
};
var p = createjs.extend(Loader, createjs.AbstractLoader);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
/**
* web audio context required for decoding audio
* @property context
* @type {AudioContext}
* @static
*/
Loader.context = null;
 
 
// public methods
p.toString = function () {
return "[WebAudioLoader]";
};
 
 
// private methods
p._createRequest = function() {
this._request = new createjs.XHRRequest(this._item, false);
this._request.setResponseType("arraybuffer");
};
 
p._sendComplete = function (event) {
// OJR we leave this wrapped in Loader because we need to reference src and the handler only receives a single argument, the decodedAudio
Loader.context.decodeAudioData(this._rawResult,
createjs.proxy(this._handleAudioDecoded, this),
createjs.proxy(this._sendError, this));
};
 
 
/**
* The audio has been decoded.
* @method handleAudioDecoded
* @param decoded
* @protected
*/
p._handleAudioDecoded = function (decodedAudio) {
this._result = decodedAudio;
this.AbstractLoader__sendComplete();
};
 
createjs.WebAudioLoader = createjs.promote(Loader, "AbstractLoader");
}());
 
//##############################################################################
// WebAudioSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
/**
* WebAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
* {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
*
* WebAudioSoundInstance exposes audioNodes for advanced users.
*
* @param {String} src The path to and file name of the sound.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @param {Object} playbackResource Any resource needed by plugin to support audio playback.
* @class WebAudioSoundInstance
* @extends AbstractSoundInstance
* @constructor
*/
(function () {
"use strict";
 
function WebAudioSoundInstance(src, startTime, duration, playbackResource) {
this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
 
 
// public properties
/**
* NOTE this is only intended for use by advanced users.
* <br />GainNode for controlling <code>WebAudioSoundInstance</code> volume. Connected to the {{#crossLink "WebAudioSoundInstance/destinationNode:property"}}{{/crossLink}}.
* @property gainNode
* @type {AudioGainNode}
* @since 0.4.0
*
*/
this.gainNode = s.context.createGain();
 
/**
* NOTE this is only intended for use by advanced users.
* <br />A panNode allowing left and right audio channel panning only. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
* @property panNode
* @type {AudioPannerNode}
* @since 0.4.0
*/
this.panNode = s.context.createPanner();
this.panNode.panningModel = s._panningModel;
this.panNode.connect(this.gainNode);
this._updatePan();
 
/**
* NOTE this is only intended for use by advanced users.
* <br />sourceNode is the audio source. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/panNode:property"}}{{/crossLink}}.
* @property sourceNode
* @type {AudioNode}
* @since 0.4.0
*
*/
this.sourceNode = null;
 
 
// private properties
/**
* Timeout that is created internally to handle sound playing to completion.
* Stored so we can remove it when stop, pause, or cleanup are called
* @property _soundCompleteTimeout
* @type {timeoutVariable}
* @default null
* @protected
* @since 0.4.0
*/
this._soundCompleteTimeout = null;
 
/**
* NOTE this is only intended for use by very advanced users.
* _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth
* looping. Connected to {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
* @property _sourceNodeNext
* @type {AudioNode}
* @default null
* @protected
* @since 0.4.1
*
*/
this._sourceNodeNext = null;
 
/**
* Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
* @property _playbackStartTime
* @type {Number}
* @default 0
* @protected
* @since 0.4.0
*/
this._playbackStartTime = 0;
 
// Proxies, make removing listeners easier.
this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
};
var p = createjs.extend(WebAudioSoundInstance, createjs.AbstractSoundInstance);
var s = WebAudioSoundInstance;
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
/**
* Note this is only intended for use by advanced users.
* <br />Audio context used to create nodes. This is and needs to be the same context used by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
* @property context
* @type {AudioContext}
* @static
* @since 0.6.0
*/
s.context = null;
 
/**
* Note this is only intended for use by advanced users.
* <br />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.
* <br /> Audio node from WebAudioPlugin that sequences to <code>context.destination</code>
* @property destinationNode
* @type {AudioNode}
* @static
* @since 0.6.0
*/
s.destinationNode = null;
 
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
* @type {Number / String}
* @protected
* @static
* @since 0.6.0
*/
s._panningModel = "equalpower";
 
 
// Public methods
p.destroy = function() {
this.AbstractSoundInstance_destroy();
 
this.panNode.disconnect(0);
this.panNode = null;
this.gainNode.disconnect(0);
this.gainNode = null;
};
 
p.toString = function () {
return "[WebAudioSoundInstance]";
};
 
 
// Private Methods
p._updatePan = function() {
this.panNode.setPosition(this._pan, 0, -0.5);
// z need to be -0.5 otherwise the sound only plays in left, right, or center
};
 
p._removeLooping = function(value) {
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
};
 
p._addLooping = function(value) {
if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
};
 
p._setDurationFromSource = function () {
this._duration = this.playbackResource.duration * 1000;
};
 
p._handleCleanUp = function () {
if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
}
 
if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
// OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work.
 
clearTimeout(this._soundCompleteTimeout);
 
this._playbackStartTime = 0; // This is used by getPosition
};
 
/**
* Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection
* @method _cleanUpAudioNode
* @param audioNode
* @return {audioNode}
* @protected
* @since 0.4.1
*/
p._cleanUpAudioNode = function(audioNode) {
if(audioNode) {
audioNode.stop(0);
audioNode.disconnect(0);
// necessary to prevent leak on iOS Safari 7-9. will throw in almost all other
// browser implementations.
try { audioNode.buffer = s._scratchBuffer; } catch(e) {}
audioNode = null;
}
return audioNode;
};
 
p._handleSoundReady = function (event) {
this.gainNode.connect(s.destinationNode); // this line can cause a memory leak. Nodes need to be disconnected from the audioDestination or any sequence that leads to it.
 
var dur = this._duration * 0.001;
var pos = this._position * 0.001;
if (pos > dur) {pos = dur;}
this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
this._playbackStartTime = this.sourceNode.startTime - pos;
 
this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);
 
if(this._loop != 0) {
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
}
};
 
/**
* Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
* @method _createAndPlayAudioNode
* @param {Number} startTime The time to add this to the web audio context, in seconds.
* @param {Number} offset The amount of time into the src audio to start playback, in seconds.
* @return {audioNode}
* @protected
* @since 0.4.1
*/
p._createAndPlayAudioNode = function(startTime, offset) {
var audioNode = s.context.createBufferSource();
audioNode.buffer = this.playbackResource;
audioNode.connect(this.panNode);
var dur = this._duration * 0.001;
audioNode.startTime = startTime + dur;
audioNode.start(audioNode.startTime, offset+(this._startTime*0.001), dur - offset);
return audioNode;
};
 
p._pause = function () {
this._position = (s.context.currentTime - this._playbackStartTime) * 1000; // * 1000 to give milliseconds, lets us restart at same point
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
 
if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
 
clearTimeout(this._soundCompleteTimeout);
};
 
p._resume = function () {
this._handleSoundReady();
};
 
/*
p._handleStop = function () {
// web audio does not need to do anything extra
};
*/
 
p._updateVolume = function () {
var newVolume = this._muted ? 0 : this._volume;
if (newVolume != this.gainNode.gain.value) {
this.gainNode.gain.value = newVolume;
}
};
 
p._calculateCurrentPosition = function () {
return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
};
 
p._updatePosition = function () {
this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
clearTimeout(this._soundCompleteTimeout);
 
if (!this._paused) {this._handleSoundReady();}
};
 
// OJR we are using a look ahead approach to ensure smooth looping.
// We add _sourceNodeNext to the audio context so that it starts playing even if this callback is delayed.
// This technique is described here: http://www.html5rocks.com/en/tutorials/audio/scheduling/
// NOTE the cost of this is that our audio loop may not always match the loop event timing precisely.
p._handleLoop = function () {
this._cleanUpAudioNode(this.sourceNode);
this.sourceNode = this._sourceNodeNext;
this._playbackStartTime = this.sourceNode.startTime;
this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
};
 
p._updateDuration = function () {
if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._pause();
this._resume();
}
};
 
createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
}());
 
//##############################################################################
// WebAudioPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
"use strict";
 
/**
* Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used
* anywhere that it is supported. To change plugin priority, check out the Sound API
* {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.
 
* <h4>Known Browser and OS issues for Web Audio</h4>
* <b>Firefox 25</b>
* <li>
* mp3 audio files do not load properly on all windows machines, reported <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>.
* <br />For this reason it is recommended to pass another FireFox-supported type (i.e. ogg) as the default
* extension, until this bug is resolved
* </li>
*
* <b>Webkit (Chrome and Safari)</b>
* <li>
* 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.
* </li>
*
* <b>iOS 6 limitations</b>
* <ul>
* <li>
* Sound is initially muted and will only unmute through play being called inside a user initiated event
* (touch/click). 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.
* </li>
* <li>
* 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.
* </li>
* </ul>
* @class WebAudioPlugin
* @extends AbstractPlugin
* @constructor
* @since 0.4.0
*/
function WebAudioPlugin() {
this.AbstractPlugin_constructor();
 
 
// Private Properties
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
* @type {Number / String}
* @protected
*/
this._panningModel = s._panningModel;;
 
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
* @property context
* @type {AudioContext}
*/
this.context = s.context;
 
/**
* A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
* It is connected to <code>context.destination</code>.
*
* Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode.
* @property dynamicsCompressorNode
* @type {AudioNode}
*/
this.dynamicsCompressorNode = this.context.createDynamicsCompressor();
this.dynamicsCompressorNode.connect(this.context.destination);
 
/**
* A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}.
*
* Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode.
* @property gainNode
* @type {AudioGainNode}
*/
this.gainNode = this.context.createGain();
this.gainNode.connect(this.dynamicsCompressorNode);
createjs.WebAudioSoundInstance.destinationNode = this.gainNode;
 
this._capabilities = s._capabilities;
 
this._loaderClass = createjs.WebAudioLoader;
this._soundInstanceClass = createjs.WebAudioSoundInstance;
 
this._addPropsToClasses();
}
var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Static Properties
var s = WebAudioPlugin;
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
* method and is used internally.
* @property _capabilities
* @type {Object}
* @default null
* @protected
* @static
*/
s._capabilities = null;
 
/**
* Value to set panning model to equal power for WebAudioSoundInstance. Can be "equalpower" or 0 depending on browser implementation.
* @property _panningModel
* @type {Number / String}
* @protected
* @static
*/
s._panningModel = "equalpower";
 
/**
* The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
* need to be created within this context.
*
* Advanced users can set this to an existing context, but <b>must</b> do so before they call
* {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
*
* @property context
* @type {AudioContext}
* @static
*/
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 <b>must</b> do so before they call
* {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
*
* @property _scratchBuffer
* @type {AudioBuffer}
* @protected
* @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;
 
 
// Static Public Methods
/**
* Determine if the plugin can be used in the current browser/OS.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
// check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file
var isMobilePhoneGap = createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry;
// OJR isMobile may be redundant with _isFileXHRSupported available. Consider removing.
if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; } // Web Audio requires XHR, which is not usually available locally
s._generateCapabilities();
if (s.context == null) {return false;}
return true;
};
 
/**
* Plays an empty sound in the web audio context. This is used to enable web audio on iOS devices, as they
* require the first sound to be played inside of a user initiated event (touch/click). This is called when
* {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}
* for example).
*
* <h4>Example</h4>
*
* function handleTouch(event) {
* createjs.WebAudioPlugin.playEmptySound();
* }
*
* @method playEmptySound
* @static
* @since 0.4.1
*/
s.playEmptySound = function() {
if (s.context == null) {return;}
var source = s.context.createBufferSource();
source.buffer = s._scratchBuffer;
source.connect(s.context.destination);
source.start(0, 0, 0);
};
 
 
// Static Private Methods
/**
* Determine if XHR is supported, which is necessary for web audio.
* @method _isFileXHRSupported
* @return {Boolean} If XHR is supported.
* @since 0.4.2
* @protected
* @static
*/
s._isFileXHRSupported = function() {
// it's much easier to detect when something goes wrong, so let's start optimistically
var supported = true;
 
var xhr = new XMLHttpRequest();
try {
xhr.open("GET", "WebAudioPluginTest.fail", false); // loading non-existant file triggers 404 only if it could load (synchronous call)
} catch (error) {
// catch errors in cases where the onerror is passed by
supported = false;
return supported;
}
xhr.onerror = function() { supported = false; }; // cause irrelevant
// with security turned off, we can get empty success results, which is actually a failed read (status code 0?)
xhr.onload = function() { supported = this.status == 404 || (this.status == 200 || (this.status == 0 && this.response != "")); };
try {
xhr.send();
} catch (error) {
// catch errors in cases where the onerror is passed by
supported = false;
}
 
return supported;
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
// Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section
var t = document.createElement("audio");
if (t.canPlayType == null) {return null;}
 
if (s.context == null) {
if (window.AudioContext) {
s.context = new AudioContext();
} else if (window.webkitAudioContext) {
s.context = new webkitAudioContext();
} else {
return null;
}
}
if (s._scratchBuffer == null) {
s._scratchBuffer = s.context.createBuffer(1, 1, 22050);
}
 
s._compatibilitySetUp();
 
// 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("touchend", s._unlock, true);
}
 
 
s._capabilities = {
panning:true,
volume:true,
tracks:-1
};
 
// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
var extensionMap = createjs.Sound.EXTENSION_MAP;
for (var i = 0, l = supportedExtensions.length; i < l; i++) {
var ext = supportedExtensions[i];
var playType = extensionMap[ext] || ext;
s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
} // OJR another way to do this might be canPlayType:"m4a", codex: mp4
 
// 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround.
// See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels.
if (s.context.destination.numberOfChannels < 2) {
s._capabilities.panning = false;
}
};
 
/**
* Set up compatibility if only deprecated web audio calls are supported.
* See http://www.w3.org/TR/webaudio/#DeprecationNotes
* Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that
* don't support new calls.
*
* @method _compatibilitySetUp
* @static
* @protected
* @since 0.4.2
*/
s._compatibilitySetUp = function() {
s._panningModel = "equalpower";
//assume that if one new call is supported, they all are
if (s.context.createGain) { return; }
 
// simple name change, functionality the same
s.context.createGain = s.context.createGainNode;
 
// source node, add to prototype
var audioNode = s.context.createBufferSource();
audioNode.__proto__.start = audioNode.__proto__.noteGrainOn; // note that noteGrainOn requires all 3 parameters
audioNode.__proto__.stop = audioNode.__proto__.noteOff;
 
// panningModel
s._panningModel = 0;
};
 
/**
* 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);
s._unlocked = true;
}
};
 
 
// Public Methods
p.toString = function () {
return "[WebAudioPlugin]";
};
 
 
// Private Methods
/**
* Set up needed properties on supported classes WebAudioSoundInstance and WebAudioLoader.
* @method _addPropsToClasses
* @static
* @protected
* @since 0.6.0
*/
p._addPropsToClasses = function() {
var c = this._soundInstanceClass;
c.context = this.context;
c._scratchBuffer = s._scratchBuffer;
c.destinationNode = this.gainNode;
c._panningModel = this._panningModel;
 
this._loaderClass.context = this.context;
};
 
 
/**
* Set the gain value for master audio. Should not be called externally.
* @method _updateVolume
* @protected
*/
p._updateVolume = function () {
var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
if (newVolume != this.gainNode.gain.value) {
this.gainNode.gain.value = newVolume;
}
};
 
createjs.WebAudioPlugin = createjs.promote(WebAudioPlugin, "AbstractPlugin");
}());
 
//##############################################################################
// HTMLAudioTagPool.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
* @class HTMLAudioTagPool
* @param {String} src The source of the channel.
* @protected
*/
function HTMLAudioTagPool() {
throw "HTMLAudioTagPool cannot be instantiated";
}
 
var s = HTMLAudioTagPool;
 
// Static Properties
/**
* A hash lookup of each base audio tag, indexed by the audio source.
* @property _tags
* @type {{}}
* @static
* @protected
*/
s._tags = {};
 
/**
* An object pool for html audio tags
* @property _tagPool
* @type {TagPool}
* @static
* @protected
*/
s._tagPool = new TagPool();
 
/**
* A hash lookup of if a base audio tag is available, indexed by the audio source
* @property _tagsUsed
* @type {{}}
* @protected
* @static
*/
s._tagUsed = {};
 
// Static Methods
/**
* Get an audio tag with the given source.
* @method get
* @param {String} src The source file used by the audio tag.
* @static
*/
s.get = function (src) {
var t = s._tags[src];
if (t == null) {
// create new base tag
t = s._tags[src] = s._tagPool.get();
t.src = src;
} else {
// get base or pool
if (s._tagUsed[src]) {
t = s._tagPool.get();
t.src = src;
} else {
s._tagUsed[src] = true;
}
}
return t;
};
 
/**
* Return an audio tag to the pool.
* @method set
* @param {String} src The source file used by the audio tag.
* @param {HTMLElement} tag Audio tag to set.
* @static
*/
s.set = function (src, tag) {
// check if this is base, if yes set boolean if not return to pool
if(tag == s._tags[src]) {
s._tagUsed[src] = false;
} else {
s._tagPool.set(tag);
}
};
 
/**
* Delete stored tag reference and return them to pool. Note that if the tag reference does not exist, this will fail.
* @method remove
* @param {String} src The source for the tag
* @return {Boolean} If the TagPool was deleted.
* @static
*/
s.remove = function (src) {
var tag = s._tags[src];
if (tag == null) {return false;}
s._tagPool.set(tag);
delete(s._tags[src]);
delete(s._tagUsed[src]);
return true;
};
 
/**
* Gets the duration of the src audio in milliseconds
* @method getDuration
* @param {String} src The source file used by the audio tag.
* @return {Number} Duration of src in milliseconds
* @static
*/
s.getDuration= function (src) {
var t = s._tags[src];
if (t == null || !t.duration) {return 0;} // OJR duration is NaN if loading has not completed
return t.duration * 1000;
};
 
createjs.HTMLAudioTagPool = HTMLAudioTagPool;
 
 
// ************************************************************************************************************
/**
* The TagPool is an object pool for HTMLAudio tag instances.
* #class TagPool
* @param {String} src The source of the channel.
* @protected
*/
function TagPool(src) {
 
// Public Properties
/**
* A list of all available tags in the pool.
* #property tags
* @type {Array}
* @protected
*/
this._tags = [];
};
 
var p = TagPool.prototype;
p.constructor = TagPool;
 
 
// Public Methods
/**
* Get an HTMLAudioElement for immediate playback. This takes it out of the pool.
* #method get
* @return {HTMLAudioElement} An HTML audio tag.
*/
p.get = function () {
var tag;
if (this._tags.length == 0) {
tag = this._createTag();
} else {
tag = this._tags.pop();
}
if (tag.parentNode == null) {document.body.appendChild(tag);}
return tag;
};
 
/**
* Put an HTMLAudioElement back in the pool for use.
* #method set
* @param {HTMLAudioElement} tag HTML audio tag
*/
p.set = function (tag) {
// OJR this first step seems unnecessary
var index = createjs.indexOf(this._tags, tag);
if (index == -1) {
this._tags.src = null;
this._tags.push(tag);
}
};
 
p.toString = function () {
return "[TagPool]";
};
 
 
// Private Methods
/**
* Create an HTML audio tag.
* #method _createTag
* @param {String} src The source file to set for the audio tag.
* @return {HTMLElement} Returns an HTML audio tag.
* @protected
*/
p._createTag = function () {
var tag = document.createElement("audio");
tag.autoplay = false;
tag.preload = "none";
//LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
return tag;
};
 
}());
 
//##############################################################################
// HTMLAudioSoundInstance.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
"use strict";
 
/**
* HTMLAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
* {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
*
* @param {String} src The path to and file name of the sound.
* @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
* @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
* @param {Object} playbackResource Any resource needed by plugin to support audio playback.
* @class HTMLAudioSoundInstance
* @extends AbstractSoundInstance
* @constructor
*/
function HTMLAudioSoundInstance(src, startTime, duration, playbackResource) {
this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);
 
 
// Private Properties
this._audioSpriteStopTime = null;
this._delayTimeoutId = null;
 
// Proxies, make removing listeners easier.
this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
this._readyHandler = createjs.proxy(this._handleTagReady, this);
this._stalledHandler = createjs.proxy(this._playFailed, this);
this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
this._loopHandler = createjs.proxy(this._handleSoundComplete, this);
 
if (duration) {
this._audioSpriteStopTime = (startTime + duration) * 0.001;
} else {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
}
}
var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Public Methods
/**
* Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
* undoc'd because it is not meant to be used outside of Sound
* #method setMasterVolume
* @param value
*/
p.setMasterVolume = function (value) {
this._updateVolume();
};
 
/**
* Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master mute.
* undoc'd because it is not meant to be used outside of Sound
* #method setMasterMute
* @param value
*/
p.setMasterMute = function (isMuted) {
this._updateVolume();
};
 
p.toString = function () {
return "[HTMLAudioSoundInstance]";
};
 
//Private Methods
p._removeLooping = function() {
if(this._playbackResource == null) {return;}
this._playbackResource.loop = false;
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
};
 
p._addLooping = function() {
if(this._playbackResource == null || this._audioSpriteStopTime) {return;}
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
this._playbackResource.loop = true;
};
 
p._handleCleanUp = function () {
var tag = this._playbackResource;
if (tag != null) {
tag.pause();
tag.loop = false;
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
 
try {
tag.currentTime = this._startTime;
} catch (e) {
} // Reset Position
createjs.HTMLAudioTagPool.set(this.src, tag);
this._playbackResource = null;
}
};
 
p._beginPlaying = function (playProps) {
this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
return this.AbstractSoundInstance__beginPlaying(playProps);
};
 
p._handleSoundReady = function (event) {
if (this._playbackResource.readyState !== 4) {
var tag = this._playbackResource;
tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set.
tag.load();
return;
}
 
this._updateVolume();
this._playbackResource.currentTime = (this._startTime + this._position) * 0.001;
if (this._audioSpriteStopTime) {
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
} else {
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
if(this._loop != 0) {
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
this._playbackResource.loop = true;
}
}
 
this._playbackResource.play();
};
 
/**
* Used to handle when a tag is not ready for immediate playback when it is returned from the HTMLAudioTagPool.
* @method _handleTagReady
* @param event
* @protected
*/
p._handleTagReady = function (event) {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
 
this._handleSoundReady();
};
 
p._pause = function () {
this._playbackResource.pause();
};
 
p._resume = function () {
this._playbackResource.play();
};
 
p._updateVolume = function () {
if (this._playbackResource != null) {
var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
if (newVolume != this._playbackResource.volume) {this._playbackResource.volume = newVolume;}
}
};
 
p._calculateCurrentPosition = function() {
return (this._playbackResource.currentTime * 1000) - this._startTime;
};
 
p._updatePosition = function() {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
try {
this._playbackResource.currentTime = (this._position + this._startTime) * 0.001;
} catch (error) { // Out of range
this._handleSetPositionSeek(null);
}
};
 
/**
* Used to enable setting position, as we need to wait for that seek to be done before we add back our loop handling seek listener
* @method _handleSetPositionSeek
* @param event
* @protected
*/
p._handleSetPositionSeek = function(event) {
if (this._playbackResource == null) { return; }
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
};
 
/**
* Timer used to loop audio sprites.
* NOTE because of the inaccuracies in the timeupdate event (15 - 250ms) and in setting the tag to the desired timed
* (up to 300ms), it is strongly recommended not to loop audio sprites with HTML Audio if smooth looping is desired
*
* @method _handleAudioSpriteLoop
* @param event
* @private
*/
p._handleAudioSpriteLoop = function (event) {
if(this._playbackResource.currentTime <= this._audioSpriteStopTime) {return;}
this._playbackResource.pause();
if(this._loop == 0) {
this._handleSoundComplete(null);
} else {
this._position = 0;
this._loop--;
this._playbackResource.currentTime = this._startTime * 0.001;
if(!this._paused) {this._playbackResource.play();}
this._sendEvent("loop");
}
};
 
// NOTE with this approach audio will loop as reliably as the browser allows
// but we could end up sending the loop event after next loop playback begins
p._handleLoop = function (event) {
if(this._loop == 0) {
this._playbackResource.loop = false;
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
}
};
 
p._updateStartTime = function () {
this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
 
if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
}
};
 
p._updateDuration = function () {
this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;
 
if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
}
};
 
p._setDurationFromSource = function () {
this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
this._playbackResource = null;
};
 
createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());
 
//##############################################################################
// HTMLAudioPlugin.js
//##############################################################################
 
this.createjs = this.createjs || {};
 
(function () {
 
"use strict";
 
/**
* Play sounds using HTML &lt;audio&gt; tags in the browser. This plugin is the second priority plugin installed
* by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}. For older browsers that do not support html
* audio, include and install the {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
*
* <h4>Known Browser and OS issues for HTML Audio</h4>
* <b>All browsers</b><br />
* Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed. If you exceed
* this limit, you can expect to see unpredictable results. 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.
*
* <b>IE html limitations</b><br />
* <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
* muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
* when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
* <li>MP3 encoding will not always work for audio tags if it's not default. We've found default encoding with
* 64kbps works.</li>
* <li>Occasionally very short samples will get cut off.</li>
* <li>There is a limit to how many audio tags you can load 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.</li></ul>
*
* <b>Safari limitations</b><br />
* <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
*
* <b>iOS 6 limitations</b><br />
* <ul><li>can only have one &lt;audio&gt; tag</li>
* <li>can not preload or autoplay the audio</li>
* <li>can not cache the audio</li>
* <li>can not play the audio except inside a user initiated event.</li>
* <li>Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)</li>
* <li>audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS</li>
* </ul>
*
* <b>Android Native Browser limitations</b><br />
* <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
* <li>We can only play audio inside a user event (touch/click). This currently means you cannot loop sound or use a delay.</li></ul>
* <b> Android Chrome 26.0.1410.58 specific limitations</b><br />
* <ul> <li>Can only play 1 sound at a time.</li>
* <li>Sound is not cached.</li>
* <li>Sound can only be loaded in a user initiated touch/click event.</li>
* <li>There is a delay before a sound is played, presumably while the src is loaded.</li>
* </ul>
*
* See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues.
*
* @class HTMLAudioPlugin
* @extends AbstractPlugin
* @constructor
*/
function HTMLAudioPlugin() {
this.AbstractPlugin_constructor();
 
 
// Public Properties
/**
* This is no longer needed as we are now using object pooling for tags.
*
* <b>NOTE this property only exists as a limitation of HTML audio.</b>
* @property defaultNumChannels
* @type {Number}
* @default 2
* @since 0.4.0
* @deprecated
*/
this.defaultNumChannels = 2;
 
this._capabilities = s._capabilities;
 
this._loaderClass = createjs.SoundLoader;
this._soundInstanceClass = createjs.HTMLAudioSoundInstance;
}
 
var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
var s = HTMLAudioPlugin;
 
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
 
 
// Static Properties
/**
* The maximum number of instances that can be loaded or played. This is a browser limitation, primarily limited to IE9.
* The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate.
* Audio sprites work around this limitation.
* @property MAX_INSTANCES
* @type {Number}
* @default 30
* @static
*/
s.MAX_INSTANCES = 30;
 
/**
* Event constant for the "canPlayThrough" event for cleaner code.
* @property _AUDIO_READY
* @type {String}
* @default canplaythrough
* @static
* @protected
*/
s._AUDIO_READY = "canplaythrough";
 
/**
* Event constant for the "ended" event for cleaner code.
* @property _AUDIO_ENDED
* @type {String}
* @default ended
* @static
* @protected
*/
s._AUDIO_ENDED = "ended";
 
/**
* Event constant for the "seeked" event for cleaner code. We utilize this event for maintaining loop events.
* @property _AUDIO_SEEKED
* @type {String}
* @default seeked
* @static
* @protected
*/
s._AUDIO_SEEKED = "seeked";
 
/**
* Event constant for the "stalled" event for cleaner code.
* @property _AUDIO_STALLED
* @type {String}
* @default stalled
* @static
* @protected
*/
s._AUDIO_STALLED = "stalled";
 
/**
* Event constant for the "timeupdate" event for cleaner code. Utilized for looping audio sprites.
* This event callsback ever 15 to 250ms and can be dropped by the browser for performance.
* @property _TIME_UPDATE
* @type {String}
* @default timeupdate
* @static
* @protected
*/
s._TIME_UPDATE = "timeupdate";
 
/**
* The capabilities of the plugin. This is generated via the {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}}
* method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all
* of the available properties.
* @property _capabilities
* @type {Object}
* @protected
* @static
*/
s._capabilities = null;
 
 
// Static Methods
/**
* Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
* browsers, but is disabled in iOS because of its limitations.
* @method isSupported
* @return {Boolean} If the plugin can be initialized.
* @static
*/
s.isSupported = function () {
s._generateCapabilities();
return (s._capabilities != null);
};
 
/**
* Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
* method for an overview of plugin capabilities.
* @method _generateCapabilities
* @static
* @protected
*/
s._generateCapabilities = function () {
if (s._capabilities != null) {return;}
var t = document.createElement("audio");
if (t.canPlayType == null) {return null;}
 
s._capabilities = {
panning:false,
volume:true,
tracks:-1
};
 
// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
var extensionMap = createjs.Sound.EXTENSION_MAP;
for (var i = 0, l = supportedExtensions.length; i < l; i++) {
var ext = supportedExtensions[i];
var playType = extensionMap[ext] || ext;
s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
} // OJR another way to do this might be canPlayType:"m4a", codex: mp4
};
 
 
// public methods
p.register = function (loadItem) {
var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
var loader = this.AbstractPlugin_register(loadItem);
loader.setTag(tag);
 
return loader;
};
 
p.removeSound = function (src) {
this.AbstractPlugin_removeSound(src);
createjs.HTMLAudioTagPool.remove(src);
};
 
p.create = function (src, startTime, duration) {
var si = this.AbstractPlugin_create(src, startTime, duration);
si.setPlaybackResource(null);
return si;
};
 
p.toString = function () {
return "[HTMLAudioPlugin]";
};
 
// plugin does not support these
p.setVolume = p.getVolume = p.setMute = null;
 
 
createjs.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
}());
/bower_components/SoundJS/lib/soundjs-NEXT.min.js
@@ -0,0 +1,18 @@
/*!
* @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="NEXT",a.buildDate="Fri, 04 Dec 2015 17:24:04 GMT"}(),this.createjs=this.createjs||{},createjs.extend=function(a,b){"use strict";function c(){this.constructor=a}return c.prototype=b.prototype,a.prototype=new c},this.createjs=this.createjs||{},createjs.promote=function(a,b){"use strict";var c=a.prototype,d=Object.getPrototypeOf&&Object.getPrototypeOf(c)||c.__proto__;if(d){c[(b+="_")+"constructor"]=d.constructor;for(var e in d)c.hasOwnProperty(e)&&"function"==typeof d[e]&&(c[b+e]=d[e])}return a},this.createjs=this.createjs||{},createjs.indexOf=function(a,b){"use strict";for(var c=0,d=a.length;d>c;c++)if(b===a[c])return c;return-1},this.createjs=this.createjs||{},function(){"use strict";createjs.proxy=function(a,b){var c=Array.prototype.slice.call(arguments,2);return function(){return a.apply(b,Array.prototype.slice.call(arguments,0).concat(c))}}}(),this.createjs=this.createjs||{},function(){"use strict";function BrowserDetect(){throw"BrowserDetect cannot be instantiated"}var a=BrowserDetect.agent=window.navigator.userAgent;BrowserDetect.isWindowPhone=a.indexOf("IEMobile")>-1||a.indexOf("Windows Phone")>-1,BrowserDetect.isFirefox=a.indexOf("Firefox")>-1,BrowserDetect.isOpera=null!=window.opera,BrowserDetect.isChrome=a.indexOf("Chrome")>-1,BrowserDetect.isIOS=(a.indexOf("iPod")>-1||a.indexOf("iPhone")>-1||a.indexOf("iPad")>-1)&&!BrowserDetect.isWindowPhone,BrowserDetect.isAndroid=a.indexOf("Android")>-1&&!BrowserDetect.isWindowPhone,BrowserDetect.isBlackberry=a.indexOf("Blackberry")>-1,createjs.BrowserDetect=BrowserDetect}(),this.createjs=this.createjs||{},function(){"use strict";function EventDispatcher(){this._listeners=null,this._captureListeners=null}var a=EventDispatcher.prototype;EventDispatcher.initialize=function(b){b.addEventListener=a.addEventListener,b.on=a.on,b.removeEventListener=b.off=a.removeEventListener,b.removeAllEventListeners=a.removeAllEventListeners,b.hasEventListener=a.hasEventListener,b.dispatchEvent=a.dispatchEvent,b._dispatchEvent=a._dispatchEvent,b.willTrigger=a.willTrigger},a.addEventListener=function(a,b,c){var d;d=c?this._captureListeners=this._captureListeners||{}:this._listeners=this._listeners||{};var e=d[a];return e&&this.removeEventListener(a,b,c),e=d[a],e?e.push(b):d[a]=[b],b},a.on=function(a,b,c,d,e,f){return b.handleEvent&&(c=c||b,b=b.handleEvent),c=c||this,this.addEventListener(a,function(a){b.call(c,a,e),d&&a.remove()},f)},a.removeEventListener=function(a,b,c){var d=c?this._captureListeners:this._listeners;if(d){var e=d[a];if(e)for(var f=0,g=e.length;g>f;f++)if(e[f]==b){1==g?delete d[a]:e.splice(f,1);break}}},a.off=a.removeEventListener,a.removeAllEventListeners=function(a){a?(this._listeners&&delete this._listeners[a],this._captureListeners&&delete this._captureListeners[a]):this._listeners=this._captureListeners=null},a.dispatchEvent=function(a,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=1==b?this._captureListeners:this._listeners;if(a&&d){var e=d[a.type];if(!e||!(c=e.length))return;try{a.currentTarget=this}catch(f){}try{a.eventPhase=b}catch(f){}a.removed=!1,e=e.slice();for(var g=0;c>g&&!a.immediatePropagationStopped;g++){var h=e[g];h.handleEvent?h.handleEvent(a):h(a),a.removed&&(this.off(a.type,h,1==b),a.removed=!1)}}},createjs.EventDispatcher=EventDispatcher}(),this.createjs=this.createjs||{},function(){"use strict";function Event(a,b,c){this.type=a,this.target=null,this.currentTarget=null,this.eventPhase=0,this.bubbles=!!b,this.cancelable=!!c,this.timeStamp=(new Date).getTime(),this.defaultPrevented=!1,this.propagationStopped=!1,this.immediatePropagationStopped=!1,this.removed=!1}var a=Event.prototype;a.preventDefault=function(){this.defaultPrevented=this.cancelable&&!0},a.stopPropagation=function(){this.propagationStopped=!0},a.stopImmediatePropagation=function(){this.immediatePropagationStopped=this.propagationStopped=!0},a.remove=function(){this.removed=!0},a.clone=function(){return new Event(this.type,this.bubbles,this.cancelable)},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[Event (type="+this.type+")]"},createjs.Event=Event}(),this.createjs=this.createjs||{},function(){"use strict";function ErrorEvent(a,b,c){this.Event_constructor("error"),this.title=a,this.message=b,this.data=c}var a=createjs.extend(ErrorEvent,createjs.Event);a.clone=function(){return new createjs.ErrorEvent(this.title,this.message,this.data)},createjs.ErrorEvent=createjs.promote(ErrorEvent,"Event")}(),this.createjs=this.createjs||{},function(){"use strict";function ProgressEvent(a,b){this.Event_constructor("progress"),this.loaded=a,this.total=null==b?1:b,this.progress=0==b?0:this.loaded/this.total}var a=createjs.extend(ProgressEvent,createjs.Event);a.clone=function(){return new createjs.ProgressEvent(this.loaded,this.total)},createjs.ProgressEvent=createjs.promote(ProgressEvent,"Event")}(window),this.createjs=this.createjs||{},function(){"use strict";function LoadItem(){this.src=null,this.type=null,this.id=null,this.maintainOrder=!1,this.callback=null,this.data=null,this.method=createjs.LoadItem.GET,this.values=null,this.headers=null,this.withCredentials=!1,this.mimeType=null,this.crossOrigin=null,this.loadTimeout=b.LOAD_TIMEOUT_DEFAULT}var a=LoadItem.prototype={},b=LoadItem;b.LOAD_TIMEOUT_DEFAULT=8e3,b.create=function(a){if("string"==typeof a){var c=new LoadItem;return c.src=a,c}if(a instanceof b)return a;if(a instanceof Object&&a.src)return null==a.loadTimeout&&(a.loadTimeout=b.LOAD_TIMEOUT_DEFAULT),a;throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},createjs.LoadItem=b}(),function(){var a={};a.ABSOLUTE_PATT=/^(?:\w+:)?\/{2}/i,a.RELATIVE_PATT=/^[.\/]*?\//i,a.EXTENSION_PATT=/\/?[^\/]+\.(\w{1,5})$/i,a.parseURI=function(b){var c={absolute:!1,relative:!1};if(null==b)return c;var d=b.indexOf("?");d>-1&&(b=b.substr(0,d));var e;return a.ABSOLUTE_PATT.test(b)?c.absolute=!0:a.RELATIVE_PATT.test(b)&&(c.relative=!0),(e=b.match(a.EXTENSION_PATT))&&(c.extension=e[1].toLowerCase()),c},a.formatQueryString=function(a,b){if(null==a)throw new Error("You must specify data.");var c=[];for(var d in a)c.push(d+"="+escape(a[d]));return b&&(c=c.concat(b)),c.join("&")},a.buildPath=function(a,b){if(null==b)return a;var c=[],d=a.indexOf("?");if(-1!=d){var e=a.slice(d+1);c=c.concat(e.split("&"))}return-1!=d?a.slice(0,d)+"?"+this.formatQueryString(b,c):a+"?"+this.formatQueryString(b,c)},a.isCrossDomain=function(a){var b=document.createElement("a");b.href=a.src;var c=document.createElement("a");c.href=location.href;var d=""!=b.hostname&&(b.port!=c.port||b.protocol!=c.protocol||b.hostname!=c.hostname);return d},a.isLocal=function(a){var b=document.createElement("a");return b.href=a.src,""==b.hostname&&"file:"==b.protocol},a.isBinary=function(a){switch(a){case createjs.AbstractLoader.IMAGE:case createjs.AbstractLoader.BINARY:return!0;default:return!1}},a.isImageTag=function(a){return a instanceof HTMLImageElement},a.isAudioTag=function(a){return window.HTMLAudioElement?a instanceof HTMLAudioElement:!1},a.isVideoTag=function(a){return window.HTMLVideoElement?a instanceof HTMLVideoElement:!1},a.isText=function(a){switch(a){case createjs.AbstractLoader.TEXT:case createjs.AbstractLoader.JSON:case createjs.AbstractLoader.MANIFEST:case createjs.AbstractLoader.XML:case createjs.AbstractLoader.CSS:case createjs.AbstractLoader.SVG:case createjs.AbstractLoader.JAVASCRIPT:case createjs.AbstractLoader.SPRITESHEET:return!0;default:return!1}},a.getTypeByExtension=function(a){if(null==a)return createjs.AbstractLoader.TEXT;switch(a.toLowerCase()){case"jpeg":case"jpg":case"gif":case"png":case"webp":case"bmp":return createjs.AbstractLoader.IMAGE;case"ogg":case"mp3":case"webm":return createjs.AbstractLoader.SOUND;case"mp4":case"webm":case"ts":return createjs.AbstractLoader.VIDEO;case"json":return createjs.AbstractLoader.JSON;case"xml":return createjs.AbstractLoader.XML;case"css":return createjs.AbstractLoader.CSS;case"js":return createjs.AbstractLoader.JAVASCRIPT;case"svg":return createjs.AbstractLoader.SVG;default:return createjs.AbstractLoader.TEXT}},createjs.RequestUtils=a}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractLoader(a,b,c){this.EventDispatcher_constructor(),this.loaded=!1,this.canceled=!1,this.progress=0,this.type=c,this.resultFormatter=null,this._item=a?createjs.LoadItem.create(a):null,this._preferXHR=b,this._result=null,this._rawResult=null,this._loadedItems=null,this._tagSrcAttribute=null,this._tag=null}var a=createjs.extend(AbstractLoader,createjs.EventDispatcher),b=AbstractLoader;b.POST="POST",b.GET="GET",b.BINARY="binary",b.CSS="css",b.IMAGE="image",b.JAVASCRIPT="javascript",b.JSON="json",b.JSONP="jsonp",b.MANIFEST="manifest",b.SOUND="sound",b.VIDEO="video",b.SPRITESHEET="spritesheet",b.SVG="svg",b.TEXT="text",b.XML="xml",a.getItem=function(){return this._item},a.getResult=function(a){return a?this._rawResult:this._result},a.getTag=function(){return this._tag},a.setTag=function(a){this._tag=a},a.load=function(){this._createRequest(),this._request.on("complete",this,this),this._request.on("progress",this,this),this._request.on("loadStart",this,this),this._request.on("abort",this,this),this._request.on("timeout",this,this),this._request.on("error",this,this);var a=new createjs.Event("initialize");a.loader=this._request,this.dispatchEvent(a),this._request.load()},a.cancel=function(){this.canceled=!0,this.destroy()},a.destroy=function(){this._request&&(this._request.removeAllEventListeners(),this._request.destroy()),this._request=null,this._item=null,this._rawResult=null,this._result=null,this._loadItems=null,this.removeAllEventListeners()},a.getLoadedItems=function(){return this._loadedItems},a._createRequest=function(){this._request=this._preferXHR?new createjs.XHRRequest(this._item):new createjs.TagRequest(this._item,this._tag||this._createTag(),this._tagSrcAttribute)},a._createTag=function(){return null},a._sendLoadStart=function(){this._isCanceled()||this.dispatchEvent("loadstart")},a._sendProgress=function(a){if(!this._isCanceled()){var b=null;"number"==typeof a?(this.progress=a,b=new createjs.ProgressEvent(this.progress)):(b=a,this.progress=a.loaded/a.total,b.progress=this.progress,(isNaN(this.progress)||1/0==this.progress)&&(this.progress=0)),this.hasEventListener("progress")&&this.dispatchEvent(b)}},a._sendComplete=function(){if(!this._isCanceled()){this.loaded=!0;var a=new createjs.Event("complete");a.rawResult=this._rawResult,null!=this._result&&(a.result=this._result),this.dispatchEvent(a)}},a._sendError=function(a){!this._isCanceled()&&this.hasEventListener("error")&&(null==a&&(a=new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY")),this.dispatchEvent(a))},a._isCanceled=function(){return null==window.createjs||this.canceled?!0:!1},a.resultFormatter=null,a.handleEvent=function(a){switch(a.type){case"complete":this._rawResult=a.target._response;var b=this.resultFormatter&&this.resultFormatter(this);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.buildPath=function(a,b){return createjs.RequestUtils.buildPath(a,b)},a.toString=function(){return"[PreloadJS AbstractLoader]"},createjs.AbstractLoader=createjs.promote(AbstractLoader,"EventDispatcher")}(),this.createjs=this.createjs||{},function(){"use strict";function AbstractMediaLoader(a,b,c){this.AbstractLoader_constructor(a,b,c),this.resultFormatter=this._formatResult,this._tagSrcAttribute="src",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,this._startTagVisibility=null}var a=createjs.extend(TagRequest,createjs.AbstractRequest);a.load=function(){this._tag.onload=createjs.proxy(this._handleTagComplete,this),this._tag.onreadystatechange=createjs.proxy(this._handleReadyStateChange,this),this._tag.onerror=createjs.proxy(this._handleError,this);var a=new createjs.Event("initialize");a.loader=this._tag,this.dispatchEvent(a),this._hideTag(),this._loadTimeout=setTimeout(createjs.proxy(this._handleTimeout,this),this._item.loadTimeout),this._tag[this._tagSrcAttribute]=this._item.src,null==this._tag.parentNode&&(window.document.body.appendChild(this._tag),this._addedToDOM=!0)},a.destroy=function(){this._clean(),this._tag=null,this.AbstractRequest_destroy()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleError=function(){this._clean(),this.dispatchEvent("error")},a._handleTagComplete=function(){this._rawResult=this._tag,this._result=this.resultFormatter&&this.resultFormatter(this)||this._rawResult,this._clean(),this._showTag(),this.dispatchEvent("complete")},a._handleTimeout=function(){this._clean(),this.dispatchEvent(new createjs.Event("timeout"))},a._clean=function(){this._tag.onload=null,this._tag.onreadystatechange=null,this._tag.onerror=null,this._addedToDOM&&null!=this._tag.parentNode&&this._tag.parentNode.removeChild(this._tag),clearTimeout(this._loadTimeout)},a._hideTag=function(){this._startTagVisibility=this._tag.style.visibility,this._tag.style.visibility="hidden"},a._showTag=function(){this._tag.style.visibility=this._startTagVisibility},a._handleStalled=function(){},createjs.TagRequest=createjs.promote(TagRequest,"AbstractRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function MediaTagRequest(a,b,c){this.AbstractRequest_constructor(a),this._tag=b,this._tagSrcAttribute=c,this._loadedHandler=createjs.proxy(this._handleTagComplete,this)}var a=createjs.extend(MediaTagRequest,createjs.TagRequest);a.load=function(){var a=createjs.proxy(this._handleStalled,this);this._stalledCallback=a;var b=createjs.proxy(this._handleProgress,this);this._handleProgress=b,this._tag.addEventListener("stalled",a),this._tag.addEventListener("progress",b),this._tag.addEventListener&&this._tag.addEventListener("canplaythrough",this._loadedHandler,!1),this.TagRequest_load()},a._handleReadyStateChange=function(){clearTimeout(this._loadTimeout);var a=this._tag;("loaded"==a.readyState||"complete"==a.readyState)&&this._handleTagComplete()},a._handleStalled=function(){},a._handleProgress=function(a){if(a&&!(a.loaded>0&&0==a.total)){var b=new createjs.ProgressEvent(a.loaded,a.total);this.dispatchEvent(b)}},a._clean=function(){this._tag.removeEventListener&&this._tag.removeEventListener("canplaythrough",this._loadedHandler),this._tag.removeEventListener("stalled",this._stalledCallback),this._tag.removeEventListener("progress",this._progressCallback),this.TagRequest__clean()},createjs.MediaTagRequest=createjs.promote(MediaTagRequest,"TagRequest")}(),this.createjs=this.createjs||{},function(){"use strict";function XHRRequest(a){this.AbstractRequest_constructor(a),this._request=null,this._loadTimeout=null,this._xhrLevel=1,this._response=null,this._rawResponse=null,this._canceled=!1,this._handleLoadStartProxy=createjs.proxy(this._handleLoadStart,this),this._handleProgressProxy=createjs.proxy(this._handleProgress,this),this._handleAbortProxy=createjs.proxy(this._handleAbort,this),this._handleErrorProxy=createjs.proxy(this._handleError,this),this._handleTimeoutProxy=createjs.proxy(this._handleTimeout,this),this._handleLoadProxy=createjs.proxy(this._handleLoad,this),this._handleReadyStateChangeProxy=createjs.proxy(this._handleReadyStateChange,this),!this._createXHR(a)}var a=createjs.extend(XHRRequest,createjs.AbstractRequest);XHRRequest.ACTIVEX_VERSIONS=["Msxml2.XMLHTTP.6.0","Msxml2.XMLHTTP.5.0","Msxml2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"],a.getResult=function(a){return a&&this._rawResponse?this._rawResponse:this._response},a.cancel=function(){this.canceled=!0,this._clean(),this._request.abort()},a.load=function(){if(null==this._request)return void this._handleError();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._item.method!=createjs.AbstractLoader.GET?this._item.method==createjs.AbstractLoader.POST&&this._request.send(createjs.RequestUtils.formatQueryString(this._item.values)):this._request.send()}catch(a){this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND",null,a))}},a.setResponseType=function(a){"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);switch(a){case 404:case 0:return new Error(a)}return null},a._getResponse=function(){if(null!=this._response)return this._response;if(null!=this._request.response)return this._request.response;try{if(null!=this._request.responseText)return this._request.responseText}catch(a){}try{if(null!=this._request.responseXML)return this._request.responseXML}catch(a){}return null},a._createXHR=function(a){var b=createjs.RequestUtils.isCrossDomain(a),c={},d=null;if(window.XMLHttpRequest)d=new XMLHttpRequest,b&&void 0===d.withCredentials&&window.XDomainRequest&&(d=new XDomainRequest);else{for(var e=0,f=s.ACTIVEX_VERSIONS.length;f>e;e++){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.AbstractLoader.GET?createjs.RequestUtils.buildPath(a.src,a.values):a.src,d.open(a.method||createjs.AbstractLoader.GET,i,!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 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.AbstractLoader.SOUND),createjs.RequestUtils.isAudioTag(a)?this._tag=a:createjs.RequestUtils.isAudioTag(a.src)?this._tag=a:createjs.RequestUtils.isAudioTag(a.tag)&&(this._tag=createjs.RequestUtils.isAudioTag(a)?a:a.src),null!=this._tag&&(this._preferXHR=!1)}var a=createjs.extend(SoundLoader,createjs.AbstractMediaLoader),b=SoundLoader;b.canLoadItem=function(a){return a.type==createjs.AbstractLoader.SOUND},a._createTag=function(a){var b=document.createElement("audio");return b.autoplay=!1,b.preload="none",b.src=a,b},createjs.SoundLoader=createjs.promote(SoundLoader,"AbstractMediaLoader")}(),this.createjs=this.createjs||{},function(){"use strict";var PlayPropsConfig=function(){this.interrupt=null,this.delay=null,this.offset=null,this.loop=null,this.volume=null,this.pan=null,this.startTime=null,this.duration=null},a=PlayPropsConfig.prototype={},b=PlayPropsConfig;b.create=function(a){if(a instanceof b||a instanceof Object){var c=new createjs.PlayPropsConfig;return c.set(a),c}throw new Error("Type not recognized.")},a.set=function(a){for(var b in a)this[b]=a[b];return this},a.toString=function(){return"[PlayPropsConfig]"},createjs.PlayPropsConfig=b}(),this.createjs=this.createjs||{},function(){"use strict";function Sound(){throw"Sound cannot be instantiated"}function a(a,b){this.init(a,b)}var b=Sound;b.INTERRUPT_ANY="any",b.INTERRUPT_EARLY="early",b.INTERRUPT_LATE="late",b.INTERRUPT_NONE="none",b.PLAY_INITED="playInited",b.PLAY_SUCCEEDED="playSucceeded",b.PLAY_INTERRUPTED="playInterrupted",b.PLAY_FINISHED="playFinished",b.PLAY_FAILED="playFailed",b.SUPPORTED_EXTENSIONS=["mp3","ogg","opus","mpeg","wav","m4a","mp4","aiff","wma","mid"],b.EXTENSION_MAP={m4a:"mp4"},b.FILE_PATTERN=/^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([\/.]*?(?:[^?]+)?\/)?((?:[^\/?]+)\.(\w+))(?:\?(\S+)?)?$/,b.defaultInterruptBehavior=b.INTERRUPT_NONE,b.alternateExtensions=[],b.activePlugin=null,b._masterVolume=1,Object.defineProperty(b,"volume",{get:function(){return this._masterVolume},set:function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)}}),b._masterMute=!1,Object.defineProperty(b,"muted",{get:function(){return this._masterMute},set:function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0}}),Object.defineProperty(b,"capabilities",{get:function(){return null==b.activePlugin?null:b.activePlugin._capabilities},set:function(){return!1}}),b._pluginsRegistered=!1,b._lastID=0,b._instances=[],b._idHash={},b._preloadHash={},b._defaultPlayPropsHash={},b.addEventListener=null,b.removeEventListener=null,b.removeAllEventListeners=null,b.dispatchEvent=null,b.hasEventListener=null,b._listeners=null,createjs.EventDispatcher.initialize(b),b.getPreloadHandlers=function(){return{callback:createjs.proxy(b.initLoad,b),types:["sound"],extensions:b.SUPPORTED_EXTENSIONS}},b._handleLoadComplete=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!0,b.hasEventListener("fileload")){var a=new createjs.Event("fileload");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._handleLoadError=function(a){var c=a.target.getItem().src;if(b._preloadHash[c])for(var d=0,e=b._preloadHash[c].length;e>d;d++){var f=b._preloadHash[c][d];if(b._preloadHash[c][d]=!1,b.hasEventListener("fileerror")){var a=new createjs.Event("fileerror");a.src=f.src,a.id=f.id,a.data=f.data,a.sprite=f.sprite,b.dispatchEvent(a)}}},b._registerPlugin=function(a){return a.isSupported()?(b.activePlugin=new a,!0):!1},b.registerPlugins=function(a){b._pluginsRegistered=!0;for(var c=0,d=a.length;d>c;c++)if(b._registerPlugin(a[c]))return!0;return!1},b.initializeDefaultPlugins=function(){return null!=b.activePlugin?!0:b._pluginsRegistered?!1:b.registerPlugins([createjs.WebAudioPlugin,createjs.HTMLAudioPlugin])?!0:!1},b.isReady=function(){return null!=b.activePlugin},b.getCapabilities=function(){return null==b.activePlugin?null:b.activePlugin._capabilities},b.getCapability=function(a){return null==b.activePlugin?null:b.activePlugin._capabilities[a]},b.initLoad=function(a){return b._registerSound(a)},b._registerSound=function(c){if(!b.initializeDefaultPlugins())return!1;var d;if(c.src instanceof Object?(d=b._parseSrc(c.src),d.src=c.path+d.src):d=b._parsePath(c.src),null==d)return!1;c.src=d.src,c.type="sound";var e=c.data,f=null;if(null!=e&&(isNaN(e.channels)?isNaN(e)||(f=parseInt(e)):f=parseInt(e.channels),e.audioSprite))for(var g,h=e.audioSprite.length;h--;)g=e.audioSprite[h],b._idHash[g.id]={src:c.src,startTime:parseInt(g.startTime),duration:parseInt(g.duration)},g.defaultPlayProps&&(b._defaultPlayPropsHash[g.id]=createjs.PlayPropsConfig.create(g.defaultPlayProps));null!=c.id&&(b._idHash[c.id]={src:c.src});var i=b.activePlugin.register(c);return a.create(c.src,f),null!=e&&isNaN(e)?c.data.channels=f||a.maxPerChannel():c.data=f||a.maxPerChannel(),i.type&&(c.type=i.type),c.defaultPlayProps&&(b._defaultPlayPropsHash[c.src]=createjs.PlayPropsConfig.create(c.defaultPlayProps)),i},b.registerSound=function(a,c,d,e,f){var g={src:a,id:c,data:d,defaultPlayProps:f};a instanceof Object&&a.src&&(e=c,g=a),g=createjs.LoadItem.create(g),g.path=e,null==e||g.src instanceof Object||(g.src=e+a);var h=b._registerSound(g);if(!h)return!1;if(b._preloadHash[g.src]||(b._preloadHash[g.src]=[]),b._preloadHash[g.src].push(g),1==b._preloadHash[g.src].length)h.on("complete",createjs.proxy(this._handleLoadComplete,this)),h.on("error",createjs.proxy(this._handleLoadError,this)),b.activePlugin.preload(h);else if(1==b._preloadHash[g.src][0])return!0;return g},b.registerSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.registerSound(a[d].src,a[d].id,a[d].data,b,a[d].defaultPlayProps);return c},b.removeSound=function(c,d){if(null==b.activePlugin)return!1;c instanceof Object&&c.src&&(c=c.src);var e;if(c instanceof Object?e=b._parseSrc(c):(c=b._getSrcById(c).src,e=b._parsePath(c)),null==e)return!1;c=e.src,null!=d&&(c=d+c);for(var f in b._idHash)b._idHash[f].src==c&&delete b._idHash[f];return a.removeSrc(c),delete b._preloadHash[c],b.activePlugin.removeSound(c),!0},b.removeSounds=function(a,b){var c=[];a.path&&(b?b+=a.path:b=a.path,a=a.manifest);for(var d=0,e=a.length;e>d;d++)c[d]=createjs.Sound.removeSound(a[d].src,b);return c},b.removeAllSounds=function(){b._idHash={},b._preloadHash={},a.removeAll(),b.activePlugin&&b.activePlugin.removeAllSounds()},b.loadComplete=function(a){if(!b.isReady())return!1;var c=b._parsePath(a);return a=c?b._getSrcById(c.src).src:b._getSrcById(a).src,void 0==b._preloadHash[a]?!1:1==b._preloadHash[a][0]},b._parsePath=function(a){"string"!=typeof a&&(a=a.toString());var c=a.match(b.FILE_PATTERN);if(null==c)return!1;for(var d=c[4],e=c[5],f=b.capabilities,g=0;!f[e];)if(e=b.alternateExtensions[g++],g>b.alternateExtensions.length)return null;a=a.replace("."+c[5],"."+e);var h={name:d,src:a,extension:e};return h},b._parseSrc=function(a){var c={name:void 0,src:void 0,extension:void 0},d=b.capabilities;for(var e in a)if(a.hasOwnProperty(e)&&d[e]){c.src=a[e],c.extension=e;break}if(!c.src)return!1;var f=c.src.lastIndexOf("/");return c.name=-1!=f?c.src.slice(f+1):c.src,c},b.play=function(a,c,d,e,f,g,h,i,j){var k;k=createjs.PlayPropsConfig.create(c instanceof Object||c instanceof createjs.PlayPropsConfig?c:{interrupt:c,delay:d,offset:e,loop:f,volume:g,pan:h,startTime:i,duration:j});var l=b.createInstance(a,k.startTime,k.duration),m=b._playInstance(l,k);return m||l._playFailed(),l},b.createInstance=function(c,d,e){if(!b.initializeDefaultPlugins())return new createjs.DefaultSoundInstance(c,d,e);var f=b._defaultPlayPropsHash[c];c=b._getSrcById(c);var g=b._parsePath(c.src),h=null;
return null!=g&&null!=g.src?(a.create(g.src),null==d&&(d=c.startTime),h=b.activePlugin.create(g.src,d,e||c.duration),f=f||b._defaultPlayPropsHash[g.src],f&&h.applyPlayProps(f)):h=new createjs.DefaultSoundInstance(c,d,e),h.uniqueId=b._lastID++,h},b.stop=function(){for(var a=this._instances,b=a.length;b--;)a[b].stop()},b.setVolume=function(a){if(null==Number(a))return!1;if(a=Math.max(0,Math.min(1,a)),b._masterVolume=a,!this.activePlugin||!this.activePlugin.setVolume||!this.activePlugin.setVolume(a))for(var c=this._instances,d=0,e=c.length;e>d;d++)c[d].setMasterVolume(a)},b.getVolume=function(){return this._masterVolume},b.setMute=function(a){if(null==a)return!1;if(this._masterMute=a,!this.activePlugin||!this.activePlugin.setMute||!this.activePlugin.setMute(a))for(var b=this._instances,c=0,d=b.length;d>c;c++)b[c].setMasterMute(a);return!0},b.getMute=function(){return this._masterMute},b.setDefaultPlayProps=function(a,c){a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]=createjs.PlayPropsConfig.create(c)},b.getDefaultPlayProps=function(a){return a=b._getSrcById(a),b._defaultPlayPropsHash[b._parsePath(a.src).src]},b._playInstance=function(a,c){var d=b._defaultPlayPropsHash[a.src]||{};if(null==c.interrupt&&(c.interrupt=d.interrupt||b.defaultInterruptBehavior),null==c.delay&&(c.delay=d.delay||0),null==c.offset&&(c.offset=a.getPosition()),null==c.loop&&(c.loop=a.loop),null==c.volume&&(c.volume=a.volume),null==c.pan&&(c.pan=a.pan),0==c.delay){var e=b._beginPlaying(a,c);if(!e)return!1}else{var f=setTimeout(function(){b._beginPlaying(a,c)},c.delay);a.delayTimeoutId=f}return this._instances.push(a),!0},b._beginPlaying=function(b,c){if(!a.add(b,c.interrupt))return!1;var d=b._beginPlaying(c);if(!d){var e=createjs.indexOf(this._instances,b);return e>-1&&this._instances.splice(e,1),!1}return!0},b._getSrcById=function(a){return b._idHash[a]||{src:a}},b._playFinished=function(b){a.remove(b);var c=createjs.indexOf(this._instances,b);c>-1&&this._instances.splice(c,1)},createjs.Sound=Sound,a.channels={},a.create=function(b,c){var d=a.get(b);return null==d?(a.channels[b]=new a(b,c),!0):!1},a.removeSrc=function(b){var c=a.get(b);return null==c?!1:(c._removeAll(),delete a.channels[b],!0)},a.removeAll=function(){for(var b in a.channels)a.channels[b]._removeAll();a.channels={}},a.add=function(b,c){var d=a.get(b.src);return null==d?!1:d._add(b,c)},a.remove=function(b){var c=a.get(b.src);return null==c?!1:(c._remove(b),!0)},a.maxPerChannel=function(){return c.maxDefault},a.get=function(b){return a.channels[b]};var c=a.prototype;c.constructor=a,c.src=null,c.max=null,c.maxDefault=100,c.length=0,c.init=function(a,b){this.src=a,this.max=b||this.maxDefault,-1==this.max&&(this.max=this.maxDefault),this._instances=[]},c._get=function(a){return this._instances[a]},c._add=function(a,b){return this._getSlot(b,a)?(this._instances.push(a),this.length++,!0):!1},c._remove=function(a){var b=createjs.indexOf(this._instances,a);return-1==b?!1:(this._instances.splice(b,1),this.length--,!0)},c._removeAll=function(){for(var a=this.length-1;a>=0;a--)this._instances[a].stop()},c._getSlot=function(a){var b,c;if(a!=Sound.INTERRUPT_NONE&&(c=this._get(0),null==c))return!0;for(var d=0,e=this.max;e>d;d++){if(b=this._get(d),null==b)return!0;if(b.playState==Sound.PLAY_FINISHED||b.playState==Sound.PLAY_INTERRUPTED||b.playState==Sound.PLAY_FAILED){c=b;break}a!=Sound.INTERRUPT_NONE&&(a==Sound.INTERRUPT_EARLY&&b.getPosition()<c.getPosition()||a==Sound.INTERRUPT_LATE&&b.getPosition()>c.getPosition())&&(c=b)}return null!=c?(c._interrupt(),this._remove(c),!0):!1},c.toString=function(){return"[Sound SoundChannel]"}}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractSoundInstance=function(a,b,c,d){this.EventDispatcher_constructor(),this.src=a,this.uniqueId=-1,this.playState=null,this.delayTimeoutId=null,this._volume=1,Object.defineProperty(this,"volume",{get:this.getVolume,set:this.setVolume}),this._pan=0,Object.defineProperty(this,"pan",{get:this.getPan,set:this.setPan}),this._startTime=Math.max(0,b||0),Object.defineProperty(this,"startTime",{get:this.getStartTime,set:this.setStartTime}),this._duration=Math.max(0,c||0),Object.defineProperty(this,"duration",{get:this.getDuration,set:this.setDuration}),this._playbackResource=null,Object.defineProperty(this,"playbackResource",{get:this.getPlaybackResource,set:this.setPlaybackResource}),d!==!1&&d!==!0&&this.setPlaybackResource(d),this._position=0,Object.defineProperty(this,"position",{get:this.getPosition,set:this.setPosition}),this._loop=0,Object.defineProperty(this,"loop",{get:this.getLoop,set:this.setLoop}),this._muted=!1,Object.defineProperty(this,"muted",{get:this.getMuted,set:this.setMuted}),this._paused=!1,Object.defineProperty(this,"paused",{get:this.getPaused,set:this.setPaused})},a=createjs.extend(AbstractSoundInstance,createjs.EventDispatcher);a.play=function(a,b,c,d,e,f){var g;return g=createjs.PlayPropsConfig.create(a instanceof Object||a instanceof createjs.PlayPropsConfig?a:{interrupt:a,delay:b,offset:c,loop:d,volume:e,pan:f}),this.playState==createjs.Sound.PLAY_SUCCEEDED?(this.applyPlayProps(g),void(this._paused&&this.setPaused(!1))):(this._cleanUp(),createjs.Sound._playInstance(this,g),this)},a.stop=function(){return this._position=0,this._paused=!1,this._handleStop(),this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,this},a.destroy=function(){this._cleanUp(),this.src=null,this.playbackResource=null,this.removeAllEventListeners()},a.applyPlayProps=function(a){return null!=a.offset&&this.setPosition(a.offset),null!=a.loop&&this.setLoop(a.loop),null!=a.volume&&this.setVolume(a.volume),null!=a.pan&&this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),this},a.toString=function(){return"[AbstractSoundInstance]"},a.getPaused=function(){return this._paused},a.setPaused=function(a){return a!==!0&&a!==!1||this._paused==a||1==a&&this.playState!=createjs.Sound.PLAY_SUCCEEDED?void 0:(this._paused=a,a?this._pause():this._resume(),clearTimeout(this.delayTimeoutId),this)},a.setVolume=function(a){return a==this._volume?this:(this._volume=Math.max(0,Math.min(1,a)),this._muted||this._updateVolume(),this)},a.getVolume=function(){return this._volume},a.setMuted=function(a){return a===!0||a===!1?(this._muted=a,this._updateVolume(),this):void 0},a.getMuted=function(){return this._muted},a.setPan=function(a){return a==this._pan?this:(this._pan=Math.max(-1,Math.min(1,a)),this._updatePan(),this)},a.getPan=function(){return this._pan},a.getPosition=function(){return this._paused||this.playState!=createjs.Sound.PLAY_SUCCEEDED||(this._position=this._calculateCurrentPosition()),this._position},a.setPosition=function(a){return this._position=Math.max(0,a),this.playState==createjs.Sound.PLAY_SUCCEEDED&&this._updatePosition(),this},a.getStartTime=function(){return this._startTime},a.setStartTime=function(a){return a==this._startTime?this:(this._startTime=Math.max(0,a||0),this._updateStartTime(),this)},a.getDuration=function(){return this._duration},a.setDuration=function(a){return a==this._duration?this:(this._duration=Math.max(0,a||0),this._updateDuration(),this)},a.setPlaybackResource=function(a){return this._playbackResource=a,0==this._duration&&this._setDurationFromSource(),this},a.getPlaybackResource=function(){return this._playbackResource},a.getLoop=function(){return this._loop},a.setLoop=function(a){null!=this._playbackResource&&(0!=this._loop&&0==a?this._removeLooping(a):0==this._loop&&0!=a&&this._addLooping(a)),this._loop=a},a._sendEvent=function(a){var b=new createjs.Event(a);this.dispatchEvent(b)},a._cleanUp=function(){clearTimeout(this.delayTimeoutId),this._handleCleanUp(),this._paused=!1,createjs.Sound._playFinished(this)},a._interrupt=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_INTERRUPTED,this._sendEvent("interrupted")},a._beginPlaying=function(a){return this.setPosition(a.offset),this.setLoop(a.loop),this.setVolume(a.volume),this.setPan(a.pan),null!=a.startTime&&(this.setStartTime(a.startTime),this.setDuration(a.duration)),null!=this._playbackResource&&this._position<this._duration?(this._paused=!1,this._handleSoundReady(),this.playState=createjs.Sound.PLAY_SUCCEEDED,this._sendEvent("succeeded"),!0):(this._playFailed(),!1)},a._playFailed=function(){this._cleanUp(),this.playState=createjs.Sound.PLAY_FAILED,this._sendEvent("failed")},a._handleSoundComplete=function(){return this._position=0,0!=this._loop?(this._loop--,this._handleLoop(),void this._sendEvent("loop")):(this._cleanUp(),this.playState=createjs.Sound.PLAY_FINISHED,void this._sendEvent("complete"))},a._handleSoundReady=function(){},a._updateVolume=function(){},a._updatePan=function(){},a._updateStartTime=function(){},a._updateDuration=function(){},a._setDurationFromSource=function(){},a._calculateCurrentPosition=function(){},a._updatePosition=function(){},a._removeLooping=function(){},a._addLooping=function(){},a._pause=function(){},a._resume=function(){},a._handleStop=function(){},a._handleCleanUp=function(){},a._handleLoop=function(){},createjs.AbstractSoundInstance=createjs.promote(AbstractSoundInstance,"EventDispatcher"),createjs.DefaultSoundInstance=createjs.AbstractSoundInstance}(),this.createjs=this.createjs||{},function(){"use strict";var AbstractPlugin=function(){this._capabilities=null,this._loaders={},this._audioSources={},this._soundInstances={},this._volume=1,this._loaderClass,this._soundInstanceClass},a=AbstractPlugin.prototype;AbstractPlugin._capabilities=null,AbstractPlugin.isSupported=function(){return!0},a.register=function(a){var b=this._loaders[a.src];return b&&!b.canceled?this._loaders[a.src]:(this._audioSources[a.src]=!0,this._soundInstances[a.src]=[],b=new this._loaderClass(a),b.on("complete",this._handlePreloadComplete,this),this._loaders[a.src]=b,b)},a.preload=function(a){a.on("error",this._handlePreloadError,this),a.load()},a.isPreloadStarted=function(a){return null!=this._audioSources[a]},a.isPreloadComplete=function(a){return!(null==this._audioSources[a]||1==this._audioSources[a])},a.removeSound=function(a){if(this._soundInstances[a]){for(var b=this._soundInstances[a].length;b--;){var c=this._soundInstances[a][b];c.destroy()}delete this._soundInstances[a],delete this._audioSources[a],this._loaders[a]&&this._loaders[a].destroy(),delete this._loaders[a]}},a.removeAllSounds=function(){for(var a in this._audioSources)this.removeSound(a)},a.create=function(a,b,c){this.isPreloadStarted(a)||this.preload(this.register(a));var d=new this._soundInstanceClass(a,b,c,this._audioSources[a]);return this._soundInstances[a].push(d),d},a.setVolume=function(a){return this._volume=a,this._updateVolume(),!0},a.getVolume=function(){return this._volume},a.setMute=function(){return this._updateVolume(),!0},a.toString=function(){return"[AbstractPlugin]"},a._handlePreloadComplete=function(a){var b=a.target.getItem().src;this._audioSources[b]=a.result;for(var c=0,d=this._soundInstances[b].length;d>c;c++){var e=this._soundInstances[b][c];e.setPlaybackResource(this._audioSources[b])}},a._handlePreloadError=function(){},a._updateVolume=function(){},createjs.AbstractPlugin=AbstractPlugin}(),this.createjs=this.createjs||{},function(){"use strict";function a(a){this.AbstractLoader_constructor(a,!0,createjs.AbstractLoader.SOUND)}var b=createjs.extend(a,createjs.AbstractLoader);a.context=null,b.toString=function(){return"[WebAudioLoader]"},b._createRequest=function(){this._request=new createjs.XHRRequest(this._item,!1),this._request.setResponseType("arraybuffer")},b._sendComplete=function(){a.context.decodeAudioData(this._rawResult,createjs.proxy(this._handleAudioDecoded,this),createjs.proxy(this._sendError,this))},b._handleAudioDecoded=function(a){this._result=a,this.AbstractLoader__sendComplete()},createjs.WebAudioLoader=createjs.promote(a,"AbstractLoader")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioSoundInstance(a,c,d,e){this.AbstractSoundInstance_constructor(a,c,d,e),this.gainNode=b.context.createGain(),this.panNode=b.context.createPanner(),this.panNode.panningModel=b._panningModel,this.panNode.connect(this.gainNode),this._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){a.stop(0),a.disconnect(0);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=.001*this._position;c>a&&(c=a),this.sourceNode=this._createAndPlayAudioNode(b.context.currentTime-a,c),this._playbackStartTime=this.sourceNode.startTime-c,this._soundCompleteTimeout=setTimeout(this._endedHandler,1e3*(a-c)),0!=this._loop&&(this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0))},a._createAndPlayAudioNode=function(a,c){var d=b.context.createBufferSource();d.buffer=this.playbackResource,d.connect(this.panNode);var e=.001*this._duration;return d.startTime=a+e,d.start(d.startTime,c+.001*this._startTime,e-c),d},a._pause=function(){this._position=1e3*(b.context.currentTime-this._playbackStartTime),this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),0!=this.gainNode.numberOfOutputs&&this.gainNode.disconnect(0),clearTimeout(this._soundCompleteTimeout)},a._resume=function(){this._handleSoundReady()},a._updateVolume=function(){var a=this._muted?0:this._volume;a!=this.gainNode.gain.value&&(this.gainNode.gain.value=a)},a._calculateCurrentPosition=function(){return 1e3*(b.context.currentTime-this._playbackStartTime)},a._updatePosition=function(){this.sourceNode=this._cleanUpAudioNode(this.sourceNode),this._sourceNodeNext=this._cleanUpAudioNode(this._sourceNodeNext),clearTimeout(this._soundCompleteTimeout),this._paused||this._handleSoundReady()},a._handleLoop=function(){this._cleanUpAudioNode(this.sourceNode),this.sourceNode=this._sourceNodeNext,this._playbackStartTime=this.sourceNode.startTime,this._sourceNodeNext=this._createAndPlayAudioNode(this._playbackStartTime,0),this._soundCompleteTimeout=setTimeout(this._endedHandler,this._duration)},a._updateDuration=function(){this.playState==createjs.Sound.PLAY_SUCCEEDED&&(this._pause(),this._resume())},createjs.WebAudioSoundInstance=createjs.promote(WebAudioSoundInstance,"AbstractSoundInstance")}(),this.createjs=this.createjs||{},function(){"use strict";function WebAudioPlugin(){this.AbstractPlugin_constructor(),this._panningModel=b._panningModel,this.context=b.context,this.dynamicsCompressorNode=this.context.createDynamicsCompressor(),this.dynamicsCompressorNode.connect(this.context.destination),this.gainNode=this.context.createGain(),this.gainNode.connect(this.dynamicsCompressorNode),createjs.WebAudioSoundInstance.destinationNode=this.gainNode,this._capabilities=b._capabilities,this._loaderClass=createjs.WebAudioLoader,this._soundInstanceClass=createjs.WebAudioSoundInstance,this._addPropsToClasses()}var a=createjs.extend(WebAudioPlugin,createjs.AbstractPlugin),b=WebAudioPlugin;b._capabilities=null,b._panningModel="equalpower",b.context=null,b._scratchBuffer=null,b._unlocked=!1,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)if(window.AudioContext)b.context=new AudioContext;else{if(!window.webkitAudioContext)return null;b.context=new webkitAudioContext}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("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._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),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.defaultNumChannels=2,this._capabilities=b._capabilities,this._loaderClass=createjs.SoundLoader,this._soundInstanceClass=createjs.HTMLAudioSoundInstance}var a=createjs.extend(HTMLAudioPlugin,createjs.AbstractPlugin),b=HTMLAudioPlugin;b.MAX_INSTANCES=30,b._AUDIO_READY="canplaythrough",b._AUDIO_ENDED="ended",b._AUDIO_SEEKED="seeked",b._AUDIO_STALLED="stalled",b._TIME_UPDATE="timeupdate",b._capabilities=null,b.isSupported=function(){return b._generateCapabilities(),null!=b._capabilities},b._generateCapabilities=function(){if(null==b._capabilities){var a=document.createElement("audio");if(null==a.canPlayType)return null;b._capabilities={panning:!1,volume:!0,tracks:-1};for(var c=createjs.Sound.SUPPORTED_EXTENSIONS,d=createjs.Sound.EXTENSION_MAP,e=0,f=c.length;f>e;e++){var g=c[e],h=d[g]||g;b._capabilities[g]="no"!=a.canPlayType("audio/"+g)&&""!=a.canPlayType("audio/"+g)||"no"!=a.canPlayType("audio/"+h)&&""!=a.canPlayType("audio/"+h)}}},a.register=function(a){var b=createjs.HTMLAudioTagPool.get(a.src),c=this.AbstractPlugin_register(a);return c.setTag(b),c},a.removeSound=function(a){this.AbstractPlugin_removeSound(a),createjs.HTMLAudioTagPool.remove(a)},a.create=function(a,b,c){var d=this.AbstractPlugin_create(a,b,c);return d.setPlaybackResource(null),d},a.toString=function(){return"[HTMLAudioPlugin]"},a.setVolume=a.getVolume=a.setMute=null,createjs.HTMLAudioPlugin=createjs.promote(HTMLAudioPlugin,"AbstractPlugin")}();
/bower_components/SoundJS/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.combined.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"]);
};
/bower_components/SoundJS/tests/lib/jasmine-2.0.2/MIT.LICENSE
/bower_components/SoundJS/tests/lib/jasmine-2.0.2/console.js
/bower_components/SoundJS/tests/lib/jasmine-2.0.2/jasmine.css
--- SoundJS/tests/package.json (nonexistent)
+++ SoundJS/tests/package.json (revision 73)
@@ -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"
+}
/bower_components/SoundJS/tests/lib/jasmine-2.0.2/jasmine_favicon.png
/bower_components/SoundJS/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);
});
/bower_components/SoundJS/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();
}
});
});
});
/bower_components/SoundJS/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();
});
}
};
/bower_components/SoundJS/tests/tasks/listips.js
@@ -0,0 +1,55 @@
/*
* 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 os = require('os');
 
grunt.registerMultiTask('listips', 'Prints a list of active ips.', function() {
var opts = this.options({"port": 80});
 
var port = opts.port;
var label = opts.label?'('+opts.label+') ':'';
 
if (port == 80) {
port = '';
} else {
port = ':'+port;
}
 
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);
}
}
}
 
addresses.push('http://localhost'+port);
grunt.log.subhead('\n'+label+'Listening on:\n\t', addresses.join('\n\t '));
});
}