/*! * PreloadJS * Visitfor 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 () { "use strict"; /** * Static class holding library specific information such as the version and buildDate of the library. * @class PreloadJS **/ var s = createjs.PreloadJS = createjs.PreloadJS || {}; /** * The version string for this release. * @property version * @type {String} * @static **/ s.version = /*=version*/"1.0.0"; // injected by build process /** * The build date for this release in UTC format. * @property buildDate * @type {String} * @static **/ s.buildDate = /*=date*/"Thu, 14 Sep 2017 19:47:47 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; }; //############################################################################## // deprecate.js //############################################################################## this.createjs = this.createjs||{}; /** * @class Utility Methods */ /** * Wraps deprecated methods so they still be used, but throw warnings to developers. * * obj.deprecatedMethod = createjs.deprecate("Old Method Name", obj._fallbackMethod); * * The recommended approach for deprecated properties is: * * try { * Obj ect.defineProperties(object, { * readyOnlyProp: { get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }) }, * readWriteProp: { * get: createjs.deprecate("readOnlyProp", function() { return this.alternateProp; }), * set: createjs.deprecate("readOnlyProp", function(val) { this.alternateProp = val; }) * }); * } catch (e) {} * * @method deprecate * @param {Function} [fallbackMethod=null] A method to call when the deprecated method is used. See the example for how * @param {String} [name=null] The name of the method or property to display in the console warning. * to deprecate properties. * @return {Function} If a fallbackMethod is supplied, returns a closure that will call the fallback method after * logging the warning in the console. */ createjs.deprecate = function(fallbackMethod, name) { "use strict"; return function() { var msg = "Deprecated property or method '"+name+"'. See docs for info."; console && (console.warn ? console.warn(msg) : console.log(msg)); return fallbackMethod && fallbackMethod.apply(this, arguments); } }; //############################################################################## // 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. * *
loaded
* and total
properties.
* @protected
*/
p._sendProgress = function (value) {
if (this._isCanceled()) { return; }
var event = null;
if (typeof(value) == "number") {
this.progress = value;
event = new createjs.ProgressEvent(this.progress);
} else {
event = value;
this.progress = value.loaded / value.total;
event.progress = this.progress;
if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
}
this.hasEventListener("progress") && this.dispatchEvent(event);
};
/**
* Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
* @method _sendComplete
* @protected
*/
p._sendComplete = function () {
if (this._isCanceled()) { return; }
this.loaded = true;
var event = new createjs.Event("complete");
event.rawResult = this._rawResult;
if (this._result != null) {
event.result = this._result;
}
this.dispatchEvent(event);
};
/**
* Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendError
* @param {ErrorEvent} event The event object containing specific error properties.
* @protected
*/
p._sendError = function (event) {
if (this._isCanceled() || !this.hasEventListener("error")) { return; }
if (event == null) {
event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
}
this.dispatchEvent(event);
};
/**
* Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
* do not cause issues after the queue has been cleaned up.
* @method _isCanceled
* @return {Boolean} If the loader has been canceled.
* @protected
*/
p._isCanceled = function () {
if (window.createjs == null || this.canceled) {
return true;
}
return false;
};
/**
* A custom result formatter function, which is called just before a request dispatches its complete event. Most
* loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
* formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
* @property resultFormatter
* @type Function
* @return {Object} The formatted result
* @since 0.6.0
*/
p.resultFormatter = null;
/**
* 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);
// The resultFormatter is asynchronous
if (result instanceof Function) {
result.call(this,
createjs.proxy(this._resultFormatSuccess, this),
createjs.proxy(this._resultFormatFailed, this)
);
// The result formatter is synchronous
} else {
this._result = result || this._rawResult;
this._sendComplete();
}
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 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;
};
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._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
this._tag[this._tagSrcAttribute] = this._item.src;
// wdg:: Append the tag AFTER setting the src, or SVG loading on iOS will fail.
if (this._tag.parentNode == null) {
createjs.DomUtils.appendToBody(this._tag);
this._addedToDOM = true;
}
};
p.destroy = function() {
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.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);
};
/**
* 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 result
property, but the developer may still
* want to access the raw content as it was loaded.
* @property _rawResponse
* @type {String|Object}
* @private
*/
this._rawResponse = null;
this._canceled = false;
// Setup our event handlers now.
this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this);
this._handleProgressProxy = createjs.proxy(this._handleProgress, this);
this._handleAbortProxy = createjs.proxy(this._handleAbort, this);
this._handleErrorProxy = createjs.proxy(this._handleError, this);
this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this);
this._handleLoadProxy = createjs.proxy(this._handleLoad, this);
this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this);
if (!this._createXHR(item)) {
//TODO: Throw error?
}
};
var p = createjs.extend(XHRRequest, createjs.AbstractRequest);
// static properties
/**
* A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE.
* @property ACTIVEX_VERSIONS
* @type {Array}
* @since 0.4.2
* @private
*/
XHRRequest.ACTIVEX_VERSIONS = [
"Msxml2.XMLHTTP.6.0",
"Msxml2.XMLHTTP.5.0",
"Msxml2.XMLHTTP.4.0",
"MSXML2.XMLHTTP.3.0",
"MSXML2.XMLHTTP",
"Microsoft.XMLHTTP"
];
// Public methods
/**
* Look up the loaded result.
* @method getResult
* @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content
* loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
* returned instead.
* @return {Object} A result object containing the content that was loaded, such as:
* request.readyState == 4
. Only the first call to this method will be processed.
*
* Note that This method uses {{#crossLink "_checkError"}}{{/crossLink}} to determine if the server has returned an
* error code.
* @method _handleLoad
* @param {Object} event The XHR load event.
* @private
*/
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 setTimeout
* callback.
* @method _handleTimeout
* @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout.
* @private
*/
p._handleTimeout = function (event) {
this._clean();
this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
};
// Protected
/**
* Determine if there is an error in the current load.
* Currently this checks the status of the request for problem codes, and not actual response content:
* _response
property will remain null.
* @method _getResponse
* @private
*/
p._getResponse = function () {
if (this._response != null) {
return this._response;
}
if (this._request.response != null) {
return this._request.response;
}
// Android 2.2 uses .responseText
try {
if (this._request.responseText != null) {
return this._request.responseText;
}
} catch (e) {
}
// When loading XML, IE9 does not return .response, instead it returns responseXML.xml
try {
if (this._request.responseXML != null) {
return this._request.responseXML;
}
} catch (e) {
}
return null;
};
/**
* Create an XHR request. Depending on a number of factors, we get totally different results.
* XDomainRequest
when loading cross-domain.type
property with any manifest item.
*
* queue.loadFile({src:"path/to/myFile.mp3x", type:createjs.Types.SOUND});
*
* // Note that PreloadJS will not read a file extension from the query string
* queue.loadFile({src:"", type:createjs.Types.IMAGE});
*
* Supported types are defined on the {{#crossLink "AbstractLoader"}}{{/crossLink}} class, and include:
* rawResult
property of the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}}
* event, or can be looked up using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}, passing `true` as the 2nd
* argument. This is only applicable for content that has been parsed for the browser, specifically: JavaScript,
* CSS, XML, SVG, and JSON objects, or anything loaded with XHR.
*
* var image = queue.getResult("image", true); // load the binary image data loaded with XHR.
*
* PluginscanPlayThrough
event is fired. Browsers other
* than Chrome will continue to download in the background.null
when they are
* requested, contain the loaded item if it has completed, but not been dispatched to the user, and true
* once they are complete and have been dispatched.
* @property _loadedScripts
* @type {Array}
* @private
*/
this._loadedScripts = [];
/**
* The last progress amount. This is used to suppress duplicate progress events.
* @property _lastProgress
* @type {Number}
* @private
* @since 0.6.0
*/
this._lastProgress = NaN;
};
// static properties
// events
/**
* This event is fired when an individual file has loaded, and been processed.
* @event fileload
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
* object will contain that value as a `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.3.0
*/
/**
* This {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when an an individual file's progress changes.
* @event fileprogress
* @since 0.3.0
*/
/**
* This event is fired when an individual file starts to load.
* @event filestart
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
* object will contain that value as a property.
*/
/**
* Although it extends {{#crossLink "AbstractLoader"}}{{/crossLink}}, the `initialize` event is never fired from
* a LoadQueue instance.
* @event initialize
* @private
*/
// public methods
/**
* Register a custom loaders class. New loaders are given precedence over loaders added earlier and default loaders.
* It is recommended that loaders extend {{#crossLink "AbstractLoader"}}{{/crossLink}}. Loaders can only be added
* once, and will be prepended to the list of available loaders.
* @method registerLoader
* @param {Function|AbstractLoader} loader The AbstractLoader class to add.
* @since 0.6.0
*/
p.registerLoader = function (loader) {
if (!loader || !loader.canLoadItem) {
throw new Error("loader is of an incorrect type.");
} else if (this._availableLoaders.indexOf(loader) != -1) {
throw new Error("loader already exists."); //LM: Maybe just silently fail here
}
this._availableLoaders.unshift(loader);
};
/**
* Remove a custom loader added using {{#crossLink "registerLoader"}}{{/crossLink}}. Only custom loaders can be
* unregistered, the default loaders will always be available.
* @method unregisterLoader
* @param {Function|AbstractLoader} loader The AbstractLoader class to remove
*/
p.unregisterLoader = function (loader) {
var idx = this._availableLoaders.indexOf(loader);
if (idx != -1 && idx < this._defaultLoaderLength - 1) {
this._availableLoaders.splice(idx, 1);
}
};
/**
* Change the {{#crossLink "preferXHR:property"}}{{/crossLink}} value. Note that if this is set to `true`, it may
* fail, or be ignored depending on the browser's capabilities and the load type.
* @method setPreferXHR
* @param {Boolean} value
* @returns {Boolean} The value of {{#crossLink "preferXHR"}}{{/crossLink}} that was successfully set.
* @since 0.6.0
*/
p.setPreferXHR = function (value) {
// Determine if we can use XHR. XHR defaults to TRUE, but the browser may not support it.
//TODO: Should we be checking for the other XHR types? Might have to do a try/catch on the different types similar to createXHR.
this.preferXHR = (value != false && window.XMLHttpRequest != null);
return this.preferXHR;
};
/**
* Stops all queued and loading items, and clears the queue. This also removes all internal references to loaded
* content, and allows the queue to be used again.
* @method removeAll
* @since 0.3.0
*/
p.removeAll = function () {
this.remove();
};
/**
* Stops an item from being loaded, and removes it from the queue. If nothing is passed, all items are removed.
* This also removes internal references to loaded item(s).
*
* Example
*
* queue.loadManifest([
* {src:"test.png", id:"png"},
* {src:"test.jpg", id:"jpg"},
* {src:"test.mp3", id:"mp3"}
* ]);
* queue.remove("png"); // Single item by ID
* queue.remove("png", "test.jpg"); // Items as arguments. Mixed id and src.
* queue.remove(["test.png", "jpg"]); // Items in an Array. Mixed id and src.
*
* @method remove
* @param {String | Array} idsOrUrls* The id or ids to remove from this queue. You can pass an item, an array of
* items, or multiple items as arguments.
* @since 0.3.0
*/
p.remove = function (idsOrUrls) {
var args = null;
if (idsOrUrls && !Array.isArray(idsOrUrls)) {
args = [idsOrUrls];
} else if (idsOrUrls) {
args = idsOrUrls;
} else if (arguments.length > 0) {
return;
}
var itemsWereRemoved = false;
// Destroy everything
if (!args) {
this.close();
for (var n in this._loadItemsById) {
this._disposeItem(this._loadItemsById[n]);
}
this.init(this.preferXHR, this._basePath);
// Remove specific items
} else {
while (args.length) {
var item = args.pop();
var r = this.getResult(item);
//Remove from the main load Queue
for (i = this._loadQueue.length - 1; i >= 0; i--) {
loadItem = this._loadQueue[i].getItem();
if (loadItem.id == item || loadItem.src == item) {
this._loadQueue.splice(i, 1)[0].cancel();
break;
}
}
//Remove from the backup queue
for (i = this._loadQueueBackup.length - 1; i >= 0; i--) {
loadItem = this._loadQueueBackup[i].getItem();
if (loadItem.id == item || loadItem.src == item) {
this._loadQueueBackup.splice(i, 1)[0].cancel();
break;
}
}
if (r) {
this._disposeItem(this.getItem(item));
} else {
for (var i = this._currentLoads.length - 1; i >= 0; i--) {
var loadItem = this._currentLoads[i].getItem();
if (loadItem.id == item || loadItem.src == item) {
this._currentLoads.splice(i, 1)[0].cancel();
itemsWereRemoved = true;
break;
}
}
}
}
// If this was called during a load, try to load the next item.
if (itemsWereRemoved) {
this._loadNext();
}
}
};
/**
* Stops all open loads, destroys any loaded items, and resets the queue, so all items can
* be reloaded again by calling {{#crossLink "AbstractLoader/load"}}{{/crossLink}}. Items are not removed from the
* queue. To remove items use the {{#crossLink "LoadQueue/remove"}}{{/crossLink}} or
* {{#crossLink "LoadQueue/removeAll"}}{{/crossLink}} method.
* @method reset
* @since 0.3.0
*/
p.reset = function () {
this.close();
for (var n in this._loadItemsById) {
this._disposeItem(this._loadItemsById[n]);
}
//Reset the queue to its start state
var a = [];
for (var i = 0, l = this._loadQueueBackup.length; i < l; i++) {
a.push(this._loadQueueBackup[i].getItem());
}
this.loadManifest(a, false);
};
/**
* Register a plugin. Plugins can map to load types (sound, image, etc), or specific extensions (png, mp3, etc).
* Currently, only one plugin can exist per type/extension.
*
* When a plugin is installed, a getPreloadHandlers()
method will be called on it. For more information
* on this method, check out the {{#crossLink "SamplePlugin/getPreloadHandlers"}}{{/crossLink}} method in the
* {{#crossLink "SamplePlugin"}}{{/crossLink}} class.
*
* Before a file is loaded, a matching plugin has an opportunity to modify the load. If a `callback` is returned
* from the {{#crossLink "SamplePlugin/getPreloadHandlers"}}{{/crossLink}} method, it will be invoked first, and its
* result may cancel or modify the item. The callback method can also return a `completeHandler` to be fired when
* the file is loaded, or a `tag` object, which will manage the actual download. For more information on these
* methods, check out the {{#crossLink "SamplePlugin/preloadHandler"}}{{/crossLink}} and {{#crossLink "SamplePlugin/fileLoadHandler"}}{{/crossLink}}
* methods on the {{#crossLink "SamplePlugin"}}{{/crossLink}}.
*
* @method installPlugin
* @param {Function} plugin The plugin class to install.
*/
p.installPlugin = function (plugin) {
if (plugin == null) {
return;
}
if (plugin.getPreloadHandlers != null) {
this._plugins.push(plugin);
var map = plugin.getPreloadHandlers();
map.scope = plugin;
if (map.types != null) {
for (var i = 0, l = map.types.length; i < l; i++) {
this._typeCallbacks[map.types[i]] = map;
}
}
if (map.extensions != null) {
for (i = 0, l = map.extensions.length; i < l; i++) {
this._extensionCallbacks[map.extensions[i]] = map;
}
}
}
};
/**
* Set the maximum number of concurrent connections. Note that browsers and servers may have a built-in maximum
* number of open connections, so any additional connections may remain in a pending state until the browser
* opens the connection. When loading scripts using tags, and when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}}
* is `true`, only one script is loaded at a time due to browser limitations.
*
* Example
*
* var queue = new createjs.LoadQueue();
* queue.setMaxConnections(10); // Allow 10 concurrent loads
*
* @method setMaxConnections
* @param {Number} value The number of concurrent loads to allow. By default, only a single connection per LoadQueue
* is open at any time.
*/
p.setMaxConnections = function (value) {
this._maxConnections = value;
if (!this._paused && this._loadQueue.length > 0) {
this._loadNext();
}
};
/**
* Load a single file. To add multiple files at once, use the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
* method.
*
* Files are always appended to the current queue, so this method can be used multiple times to add files.
* To clear the queue first, use the {{#crossLink "AbstractLoader/close"}}{{/crossLink}} method.
* @method loadFile
* @param {LoadItem|Object|String} file The file object or path to load. A file can be either
*
* - A {{#crossLink "LoadItem"}}{{/crossLink}} instance
* - An object containing properties defined by {{#crossLink "LoadItem"}}{{/crossLink}}
* - OR A string path to a resource. Note that this kind of load item will be converted to a {{#crossLink "LoadItem"}}{{/crossLink}}
* in the background.
*
* @param {Boolean} [loadNow=true] Kick off an immediate load (true) or wait for a load call (false). The default
* value is true. If the queue is paused using {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}}, and the value is
* `true`, the queue will resume automatically.
* @param {String} [basePath] A base path that will be prepended to each file. The basePath argument overrides the
* path specified in the constructor. Note that if you load a manifest using a file of type {{#crossLink "Types/MANIFEST:property"}}{{/crossLink}},
* its files will NOT use the basePath parameter. The basePath parameter is deprecated.
* This parameter will be removed in a future version. Please either use the `basePath` parameter in the LoadQueue
* constructor, or a `path` property in a manifest definition.
*/
p.loadFile = function (file, loadNow, basePath) {
if (file == null) {
var event = new createjs.ErrorEvent("PRELOAD_NO_FILE");
this._sendError(event);
return;
}
this._addItem(file, null, basePath);
if (loadNow !== false) {
this.setPaused(false);
} else {
this.setPaused(true);
}
};
/**
* Load an array of files. To load a single file, use the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} method.
* The files in the manifest are requested in the same order, but may complete in a different order if the max
* connections are set above 1 using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}. Scripts will load
* in the right order as long as {{#crossLink "LoadQueue/maintainScriptOrder"}}{{/crossLink}} is true (which is
* default).
*
* Files are always appended to the current queue, so this method can be used multiple times to add files.
* To clear the queue first, use the {{#crossLink "AbstractLoader/close"}}{{/crossLink}} method.
* @method loadManifest
* @param {Array|String|Object} manifest An list of files to load. The loadManifest call supports four types of
* manifests:
*
* - A string path, which points to a manifest file, which is a JSON file that contains a "manifest" property,
* which defines the list of files to load, and can optionally contain a "path" property, which will be
* prepended to each file in the list.
* - An object which defines a "src", which is a JSON or JSONP file. A "callback" can be defined for JSONP
* file. The JSON/JSONP file should contain a "manifest" property, which defines the list of files to load,
* and can optionally contain a "path" property, which will be prepended to each file in the list.
* - An object which contains a "manifest" property, which defines the list of files to load, and can
* optionally contain a "path" property, which will be prepended to each file in the list.
* - An Array of files to load.
*
*
* Each "file" in a manifest can be either:
*
* - A {{#crossLink "LoadItem"}}{{/crossLink}} instance
* - An object containing properties defined by {{#crossLink "LoadItem"}}{{/crossLink}}
* - OR A string path to a resource. Note that this kind of load item will be converted to a {{#crossLink "LoadItem"}}{{/crossLink}}
* in the background.
*
*
* @param {Boolean} [loadNow=true] Kick off an immediate load (true) or wait for a load call (false). The default
* value is true. If the queue is paused using {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}} and this value is
* `true`, the queue will resume automatically.
* @param {String} [basePath] A base path that will be prepended to each file. The basePath argument overrides the
* path specified in the constructor. Note that if you load a manifest using a file of type {{#crossLink "LoadQueue/MANIFEST:property"}}{{/crossLink}},
* its files will NOT use the basePath parameter. The basePath parameter is deprecated.
* This parameter will be removed in a future version. Please either use the `basePath` parameter in the LoadQueue
* constructor, or a `path` property in a manifest definition.
*/
p.loadManifest = function (manifest, loadNow, basePath) {
var fileList = null;
var path = null;
// Array-based list of items
if (Array.isArray(manifest)) {
if (manifest.length == 0) {
var event = new createjs.ErrorEvent("PRELOAD_MANIFEST_EMPTY");
this._sendError(event);
return;
}
fileList = manifest;
// String-based. Only file manifests can be specified this way. Any other types will cause an error when loaded.
} else if (typeof(manifest) === "string") {
fileList = [
{
src: manifest,
type: s.MANIFEST
}
];
} else if (typeof(manifest) == "object") {
// An object that defines a manifest path
if (manifest.src !== undefined) {
if (manifest.type == null) {
manifest.type = s.MANIFEST;
} else if (manifest.type != s.MANIFEST) {
var event = new createjs.ErrorEvent("PRELOAD_MANIFEST_TYPE");
this._sendError(event);
}
fileList = [manifest];
// An object that defines a manifest
} else if (manifest.manifest !== undefined) {
fileList = manifest.manifest;
path = manifest.path;
}
// Unsupported. This will throw an error.
} else {
var event = new createjs.ErrorEvent("PRELOAD_MANIFEST_NULL");
this._sendError(event);
return;
}
for (var i = 0, l = fileList.length; i < l; i++) {
this._addItem(fileList[i], path, basePath);
}
if (loadNow !== false) {
this.setPaused(false);
} else {
this.setPaused(true);
}
};
/**
* Start a LoadQueue that was created, but not automatically started.
* @method load
*/
p.load = function () {
this.setPaused(false);
};
/**
* Look up a {{#crossLink "LoadItem"}}{{/crossLink}} using either the "id" or "src" that was specified when loading it. Note that if no "id" was
* supplied with the load item, the ID will be the "src", including a `path` property defined by a manifest. The
* `basePath` will not be part of the ID.
* @method getItem
* @param {String} value The id
or src
of the load item.
* @return {Object} The load item that was initially requested using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. This object is also returned via the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}}
* event as the `item` parameter.
*/
p.getItem = function (value) {
return this._loadItemsById[value] || this._loadItemsBySrc[value];
};
/**
* Look up a loaded result using either the "id" or "src" that was specified when loading it. Note that if no "id"
* was supplied with the load item, the ID will be the "src", including a `path` property defined by a manifest. The
* `basePath` will not be part of the ID.
* @method getResult
* @param {String} value The id
or src
of the load item.
* @param {Boolean} [rawResult=false] Return a raw result instead of a formatted result. This applies to content
* loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
* returned instead.
* @return {Object} A result object containing the content that was loaded, such as:
*
* - An image tag (<image />) for images
* - A script tag for JavaScript (<script />). Note that scripts are automatically added to the HTML
* DOM.
* - A style tag for CSS (<style /> or <link >)
* - Raw text for TEXT
* - A formatted JavaScript object defined by JSON
* - An XML document
* - A binary arraybuffer loaded by XHR
* - An audio tag (<audio >) for HTML audio. Note that it is recommended to use SoundJS APIs to play
* loaded audio. Specifically, audio loaded by Flash and WebAudio will return a loader object using this method
* which can not be used to play audio back.
*
* This object is also returned via the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event as the 'item`
* parameter. Note that if a raw result is requested, but not found, the result will be returned instead.
*/
p.getResult = function (value, rawResult) {
var item = this._loadItemsById[value] || this._loadItemsBySrc[value];
if (item == null) {
return null;
}
var id = item.id;
if (rawResult && this._loadedRawResults[id]) {
return this._loadedRawResults[id];
}
return this._loadedResults[id];
};
/**
* Generate an list of items loaded by this queue.
* @method getItems
* @param {Boolean} loaded Determines if only items that have been loaded should be returned. If false, in-progress
* and failed load items will also be included.
* @returns {Array} A list of objects that have been loaded. Each item includes the {{#crossLink "LoadItem"}}{{/crossLink}},
* result, and rawResult.
* @since 0.6.0
*/
p.getItems = function (loaded) {
var arr = [];
for (var n in this._loadItemsById) {
var item = this._loadItemsById[n];
var result = this.getResult(n);
if (loaded === true && result == null) {
continue;
}
arr.push({
item: item,
result: result,
rawResult: this.getResult(n, true)
});
}
return arr;
};
/**
* Pause or resume the current load. Active loads will not be cancelled, but the next items in the queue will not
* be processed when active loads complete. LoadQueues are not paused by default.
*
* Note that if new items are added to the queue using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
* {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}, a paused queue will be resumed, unless the `loadNow`
* argument is `false`.
* @method setPaused
* @param {Boolean} value Whether the queue should be paused or not.
*/
p.setPaused = function (value) {
this._paused = value;
if (!this._paused) {
this._loadNext();
}
};
/**
* Close the active queue. Closing a queue completely empties the queue, and prevents any remaining items from
* starting to download. Note that currently any active loads will remain open, and events may be processed.
*
* To stop and restart a queue, use the {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}} method instead.
* @method close
*/
p.close = function () {
while (this._currentLoads.length) {
this._currentLoads.pop().cancel();
}
this._scriptOrder.length = 0;
this._loadedScripts.length = 0;
this.loadStartWasDispatched = false;
this._itemCount = 0;
this._lastProgress = NaN;
};
// protected methods
/**
* Add an item to the queue. Items are formatted into a usable object containing all the properties necessary to
* load the content. The load queue is populated with the loader instance that handles preloading, and not the load
* item that was passed in by the user. To look up the load item by id or src, use the {{#crossLink "LoadQueue.getItem"}}{{/crossLink}}
* method.
* @method _addItem
* @param {String|Object} value The item to add to the queue.
* @param {String} [path] An optional path prepended to the `src`. The path will only be prepended if the src is
* relative, and does not start with a protocol such as `http://`, or a path like `../`. If the LoadQueue was
* provided a {{#crossLink "_basePath"}}{{/crossLink}}, then it will optionally be prepended after.
* @param {String} [basePath] DeprecatedAn optional basePath passed into a {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} call. This parameter will be removed in a future tagged
* version.
* @private
*/
p._addItem = function (value, path, basePath) {
var item = this._createLoadItem(value, path, basePath); // basePath and manifest path are added to the src.
if (item == null) {
return;
} // Sometimes plugins or types should be skipped.
var loader = this._createLoader(item);
if (loader != null) {
if ("plugins" in loader) {
loader.plugins = this._plugins;
}
item._loader = loader;
this._loadQueue.push(loader);
this._loadQueueBackup.push(loader);
this._numItems++;
this._updateProgress();
// Only worry about script order when using XHR to load scripts. Tags are only loading one at a time.
if ((this.maintainScriptOrder
&& item.type == createjs.Types.JAVASCRIPT
//&& loader instanceof createjs.XHRLoader //NOTE: Have to track all JS files this way
)
|| item.maintainOrder === true) {
this._scriptOrder.push(item);
this._loadedScripts.push(null);
}
}
};
/**
* Create a refined {{#crossLink "LoadItem"}}{{/crossLink}}, which contains all the required properties. The type of
* item is determined by browser support, requirements based on the file type, and developer settings. For example,
* XHR is only used for file types that support it in new browsers.
*
* Before the item is returned, any plugins registered to handle the type or extension will be fired, which may
* alter the load item.
* @method _createLoadItem
* @param {String | Object | HTMLAudioElement | HTMLImageElement} value The item that needs to be preloaded.
* @param {String} [path] A path to prepend to the item's source. Sources beginning with http:// or similar will
* not receive a path. Since PreloadJS 0.4.1, the src will be modified to include the `path` and {{#crossLink "LoadQueue/_basePath:property"}}{{/crossLink}}
* when it is added.
* @param {String} [basePath] Deprectated A base path to prepend to the items source in addition to
* the path argument.
* @return {Object} The loader instance that will be used.
* @private
*/
p._createLoadItem = function (value, path, basePath) {
var item = createjs.LoadItem.create(value);
if (item == null) {
return null;
}
var bp = ""; // Store the generated basePath
var useBasePath = basePath || this._basePath;
if (item.src instanceof Object) {
if (!item.type) {
return null;
} // the the src is an object, type is required to pass off to plugin
if (path) {
bp = path;
var pathMatch = createjs.URLUtils.parseURI(path);
// Also append basePath
if (useBasePath != null && !pathMatch.absolute && !pathMatch.relative) {
bp = useBasePath + bp;
}
} else if (useBasePath != null) {
bp = useBasePath;
}
} else {
// Determine Extension, etc.
var match = createjs.URLUtils.parseURI(item.src);
if (match.extension) {
item.ext = match.extension;
}
if (item.type == null) {
item.type = createjs.RequestUtils.getTypeByExtension(item.ext);
}
// Inject path & basePath
var autoId = item.src;
if (!match.absolute && !match.relative) {
if (path) {
bp = path;
var pathMatch = createjs.URLUtils.parseURI(path);
autoId = path + autoId;
// Also append basePath
if (useBasePath != null && !pathMatch.absolute && !pathMatch.relative) {
bp = useBasePath + bp;
}
} else if (useBasePath != null) {
bp = useBasePath;
}
}
item.src = bp + item.src;
}
item.path = bp;
// If there's no id, set one now.
if (item.id === undefined || item.id === null || item.id === "") {
item.id = autoId;
}
// Give plugins a chance to modify the loadItem:
var customHandler = this._typeCallbacks[item.type] || this._extensionCallbacks[item.ext];
if (customHandler) {
// Plugins are now passed both the full source, as well as a combined path+basePath (appropriately)
var result = customHandler.callback.call(customHandler.scope, item, this);
// The plugin will handle the load, or has canceled it. Ignore it.
if (result === false) {
return null;
// Load as normal:
} else if (result === true) {
// Do Nothing
// Result is a loader class:
} else if (result != null) {
item._loader = result;
}
// Update the extension in case the type changed:
match = createjs.URLUtils.parseURI(item.src);
if (match.extension != null) {
item.ext = match.extension;
}
}
// Store the item for lookup. This also helps clean-up later.
this._loadItemsById[item.id] = item;
this._loadItemsBySrc[item.src] = item;
if (item.crossOrigin == null) {
item.crossOrigin = this._crossOrigin;
}
return item;
};
/**
* Create a loader for a load item.
* @method _createLoader
* @param {Object} item A formatted load item that can be used to generate a loader.
* @return {AbstractLoader} A loader that can be used to load content.
* @private
*/
p._createLoader = function (item) {
if (item._loader != null) { // A plugin already specified a loader
return item._loader;
}
// Initially, try and use the provided/supported XHR mode:
var preferXHR = this.preferXHR;
for (var i = 0; i < this._availableLoaders.length; i++) {
var loader = this._availableLoaders[i];
if (loader && loader.canLoadItem(item)) {
return new loader(item, preferXHR);
}
}
// TODO: Log error (requires createjs.log)
return null;
};
/**
* Load the next item in the queue. If the queue is empty (all items have been loaded), then the complete event
* is processed. The queue will "fill up" any empty slots, up to the max connection specified using
* {{#crossLink "LoadQueue.setMaxConnections"}}{{/crossLink}} method. The only exception is scripts that are loaded
* using tags, which have to be loaded one at a time to maintain load order.
* @method _loadNext
* @private
*/
p._loadNext = function () {
if (this._paused) {
return;
}
// Only dispatch loadstart event when the first file is loaded.
if (!this._loadStartWasDispatched) {
this._sendLoadStart();
this._loadStartWasDispatched = true;
}
// The queue has completed.
if (this._numItems == this._numItemsLoaded) {
this.loaded = true;
this._sendComplete();
// Load the next queue, if it has been defined.
if (this.next && this.next.load) {
this.next.load();
}
} else {
this.loaded = false;
}
// Must iterate forwards to load in the right order.
for (var i = 0; i < this._loadQueue.length; i++) {
if (this._currentLoads.length >= this._maxConnections) {
break;
}
var loader = this._loadQueue[i];
// Determine if we should be only loading one tag-script at a time:
// Note: maintainOrder items don't do anything here because we can hold onto their loaded value
if (!this._canStartLoad(loader)) {
continue;
}
this._loadQueue.splice(i, 1);
i--;
this._loadItem(loader);
}
};
/**
* Begin loading an item. Event listeners are not added to the loaders until the load starts.
* @method _loadItem
* @param {AbstractLoader} loader The loader instance to start. Currently, this will be an XHRLoader or TagLoader.
* @private
*/
p._loadItem = function (loader) {
loader.on("fileload", this._handleFileLoad, this);
loader.on("progress", this._handleProgress, this);
loader.on("complete", this._handleFileComplete, this);
loader.on("error", this._handleError, this);
loader.on("fileerror", this._handleFileError, this);
this._currentLoads.push(loader);
this._sendFileStart(loader.getItem());
loader.load();
};
/**
* The callback that is fired when a loader loads a file. This enables loaders like {{#crossLink "ManifestLoader"}}{{/crossLink}}
* to maintain internal queues, but for this queue to dispatch the {{#crossLink "fileload:event"}}{{/crossLink}}
* events.
* @param {Event} event The {{#crossLink "AbstractLoader/fileload:event"}}{{/crossLink}} event from the loader.
* @private
* @since 0.6.0
*/
p._handleFileLoad = function (event) {
event.target = null;
this.dispatchEvent(event);
};
/**
* The callback that is fired when a loader encounters an error from an internal file load operation. This enables
* loaders like M
* @param event
* @private
*/
p._handleFileError = function (event) {
var newEvent = new createjs.ErrorEvent("FILE_LOAD_ERROR", null, event.item);
this._sendError(newEvent);
};
/**
* The callback that is fired when a loader encounters an error. The queue will continue loading unless {{#crossLink "LoadQueue/stopOnError:property"}}{{/crossLink}}
* is set to `true`.
* @method _handleError
* @param {ErrorEvent} event The error event, containing relevant error information.
* @private
*/
p._handleError = function (event) {
var loader = event.target;
this._numItemsLoaded++;
this._finishOrderedItem(loader, true);
this._updateProgress();
var newEvent = new createjs.ErrorEvent("FILE_LOAD_ERROR", null, loader.getItem());
// TODO: Propagate actual error message.
this._sendError(newEvent);
if (!this.stopOnError) {
this._removeLoadItem(loader);
this._cleanLoadItem(loader);
this._loadNext();
} else {
this.setPaused(true);
}
};
/**
* An item has finished loading. We can assume that it is totally loaded, has been parsed for immediate use, and
* is available as the "result" property on the load item. The raw text result for a parsed item (such as JSON, XML,
* CSS, JavaScript, etc) is available as the "rawResult" property, and can also be looked up using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}.
* @method _handleFileComplete
* @param {Event} event The event object from the loader.
* @private
*/
p._handleFileComplete = function (event) {
var loader = event.target;
var item = loader.getItem();
var result = loader.getResult();
this._loadedResults[item.id] = result;
var rawResult = loader.getResult(true);
if (rawResult != null && rawResult !== result) {
this._loadedRawResults[item.id] = rawResult;
}
this._saveLoadedItems(loader);
// Remove the load item
this._removeLoadItem(loader);
if (!this._finishOrderedItem(loader)) {
// The item was NOT managed, so process it now
this._processFinishedLoad(item, loader);
}
// Clean up the load item
this._cleanLoadItem(loader);
};
/**
* Some loaders might load additional content, other than the item they were passed (such as {{#crossLink "ManifestLoader"}}{{/crossLink}}).
* Any items exposed by the loader using {{#crossLink "AbstractLoader/getLoadItems"}}{{/crossLink}} are added to the
* LoadQueue's look-ups, including {{#crossLink "getItem"}}{{/crossLink}} and {{#crossLink "getResult"}}{{/crossLink}}
* methods.
* @method _saveLoadedItems
* @param {AbstractLoader} loader
* @protected
* @since 0.6.0
*/
p._saveLoadedItems = function (loader) {
// TODO: Not sure how to handle this. Would be nice to expose the items.
// Loaders may load sub-items. This adds them to this queue
var list = loader.getLoadedItems();
if (list === null) {
return;
}
for (var i = 0; i < list.length; i++) {
var item = list[i].item;
// Store item lookups
this._loadItemsBySrc[item.src] = item;
this._loadItemsById[item.id] = item;
// Store loaded content
this._loadedResults[item.id] = list[i].result;
this._loadedRawResults[item.id] = list[i].rawResult;
}
};
/**
* Flag an item as finished. If the item's order is being managed, then ensure that it is allowed to finish, and if
* so, trigger prior items to trigger as well.
* @method _finishOrderedItem
* @param {AbstractLoader} loader
* @param {Boolean} loadFailed
* @return {Boolean} If the item's order is being managed. This allows the caller to take an alternate
* behaviour if it is.
* @private
*/
p._finishOrderedItem = function (loader, loadFailed) {
var item = loader.getItem();
if ((this.maintainScriptOrder && item.type == createjs.Types.JAVASCRIPT)
|| item.maintainOrder) {
//TODO: Evaluate removal of the _currentlyLoadingScript
if (loader instanceof createjs.JavaScriptLoader) {
this._currentlyLoadingScript = false;
}
var index = createjs.indexOf(this._scriptOrder, item);
if (index == -1) {
return false;
} // This loader no longer exists
this._loadedScripts[index] = (loadFailed === true) ? true : item;
this._checkScriptLoadOrder();
return true;
}
return false;
};
/**
* Ensure the scripts load and dispatch in the correct order. When using XHR, scripts are stored in an array in the
* order they were added, but with a "null" value. When they are completed, the value is set to the load item,
* and then when they are processed and dispatched, the value is set to `true`. This method simply
* iterates the array, and ensures that any loaded items that are not preceded by a `null` value are
* dispatched.
* @method _checkScriptLoadOrder
* @private
*/
p._checkScriptLoadOrder = function () {
var l = this._loadedScripts.length;
for (var i = 0; i < l; i++) {
var item = this._loadedScripts[i];
if (item === null) {
break;
} // This is still loading. Do not process further.
if (item === true) {
continue;
} // This has completed, and been processed. Move on.
var loadItem = this._loadedResults[item.id];
if (item.type == createjs.Types.JAVASCRIPT) {
// Append script tags to the head automatically.
createjs.DomUtils.appendToHead(loadItem);
}
var loader = item._loader;
this._processFinishedLoad(item, loader);
this._loadedScripts[i] = true;
}
};
/**
* A file has completed loading, and the LoadQueue can move on. This triggers the complete event, and kick-starts
* the next item.
* @method _processFinishedLoad
* @param {LoadItem|Object} item
* @param {AbstractLoader} loader
* @protected
*/
p._processFinishedLoad = function (item, loader) {
this._numItemsLoaded++;
// Since LoadQueue needs maintain order, we can't append scripts in the loader.
// So we do it here instead. Or in _checkScriptLoadOrder();
if (!this.maintainScriptOrder && item.type == createjs.Types.JAVASCRIPT) {
var tag = loader.getTag();
createjs.DomUtils.appendToHead(tag);
}
this._updateProgress();
this._sendFileComplete(item, loader);
this._loadNext();
};
/**
* Ensure items with `maintainOrder=true` that are before the specified item have loaded. This only applies to
* JavaScript items that are being loaded with a TagLoader, since they have to be loaded and completed before
* the script can even be started, since it exist in the DOM while loading.
* @method _canStartLoad
* @param {AbstractLoader} loader The loader for the item
* @return {Boolean} Whether the item can start a load or not.
* @private
*/
p._canStartLoad = function (loader) {
if (!this.maintainScriptOrder || loader.preferXHR) {
return true;
}
var item = loader.getItem();
if (item.type != createjs.Types.JAVASCRIPT) {
return true;
}
if (this._currentlyLoadingScript) {
return false;
}
var index = this._scriptOrder.indexOf(item);
var i = 0;
while (i < index) {
var checkItem = this._loadedScripts[i];
if (checkItem == null) {
return false;
}
i++;
}
this._currentlyLoadingScript = true;
return true;
};
/**
* A load item is completed or was canceled, and needs to be removed from the LoadQueue.
* @method _removeLoadItem
* @param {AbstractLoader} loader A loader instance to remove.
* @private
*/
p._removeLoadItem = function (loader) {
var l = this._currentLoads.length;
for (var i = 0; i < l; i++) {
if (this._currentLoads[i] == loader) {
this._currentLoads.splice(i, 1);
break;
}
}
};
/**
* Remove unneeded references from a loader.
*
* @param loader
* @private
*/
p._cleanLoadItem = function(loader) {
var item = loader.getItem();
if (item) {
delete item._loader;
}
}
/**
* An item has dispatched progress. Propagate that progress, and update the LoadQueue's overall progress.
* @method _handleProgress
* @param {ProgressEvent} event The progress event from the item.
* @private
*/
p._handleProgress = function (event) {
var loader = event.target;
this._sendFileProgress(loader.getItem(), loader.progress);
this._updateProgress();
};
/**
* Overall progress has changed, so determine the new progress amount and dispatch it. This changes any time an
* item dispatches progress or completes. Note that since we don't always know the actual filesize of items before
* they are loaded. In this case, we define a "slot" for each item (1 item in 10 would get 10%), and then append
* loaded progress on top of the already-loaded items.
*
* For example, if 5/10 items have loaded, and item 6 is 20% loaded, the total progress would be:
*
* - 5/10 of the items in the queue (50%)
* - plus 20% of item 6's slot (2%)
* - equals 52%
*
* @method _updateProgress
* @private
*/
p._updateProgress = function () {
var loaded = this._numItemsLoaded / this._numItems; // Fully Loaded Progress
var remaining = this._numItems - this._numItemsLoaded;
if (remaining > 0) {
var chunk = 0;
for (var i = 0, l = this._currentLoads.length; i < l; i++) {
chunk += this._currentLoads[i].progress;
}
loaded += (chunk / remaining) * (remaining / this._numItems);
}
if (this._lastProgress != loaded) {
this._sendProgress(loaded);
this._lastProgress = loaded;
}
};
/**
* Clean out item results, to free them from memory. Mainly, the loaded item and results are cleared from internal
* hashes.
* @method _disposeItem
* @param {LoadItem|Object} item The item that was passed in for preloading.
* @private
*/
p._disposeItem = function (item) {
delete this._loadedResults[item.id];
delete this._loadedRawResults[item.id];
delete this._loadItemsById[item.id];
delete this._loadItemsBySrc[item.src];
};
/**
* Dispatch a "fileprogress" {{#crossLink "Event"}}{{/crossLink}}. Please see the LoadQueue {{#crossLink "LoadQueue/fileprogress:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendFileProgress
* @param {LoadItem|Object} item The item that is being loaded.
* @param {Number} progress The amount the item has been loaded (between 0 and 1).
* @protected
*/
p._sendFileProgress = function (item, progress) {
if (this._isCanceled() || this._paused) {
return;
}
if (!this.hasEventListener("fileprogress")) {
return;
}
//LM: Rework ProgressEvent to support this?
var event = new createjs.Event("fileprogress");
event.progress = progress;
event.loaded = progress;
event.total = 1;
event.item = item;
this.dispatchEvent(event);
};
/**
* Dispatch a fileload {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event for
* details on the event payload.
* @method _sendFileComplete
* @param {LoadItemObject} item The item that is being loaded.
* @param {AbstractLoader} loader
* @protected
*/
p._sendFileComplete = function (item, loader) {
if (this._isCanceled() || this._paused) {
return;
}
var event = new createjs.Event("fileload");
event.loader = loader;
event.item = item;
event.result = this._loadedResults[item.id];
event.rawResult = this._loadedRawResults[item.id];
// This calls a handler specified on the actual load item. Currently, the SoundJS plugin uses this.
if (item.completeHandler) {
item.completeHandler(event);
}
this.hasEventListener("fileload") && this.dispatchEvent(event);
};
/**
* Dispatch a filestart {{#crossLink "Event"}}{{/crossLink}} immediately before a file starts to load. Please see
* the {{#crossLink "LoadQueue/filestart:event"}}{{/crossLink}} event for details on the event payload.
* @method _sendFileStart
* @param {LoadItem|Object} item The item that is being loaded.
* @protected
*/
p._sendFileStart = function (item) {
var event = new createjs.Event("filestart");
event.item = item;
this.hasEventListener("filestart") && this.dispatchEvent(event);
};
p.toString = function () {
return "[PreloadJS LoadQueue]";
};
createjs.LoadQueue = createjs.promote(LoadQueue, "AbstractLoader");
}());
//##############################################################################
// TextLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for Text files.
* @class TextLoader
* @param {LoadItem|Object} loadItem
* @extends AbstractLoader
* @constructor
*/
function TextLoader(loadItem) {
this.AbstractLoader_constructor(loadItem, true, createjs.Types.TEXT);
};
var p = createjs.extend(TextLoader, createjs.AbstractLoader);
var s = TextLoader;
// static methods
/**
* Determines if the loader can load a specific item. This loader loads items that are of type {{#crossLink "Types/TEXT:property"}}{{/crossLink}},
* but is also the default loader if a file type can not be determined.
* @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.Types.TEXT;
};
createjs.TextLoader = createjs.promote(TextLoader, "AbstractLoader");
}());
//##############################################################################
// BinaryLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for binary files. This is useful for loading web audio, or content that requires an ArrayBuffer.
* @class BinaryLoader
* @param {LoadItem|Object} loadItem
* @extends AbstractLoader
* @constructor
*/
function BinaryLoader(loadItem) {
this.AbstractLoader_constructor(loadItem, true, createjs.Types.BINARY);
this.on("initialize", this._updateXHR, this);
};
var p = createjs.extend(BinaryLoader, createjs.AbstractLoader);
var s = BinaryLoader;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/BINARY: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.Types.BINARY;
};
// private methods
/**
* Before the item loads, set the response type to "arraybuffer"
* @property _updateXHR
* @param {Event} event
* @private
*/
p._updateXHR = function (event) {
event.loader.setResponseType("arraybuffer");
};
createjs.BinaryLoader = createjs.promote(BinaryLoader, "AbstractLoader");
}());
//##############################################################################
// CSSLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for CSS files.
* @class CSSLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @extends AbstractLoader
* @constructor
*/
function CSSLoader(loadItem, preferXHR) {
this.AbstractLoader_constructor(loadItem, preferXHR, createjs.Types.CSS);
// public properties
this.resultFormatter = this._formatResult;
// protected properties
this._tagSrcAttribute = "href";
if (preferXHR) {
this._tag = createjs.Elements.style();
} else {
this._tag = createjs.Elements.link();
}
this._tag.rel = "stylesheet";
this._tag.type = "text/css";
};
var p = createjs.extend(CSSLoader, createjs.AbstractLoader);
var s = CSSLoader;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/CSS: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.Types.CSS;
};
// protected methods
/**
* The result formatter for CSS files.
* @method _formatResult
* @param {AbstractLoader} loader
* @returns {HTMLLinkElement|HTMLStyleElement}
* @private
*/
p._formatResult = function (loader) {
if (this._preferXHR) {
var tag = loader.getTag();
if (tag.styleSheet) { // IE
tag.styleSheet.cssText = loader.getResult(true);
} else {
var textNode = createjs.Elements.text(loader.getResult(true));
tag.appendChild(textNode);
}
} else {
tag = this._tag;
}
createjs.DomUtils.appendToHead(tag);
return tag;
};
createjs.CSSLoader = createjs.promote(CSSLoader, "AbstractLoader");
}());
//##############################################################################
// FontLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor:
/**
* A loader that handles font files, CSS definitions, and CSS paths. FontLoader doesn't actually preload fonts
* themselves, but rather generates CSS definitions, and then tests the size changes on an HTML5 Canvas element.
*
* Note that FontLoader does not support tag-based loading due to the requirement that CSS be read to determine the
* font definitions to test for.
* @class FontLoader
* @param {LoadItem|object|string} loadItem The item to be loaded.
* @extends AbstractLoader
* @constructor
**/
function FontLoader(loadItem, preferXHR) {
this.AbstractLoader_constructor(loadItem, preferXHR, loadItem.type);
// private properties:
/**
* A lookup of font faces to load.
* @property _faces
* @protected
* @type Object
**/
this._faces = {};
/**
* A list of font faces currently being "watched". Watched fonts will be tested on a regular interval, and be
* removed from this list when they are complete.
* @oroperty _watched
* @type {Array}
* @protected
*/
this._watched = [];
/**
* A count of the total font faces to load.
* @property _count
* @type {number}
* @protected
* @default 0
*/
this._count = 0;
/**
* The interval for checking if fonts have been loaded.
* @property _watchInterval
* @type {Number}
* @protected
*/
this._watchInterval = null;
/**
* The timeout for determining if a font can't be loaded. Uses the LoadItem {{#crossLink "LoadImte/timeout:property"}}{{/crossLink}}
* value.
* @property _loadTimeout
* @type {Number}
* @protected
*/
this._loadTimeout = null;
/**
* Determines if generated CSS should be injected into the document.
* @property _injectCSS
* @type {boolean}
* @protected
*/
this._injectCSS = (loadItem.injectCSS === undefined) ? true : loadItem.injectCSS;
this.dispatchEvent("initialize");
}
var p = createjs.extend(FontLoader, createjs.AbstractLoader);
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/FONT: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
*/
FontLoader.canLoadItem = function (item) {
return item.type == createjs.Types.FONT || item.type == createjs.Types.FONTCSS;
};
// static properties:
/**
* Sample text used by the FontLoader to determine if the font has been loaded. The sample text size is compared
* to the loaded font size, and a change indicates that the font has completed.
* @property sampleText
* @type {String}
* @default abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ
* @static
* @private
*/
FontLoader.sampleText = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**
* The canvas context used to test the font size. Note that this currently requires an HTML DOM.
* @property _ctx
* @type {CanvasRenderingContext2D}
* @static
* @private
*/
FontLoader._ctx = document.createElement("canvas").getContext("2d"); // TODO: Consider a method to do this like EaselJS Stage has.
/**
* A list of reference fonts to test. Multiple faces are tested to address the rare case of a loaded font being the
* exact same dimensions as the test font.
* @property _referenceFonts
* @type {Array}
* @default ["serif", "monospace"]
* @private
*/
FontLoader._referenceFonts = ["serif","monospace"];
/**
* A regular expression that pulls out possible style values from the font name.
*
* - This includes font names that include thin, normal, book, regular, medium, black, and heavy (such as
* "Arial Black")
* - Weight modifiers including extra, ultra, semi, demi, light, and bold (such as "WorkSans SemiBold")
*
*
* Weight descriptions map to font weight values by default using the following (from
* ):
*
* - 100 - Thin
* - 200 - Extra Light, Ultra Light
* - 300 - Light, Semi Light, Demi Light
* - 400 - Normal, Book, Regular
* - 500 - Medium
* - 600 - Semi Bold, Demi Bold
* - 700 - Bold
* - 800 - Extra Bold, Ultra Bold
* - 900 - Black, Heavy
*
* @property WEIGHT_REGEX
* @type {RegExp}
* @static
*/
FontLoader.WEIGHT_REGEX = /[- ._]*(thin|normal|book|regular|medium|black|heavy|[1-9]00|(?:extra|ultra|semi|demi)?[- ._]*(?:light|bold))[- ._]*/ig;
/**
* A regular expression that pulls out possible style values from the font name. These include "italic"
* and "oblique".
* @property STYLE_REGEX
* @type {RegExp}
* @static
*/
FontLoader.STYLE_REGEX = /[- ._]*(italic|oblique)[- ._]*/ig;
/**
* A lookup of font types for generating a CSS definition. For example, TTF fonts requires a "truetype" type.
* @property FONT_FORMAT
* @type {Object}
* @static
*/
FontLoader.FONT_FORMAT = {woff2:"woff2", woff:"woff", ttf:"truetype", otf:"truetype"};
/**
* A lookup of font weights based on a name. These values are from .
* @property FONT_WEIGHT
* @type {Object}
* @static
*/
FontLoader.FONT_WEIGHT = {thin:100, extralight:200, ultralight:200, light:300, semilight:300, demilight:300, book:"normal", regular:"normal", semibold:600, demibold:600, extrabold:800, ultrabold:800, black:900, heavy:900};
/**
* The frequency in milliseconds to check for loaded fonts.
* @property WATCH_DURATION
* @type {number}
* @default 10
* @static
*/
FontLoader.WATCH_DURATION = 10;
// public methods:
p.load = function() {
if (this.type == createjs.Types.FONTCSS) {
var loaded = this._watchCSS();
// If the CSS is not ready, it will create a request, which AbstractLoader can handle.
if (!loaded) {
this.AbstractLoader_load();
return;
}
} else if (this._item.src instanceof Array) {
this._watchFontArray();
} else {
var def = this._defFromSrc(this._item.src);
this._watchFont(def);
this._injectStyleTag(this._cssFromDef(def));
}
this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
this.dispatchEvent("loadstart");
};
/**
* The font load has timed out. This is called via a setTimeout
.
* callback.
* @method _handleTimeout
* @protected
*/
p._handleTimeout = function () {
this._stopWatching();
this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT"));
};
// WatchCSS does the work for us, and provides a modified src.
p._createRequest = function() {
return this._request;
};
// Events come from the internal XHR loader.
p.handleEvent = function (event) {
switch (event.type) {
case "complete":
this._rawResult = event.target._response;
this._result = true;
this._parseCSS(this._rawResult);
break;
case "error":
this._stopWatching();
this.AbstractLoader_handleEvent(event);
break;
}
};
// private methods:
/**
* Determine if the provided CSS is a string definition, CSS HTML element, or a CSS file URI. Depending on the
* format, the CSS will be parsed, or loaded.
* @method _watchCSS
* @returns {boolean} Whether or not the CSS is ready
* @protected
*/
p._watchCSS = function() {
var src = this._item.src;
// An HTMLElement was passed in. Just use it.
if (src instanceof HTMLStyleElement) {
if (this._injectCSS && !src.parentNode) { (document.head || document.getElementsByTagName('head')[0]).appendChild(src); }
this._injectCSS = false;
src = "\n"+src.textContent;
}
// A CSS string was passed in. Parse and use it
if (src.search(/\n|\r|@font-face/i) !== -1) { // css string.
this._parseCSS(src);
return true;
}
// Load a CSS Path. Note that we CAN NOT load it without XHR because we need to read the CSS definition
this._request = new createjs.XHRRequest(this._item);
return false;
};
/**
* Parse a CSS string to determine the fonts to load.
* @method _parseCSS
* @param {String} css The CSS string to parse
* @protected
*/
p._parseCSS = function(css) {
var regex = /@font-face\s*\{([^}]+)}/g
while (true) {
var result = regex.exec(css);
if (!result) { break; }
this._watchFont(this._parseFontFace(result[1]));
}
this._injectStyleTag(css);
};
/**
* The provided fonts were an array of object or string definitions. Parse them, and inject any that are ready.
* @method _watchFontArray
* @protected
*/
p._watchFontArray = function() {
var arr = this._item.src, css = "", def;
for (var i=arr.length-1; i>=0; i--) {
var o = arr[i];
if (typeof o === "string") { def = this._defFromSrc(o) }
else { def = this._defFromObj(o); }
this._watchFont(def);
css += this._cssFromDef(def)+"\n";
}
this._injectStyleTag(css);
};
/**
* Inject any style definitions into the document head. This is necessary when the definition is just a string or
* object definition in order for the styles to be applied to the document. If the loaded fonts are already HTML CSS
* elements, they don't need to be appended again.
* @method _injectStyleTag
* @param {String} css The CSS string content to be appended to the
* @protected
*/
p._injectStyleTag = function(css) {
if (!this._injectCSS) { return; }
var head = document.head || document.getElementsByTagName('head')[0];
var styleTag = document.createElement("style");
styleTag.type = "text/css";
if (styleTag.styleSheet){
styleTag.styleSheet.cssText = css;
} else {
styleTag.appendChild(document.createTextNode(css));
}
head.appendChild(styleTag);
};
/**
* Determine the font face from a CSS definition.
* @method _parseFontFace
* @param {String} str The CSS string definition
* @protected
* @return {String} A modified CSS object containing family name, src, style, and weight
*/
p._parseFontFace = function(str) {
var family = this._getCSSValue(str, "font-family"), src = this._getCSSValue(str, "src");
if (!family || !src) { return null; }
return this._defFromObj({
family: family,
src: src,
style: this._getCSSValue(str, "font-style"),
weight: this._getCSSValue(str, "font-weight")
});
};
/**
* Add a font to the list of fonts currently being watched. If the font is already watched or loaded, it won't be
* added again.
* @method _watchFont
* @param {Object} def The font definition
* @protected
*/
p._watchFont = function(def) {
if (!def || this._faces[def.id]) { return; }
this._faces[def.id] = def;
this._watched.push(def);
this._count++;
this._calculateReferenceSizes(def);
this._startWatching();
};
/**
* Create a interval to check for loaded fonts. Only one interval is used for all fonts. The fonts are checked based
* on the {{#crossLink "FontLoader/WATCH_DURATION:property"}}{{/crossLink}}.
* @method _startWatching
* @protected
*/
p._startWatching = function() {
if (this._watchInterval != null) { return; }
this._watchInterval = setInterval(createjs.proxy(this._watch, this), FontLoader.WATCH_DURATION);
};
/**
* Clear the interval used to check fonts. This happens when all fonts are loaded, or an error occurs, such as a
* CSS file error, or a load timeout.
* @method _stopWatching
* @protected
*/
p._stopWatching = function() {
clearInterval(this._watchInterval);
clearTimeout(this._loadTimeout);
this._watchInterval = null;
};
/**
* Check all the fonts that have not been loaded. The fonts are drawn to a canvas in memory, and if their font size
* varies from the default text size, then the font is considered loaded.
*
* A {{#crossLink "AbstractLoader/fileload"}}{{/crossLink}} event will be dispatched when each file is loaded, along
* with the font family name as the `item` value. A {{#crossLink "ProgressEvent"}}{{/crossLink}} is dispatched a
* maximum of one time per check when any fonts are loaded, with the {{#crossLink "ProgressEvent/progress:property"}}{{/crossLink}}
* value showing the percentage of fonts that have loaded.
* @method _watch
* @protected
*/
p._watch = function() {
var defs = this._watched, refFonts = FontLoader._referenceFonts, l = defs.length;
for (var i = l - 1; i >= 0; i--) {
var def = defs[i], refs = def.refs;
for (var j = refs.length - 1; j >= 0; j--) {
var w = this._getTextWidth(def.family + "," + refFonts[j], def.weight, def.style);
if (w != refs[j]) {
var event = new createjs.Event("fileload");
def.type = "font-family";
event.item = def;
this.dispatchEvent(event);
defs.splice(i, 1);
break;
}
}
}
if (l !== defs.length) {
var event = new createjs.ProgressEvent(this._count-defs.length, this._count);
this.dispatchEvent(event);
}
if (l === 0) {
this._stopWatching();
this._sendComplete();
}
};
/**
* Determine the default size of the reference fonts used to compare against loaded fonts.
* @method _calculateReferenceSizes
* @param {Object} def The font definition to get the size of.
* @protected
*/
p._calculateReferenceSizes = function(def) {
var refFonts = FontLoader._referenceFonts;
var refs = def.refs = [];
for (var i=0; iwithout requiring CORS.
* JSONP files are loaded as JavaScript, and the "callback" is executed once they are loaded. The callback in the
* JSONP must match the callback passed to the loadItem.
*
* Example JSONP
*
* callbackName({
* "name": "value",
* "num": 3,
* "obj": { "bool":true }
* });
*
* Example
*
* var loadItem = {id:"json", type:"jsonp", src:"", callback:"callbackName"}
* var queue = new createjs.LoadQueue();
* queue.on("complete", handleComplete);
* queue.loadItem(loadItem);
*
* function handleComplete(event) }
* var json = queue.getResult("json");
* console.log(json.obj.bool); // true
* }
*
* JSONP files loaded concurrently require a unique callback. To ensure JSONP files are loaded in order,
* either use the {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}} method (set to 1), or set
* {{#crossLink "LoadItem/maintainOrder:property"}}{{/crossLink}} on items with the same callback.
*
* Important note: Some browsers will prevent JSONP from firing the callback if the file was loaded as JSON, and not
* JavaScript. You may have to have your server give you a JavaScript mime-type for this to work.
*
* @class JSONPLoader
* @param {LoadItem|Object} loadItem
* @extends AbstractLoader
* @constructor
*/
function JSONPLoader(loadItem) {
this.AbstractLoader_constructor(loadItem, false, createjs.Types.JSONP);
this.setTag(createjs.Elements.script());
this.getTag().type = "text/javascript";
};
var p = createjs.extend(JSONPLoader, createjs.AbstractLoader);
var s = JSONPLoader;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/JSONP: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.Types.JSONP;
};
// public methods
p.cancel = function () {
this.AbstractLoader_cancel();
this._dispose();
};
/**
* Loads the JSONp file. Because of the unique loading needs of JSONp
* we don't use the AbstractLoader.load() method.
*
* @method load
*
*/
p.load = function () {
if (this._item.callback == null) {
throw new Error('callback is required for loading JSONP requests.');
}
// TODO: Look into creating our own iFrame to handle the load
// In the first attempt, FF did not get the result
// result instanceof Object did not work either
// so we would need to clone the result.
if (window[this._item.callback] != null) {
throw new Error(
"JSONP callback '" +
this._item.callback +
"' already exists on window. You need to specify a different callback or re-name the current one.");
}
window[this._item.callback] = createjs.proxy(this._handleLoad, this);
createjs.DomUtils.appendToBody(this._tag);
this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
// Load the tag
this._tag.src = this._item.src;
};
// private methods
/**
* Handle the JSONP callback, which is a public method defined on `window`.
* @method _handleLoad
* @param {Object} data The formatted JSON data.
* @private
*/
p._handleLoad = function (data) {
this._result = this._rawResult = data;
this._sendComplete();
this._dispose();
};
/**
* The tag request has not loaded within the time specfied in loadTimeout.
* @method _handleError
* @param {Object} event The XHR error event.
* @private
*/
p._handleTimeout = function () {
this._dispose();
this.dispatchEvent(new createjs.ErrorEvent("timeout"));
};
/**
* Clean up the JSONP load. This clears out the callback and script tag that this loader creates.
* @method _dispose
* @private
*/
p._dispose = function () {
createjs.DomUtils.removeChild(this._tag);
delete window[this._item.callback];
clearTimeout(this._loadTimeout);
};
createjs.JSONPLoader = createjs.promote(JSONPLoader, "AbstractLoader");
}());
//##############################################################################
// ManifestLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for JSON manifests. Items inside the manifest are loaded before the loader completes. To load manifests
* using JSONP, specify a {{#crossLink "LoadItem/callback:property"}}{{/crossLink}} as part of the
* {{#crossLink "LoadItem"}}{{/crossLink}}.
*
* The list of files in the manifest must be defined on the top-level JSON object in a `manifest` property. This
* example shows a sample manifest definition, as well as how to to include a sub-manifest.
*
* {
* "path": "assets/",
* "manifest": [
* "image.png",
* {"src": "image2.png", "id":"image2"},
* {"src": "sub-manifest.json", "type":"manifest", "callback":"jsonCallback"}
* ]
* }
*
* When a ManifestLoader has completed loading, the parent loader (usually a {{#crossLink "LoadQueue"}}{{/crossLink}},
* but could also be another ManifestLoader) will inherit all the loaded items, so you can access them directly.
*
* Note that the {{#crossLink "JSONLoader"}}{{/crossLink}} and {{#crossLink "JSONPLoader"}}{{/crossLink}} are
* higher priority loaders, so manifests must set the {{#crossLink "LoadItem"}}{{/crossLink}}
* {{#crossLink "LoadItem/type:property"}}{{/crossLink}} property to {{#crossLink "Types/MANIFEST:property"}}{{/crossLink}}.
*
* Additionally, some browsers require the server to serve a JavaScript mime-type for JSONP, so it may not work in
* some conditions.
* @class ManifestLoader
* @param {LoadItem|Object} loadItem
* @extends AbstractLoader
* @constructor
*/
function ManifestLoader(loadItem, preferXHR) {
this.AbstractLoader_constructor(loadItem, preferXHR, createjs.Types.MANIFEST);
// Public Properties
/**
* An array of the plugins registered using {{#crossLink "LoadQueue/installPlugin"}}{{/crossLink}},
* used to pass plugins to new LoadQueues that may be created.
* @property _plugins
* @type {Array}
* @private
* @since 0.6.1
*/
this.plugins = null;
// Protected Properties
/**
* An internal {{#crossLink "LoadQueue"}}{{/crossLink}} that loads the contents of the manifest.
* @property _manifestQueue
* @type {LoadQueue}
* @private
*/
this._manifestQueue = null;
};
var p = createjs.extend(ManifestLoader, createjs.AbstractLoader);
var s = ManifestLoader;
// static properties
/**
* The amount of progress that the manifest itself takes up.
* @property MANIFEST_PROGRESS
* @type {number}
* @default 0.25 (25%)
* @private
* @static
*/
s.MANIFEST_PROGRESS = 0.25;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/MANIFEST: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.Types.MANIFEST;
};
// public methods
p.load = function () {
this.AbstractLoader_load();
};
// protected methods
p._createRequest = function() {
var callback = this._item.callback;
if (callback != null) {
this._request = new createjs.JSONPLoader(this._item);
} else {
this._request = new createjs.JSONLoader(this._item);
}
};
p.handleEvent = function (event) {
switch (event.type) {
case "complete":
this._rawResult = event.target.getResult(true);
this._result = event.target.getResult();
this._sendProgress(s.MANIFEST_PROGRESS);
this._loadManifest(this._result);
return;
case "progress":
event.loaded *= s.MANIFEST_PROGRESS;
this.progress = event.loaded / event.total;
if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
this._sendProgress(event);
return;
}
this.AbstractLoader_handleEvent(event);
};
p.destroy = function() {
this.AbstractLoader_destroy();
this._manifestQueue.close();
};
/**
* Create and load the manifest items once the actual manifest has been loaded.
* @method _loadManifest
* @param {Object} json
* @private
*/
p._loadManifest = function (json) {
if (json && json.manifest) {
var queue = this._manifestQueue = new createjs.LoadQueue(this._preferXHR);
queue.on("fileload", this._handleManifestFileLoad, this);
queue.on("progress", this._handleManifestProgress, this);
queue.on("complete", this._handleManifestComplete, this, true);
queue.on("error", this._handleManifestError, this, true);
for(var i = 0, l = this.plugins.length; i < l; i++) { // conserve order of plugins
queue.installPlugin(this.plugins[i]);
}
queue.loadManifest(json);
} else {
this._sendComplete();
}
};
/**
* An item from the {{#crossLink "_manifestQueue:property"}}{{/crossLink}} has completed.
* @method _handleManifestFileLoad
* @param {Event} event
* @private
*/
p._handleManifestFileLoad = function (event) {
event.target = null;
this.dispatchEvent(event);
};
/**
* The manifest has completed loading. This triggers the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}}
* {{#crossLink "Event"}}{{/crossLink}} from the ManifestLoader.
* @method _handleManifestComplete
* @param {Event} event
* @private
*/
p._handleManifestComplete = function (event) {
this._loadedItems = this._manifestQueue.getItems(true);
this._sendComplete();
};
/**
* The manifest has reported progress.
* @method _handleManifestProgress
* @param {ProgressEvent} event
* @private
*/
p._handleManifestProgress = function (event) {
this.progress = event.progress * (1 - s.MANIFEST_PROGRESS) + s.MANIFEST_PROGRESS;
this._sendProgress(this.progress);
};
/**
* The manifest has reported an error with one of the files.
* @method _handleManifestError
* @param {ErrorEvent} event
* @private
*/
p._handleManifestError = function (event) {
var newEvent = new createjs.Event("fileerror");
newEvent.item = event.data;
this.dispatchEvent(newEvent);
};
createjs.ManifestLoader = createjs.promote(ManifestLoader, "AbstractLoader");
}());
//##############################################################################
// SoundLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for HTML audio files. PreloadJS can not load WebAudio files, as a WebAudio context is required, which
* should be created by either a library playing the sound (such as SoundJS, or an
* external framework that handles audio playback. To load content that can be played by WebAudio, use the
* {{#crossLink "BinaryLoader"}}{{/crossLink}}, and handle the audio context decoding manually.
* @class SoundLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @extends AbstractMediaLoader
* @constructor
*/
function SoundLoader(loadItem, preferXHR) {
this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.Types.SOUND);
// protected properties
if (createjs.DomUtils.isAudioTag(loadItem)) {
this._tag = loadItem;
} else if (createjs.DomUtils.isAudioTag(loadItem.src)) {
this._tag = loadItem;
} else if (createjs.DomUtils.isAudioTag(loadItem.tag)) {
this._tag = createjs.DomUtils.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 "Types/SOUND:property"}}{{/crossLink}}.
* @method canLoadItem
* @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
* @returns {Boolean} Whether the loader can load the item.
* @static
*/
s.canLoadItem = function (item) {
return item.type == createjs.Types.SOUND;
};
// protected methods
p._createTag = function (src) {
var tag = createjs.Elements.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");
}());
//##############################################################################
// VideoLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for video files.
* @class VideoLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @extends AbstractMediaLoader
* @constructor
*/
function VideoLoader(loadItem, preferXHR) {
this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.Types.VIDEO);
if (createjs.DomUtils.isVideoTag(loadItem) || createjs.DomUtils.isVideoTag(loadItem.src)) {
this.setTag(createjs.DomUtils.isVideoTag(loadItem)?loadItem:loadItem.src);
// We can't use XHR for a tag that's passed in.
this._preferXHR = false;
} else {
this.setTag(this._createTag());
}
};
var p = createjs.extend(VideoLoader, createjs.AbstractMediaLoader);
var s = VideoLoader;
/**
* Create a new video tag
*
* @returns {HTMLElement}
* @private
*/
p._createTag = function () {
return createjs.Elements.video();
};
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/VIDEO: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.Types.VIDEO;
};
createjs.VideoLoader = createjs.promote(VideoLoader, "AbstractMediaLoader");
}());
//##############################################################################
// SpriteSheetLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for EaselJS SpriteSheets. Images inside the spritesheet definition are loaded before the loader
* completes. To load SpriteSheets using JSONP, specify a {{#crossLink "LoadItem/callback:property"}}{{/crossLink}}
* as part of the {{#crossLink "LoadItem"}}{{/crossLink}}. Note that the {{#crossLink "JSONLoader"}}{{/crossLink}}
* and {{#crossLink "JSONPLoader"}}{{/crossLink}} are higher priority loaders, so SpriteSheets must
* set the {{#crossLink "LoadItem"}}{{/crossLink}} {{#crossLink "LoadItem/type:property"}}{{/crossLink}} property
* to {{#crossLink "Types/SPRITESHEET:property"}}{{/crossLink}}.
*
* The {{#crossLink "LoadItem"}}{{/crossLink}} {{#crossLink "LoadItem/crossOrigin:property"}}{{/crossLink}} as well
* as the {{#crossLink "LoadQueue's"}}{{/crossLink}} `basePath` argument and {{#crossLink "LoadQueue/_preferXHR"}}{{/crossLink}}
* property supplied to the {{#crossLink "LoadQueue"}}{{/crossLink}} are passed on to the sub-manifest that loads
* the SpriteSheet images.
*
* Note that the SpriteSheet JSON does not respect the {{#crossLink "LoadQueue/_preferXHR:property"}}{{/crossLink}}
* property, which should instead be determined by the presence of a {{#crossLink "LoadItem/callback:property"}}{{/crossLink}}
* property on the SpriteSheet load item. This is because the JSON loaded will have a different format depending on
* if it is loaded as JSON, so just changing `preferXHR` is not enough to change how it is loaded.
* @class SpriteSheetLoader
* @param {LoadItem|Object} loadItem
* @extends AbstractLoader
* @constructor
*/
function SpriteSheetLoader(loadItem, preferXHR) {
this.AbstractLoader_constructor(loadItem, preferXHR, createjs.Types.SPRITESHEET);
// protected properties
/**
* An internal queue which loads the SpriteSheet's images.
* @method _manifestQueue
* @type {LoadQueue}
* @private
*/
this._manifestQueue = null;
}
var p = createjs.extend(SpriteSheetLoader, createjs.AbstractLoader);
var s = SpriteSheetLoader;
// static properties
/**
* The amount of progress that the manifest itself takes up.
* @property SPRITESHEET_PROGRESS
* @type {number}
* @default 0.25 (25%)
* @private
* @static
*/
s.SPRITESHEET_PROGRESS = 0.25;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/SPRITESHEET: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.Types.SPRITESHEET;
};
// public methods
p.destroy = function() {
this.AbstractLoader_destroy();
this._manifestQueue.close();
};
// protected methods
p._createRequest = function() {
var callback = this._item.callback;
if (callback != null) {
this._request = new createjs.JSONPLoader(this._item);
} else {
this._request = new createjs.JSONLoader(this._item);
}
};
p.handleEvent = function (event) {
switch (event.type) {
case "complete":
this._rawResult = event.target.getResult(true);
this._result = event.target.getResult();
this._sendProgress(s.SPRITESHEET_PROGRESS);
this._loadManifest(this._result);
return;
case "progress":
event.loaded *= s.SPRITESHEET_PROGRESS;
this.progress = event.loaded / event.total;
if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
this._sendProgress(event);
return;
}
this.AbstractLoader_handleEvent(event);
};
/**
* Create and load the images once the SpriteSheet JSON has been loaded.
* @method _loadManifest
* @param {Object} json
* @private
*/
p._loadManifest = function (json) {
if (json && json.images) {
var queue = this._manifestQueue = new createjs.LoadQueue(this._preferXHR, this._item.path, this._item.crossOrigin);
queue.on("complete", this._handleManifestComplete, this, true);
queue.on("fileload", this._handleManifestFileLoad, this);
queue.on("progress", this._handleManifestProgress, this);
queue.on("error", this._handleManifestError, this, true);
queue.loadManifest(json.images);
}
};
/**
* An item from the {{#crossLink "_manifestQueue:property"}}{{/crossLink}} has completed.
* @method _handleManifestFileLoad
* @param {Event} event
* @private
*/
p._handleManifestFileLoad = function (event) {
var image = event.result;
if (image != null) {
var images = this.getResult().images;
var pos = images.indexOf(event.item.src);
images[pos] = image;
}
};
/**
* The images have completed loading. This triggers the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}}
* {{#crossLink "Event"}}{{/crossLink}} from the SpriteSheetLoader.
* @method _handleManifestComplete
* @param {Event} event
* @private
*/
p._handleManifestComplete = function (event) {
this._result = new createjs.SpriteSheet(this._result);
this._loadedItems = this._manifestQueue.getItems(true);
this._sendComplete();
};
/**
* The images {{#crossLink "LoadQueue"}}{{/crossLink}} has reported progress.
* @method _handleManifestProgress
* @param {ProgressEvent} event
* @private
*/
p._handleManifestProgress = function (event) {
this.progress = event.progress * (1 - s.SPRITESHEET_PROGRESS) + s.SPRITESHEET_PROGRESS;
this._sendProgress(this.progress);
};
/**
* An image has reported an error.
* @method _handleManifestError
* @param {ErrorEvent} event
* @private
*/
p._handleManifestError = function (event) {
var newEvent = new createjs.Event("fileerror");
newEvent.item = event.data;
this.dispatchEvent(newEvent);
};
createjs.SpriteSheetLoader = createjs.promote(SpriteSheetLoader, "AbstractLoader");
}());
//##############################################################################
// SVGLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for SVG files.
* @class SVGLoader
* @param {LoadItem|Object} loadItem
* @param {Boolean} preferXHR
* @extends AbstractLoader
* @constructor
*/
function SVGLoader(loadItem, preferXHR) {
this.AbstractLoader_constructor(loadItem, preferXHR, createjs.Types.SVG);
// public properties
this.resultFormatter = this._formatResult;
// protected properties
this._tagSrcAttribute = "data";
if (preferXHR) {
this.setTag(createjs.Elements.svg());
} else {
this.setTag(createjs.Elements.object());
this.getTag().type = "image/svg+xml";
}
};
var p = createjs.extend(SVGLoader, createjs.AbstractLoader);
var s = SVGLoader;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/SVG: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.Types.SVG;
};
// protected methods
/**
* The result formatter for SVG files.
* @method _formatResult
* @param {AbstractLoader} loader
* @returns {Object}
* @private
*/
p._formatResult = function (loader) {
// mime should be image/svg+xml, but Opera requires text/xml
var xml = createjs.DataUtils.parseXML(loader.getResult(true));
var tag = loader.getTag();
if (!this._preferXHR && document.body.contains(tag)) {
document.body.removeChild(tag);
}
if (xml.documentElement != null) {
var element = xml.documentElement;
// Support loading an SVG from a different domain in ID
if (document.importNode) {
element = document.importNode(element, true);
}
tag.appendChild(element);
return tag;
} else { // For browsers that don't support SVG, just give them the XML. (IE 9-8)
return xml;
}
};
createjs.SVGLoader = createjs.promote(SVGLoader, "AbstractLoader");
}());
//##############################################################################
// XMLLoader.js
//##############################################################################
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* A loader for CSS files.
* @class XMLLoader
* @param {LoadItem|Object} loadItem
* @extends AbstractLoader
* @constructor
*/
function XMLLoader(loadItem) {
this.AbstractLoader_constructor(loadItem, true, createjs.Types.XML);
// public properties
this.resultFormatter = this._formatResult;
};
var p = createjs.extend(XMLLoader, createjs.AbstractLoader);
var s = XMLLoader;
// static methods
/**
* Determines if the loader can load a specific item. This loader can only load items that are of type
* {{#crossLink "Types/XML: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.Types.XML;
};
// protected methods
/**
* The result formatter for XML files.
* @method _formatResult
* @param {AbstractLoader} loader
* @returns {XMLDocument}
* @private
*/
p._formatResult = function (loader) {
return createjs.DataUtils.parseXML(loader.getResult(true));
};
createjs.XMLLoader = createjs.promote(XMLLoader, "AbstractLoader");
}());