/*
* AbstractLoader
* Visit http://createjs.com/ for documentation, updates and examples.
*
*
* Copyright (c) 2012 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* @module PreloadJS
*/
// namespace:
this.createjs = this.createjs || {};
(function () {
"use strict";
// constructor
/**
* The base loader, which defines all the generic methods, properties, and events. All loaders extend this class,
* including the {{#crossLink "LoadQueue"}}{{/crossLink}}.
* @class AbstractLoader
* @param {LoadItem|object|string} loadItem The item to be loaded.
* @param {Boolean} [preferXHR] Determines if the LoadItem should <em>try</em> and load using XHR, or take a
* tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the
* other, so this is a suggested directive.
* @param {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
* such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc.
* @extends EventDispatcher
*/
function AbstractLoader(loadItem, preferXHR, type) {
this.EventDispatcher_constructor();
// public properties
/**
* If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
* used for loading do not pile up resulting in more than one `complete` {{#crossLink "Event"}}{{/crossLink}}.
* @property loaded
* @type {Boolean}
* @default false
*/
this.loaded = false;
/**
* Determine if the loader was canceled. Canceled loads will not fire complete events. Note that this property
* is readonly, so {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "LoadQueue/close"}}{{/crossLink}}
* instead.
* @property canceled
* @type {Boolean}
* @default false
* @readonly
*/
this.canceled = false;
/**
* The current load progress (percentage) for this item. This will be a number between 0 and 1.
*
* <h4>Example</h4>
*
* var queue = new createjs.LoadQueue();
* queue.loadFile("largeImage.png");
* queue.on("progress", function() {
* console.log("Progress:", queue.progress, event.progress);
* });
*
* @property progress
* @type {Number}
* @default 0
*/
this.progress = 0;
/**
* The type of item this loader will load. See {{#crossLink "AbstractLoader"}}{{/crossLink}} for a full list of
* supported types.
* @property type
* @type {String}
*/
this.type = type;
/**
* A formatter function that converts the loaded raw result into the final result. For example, the JSONLoader
* converts a string of text into a JavaScript object. Not all loaders have a resultFormatter, and this property
* can be overridden to provide custom formatting.
*
* Optionally, a resultFormatter can return a callback function in cases where the formatting needs to be
* asynchronous, such as creating a new image.
* @property resultFormatter
* @type {Function}
* @default null
*/
this.resultFormatter = null;
// protected properties
/**
* The {{#crossLink "LoadItem"}}{{/crossLink}} this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}},
* but will be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}.
* @property _item
* @type {LoadItem|Object}
* @private
*/
if (loadItem) {
this._item = createjs.LoadItem.create(loadItem);
} else {
this._item = null;
}
/**
* Whether the loader will try and load content using XHR (true) or HTML tags (false).
* @property _preferXHR
* @type {Boolean}
* @private
*/
this._preferXHR = preferXHR;
/**
* The loaded result after it is formatted by an optional {{#crossLink "resultFormatter"}}{{/crossLink}}. For
* items that are not formatted, this will be the same as the {{#crossLink "_rawResult:property"}}{{/crossLink}}.
* The result is accessed using the {{#crossLink "getResult"}}{{/crossLink}} method.
* @property _result
* @type {Object|String}
* @private
*/
this._result = null;
/**
* The loaded result before it is formatted. The rawResult is accessed using the {{#crossLink "getResult"}}{{/crossLink}}
* method, and passing `true`.
* @property _rawResult
* @type {Object|String}
* @private
*/
this._rawResult = null;
/**
* A list of items that loaders load behind the scenes. This does not include the main item the loader is
* responsible for loading. Examples of loaders that have sub-items include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and
* {{#crossLink "ManifestLoader"}}{{/crossLink}}.
* @property _loadItems
* @type {null}
* @protected
*/
this._loadedItems = null;
/**
* The attribute the items loaded using tags use for the source.
* @type {string}
* @default null
* @private
*/
this._tagSrcAttribute = null;
/**
* An HTML tag (or similar) that a loader may use to load HTML content, such as images, scripts, etc.
* @property _tag
* @type {Object}
* @private
*/
this._tag = null;
};
var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
var s = AbstractLoader;
// TODO: deprecated
// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
/**
* Defines a POST request, use for a method value when loading data.
* @property POST
* @type {string}
* @default post
* @static
*/
s.POST = "POST";
/**
* Defines a GET request, use for a method value when loading data.
* @property GET
* @type {string}
* @default get
* @static
*/
s.GET = "GET";
/**
* The preload type for generic binary types. Note that images are loaded as binary files when using XHR.
* @property BINARY
* @type {String}
* @default binary
* @static
* @since 0.6.0
*/
s.BINARY = "binary";
/**
* The preload type for css files. CSS files are loaded using a <link> when loaded with XHR, or a
* <style> tag when loaded with tags.
* @property CSS
* @type {String}
* @default css
* @static
* @since 0.6.0
*/
s.CSS = "css";
/**
* The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an <image> tag.
* @property IMAGE
* @type {String}
* @default image
* @static
* @since 0.6.0
*/
s.IMAGE = "image";
/**
* The preload type for javascript files, usually with the "js" file extension. JavaScript files are loaded into a
* <script> tag.
*
* Since version 0.4.1+, due to how tag-loaded scripts work, all JavaScript files are automatically injected into
* the body of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier,
* only tag-loaded scripts are injected.
* @property JAVASCRIPT
* @type {String}
* @default javascript
* @static
* @since 0.6.0
*/
s.JAVASCRIPT = "javascript";
/**
* The preload type for json files, usually with the "json" file extension. JSON data is loaded and parsed into a
* JavaScript object. Note that if a `callback` is present on the load item, the file will be loaded with JSONP,
* no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to, and the JSON
* must contain a matching wrapper function.
* @property JSON
* @type {String}
* @default json
* @static
* @since 0.6.0
*/
s.JSON = "json";
/**
* The preload type for jsonp files, usually with the "json" file extension. JSON data is loaded and parsed into a
* JavaScript object. You are required to pass a callback parameter that matches the function wrapper in the JSON.
* Note that JSONP will always be used if there is a callback present, no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}}
* property is set to.
* @property JSONP
* @type {String}
* @default jsonp
* @static
* @since 0.6.0
*/
s.JSONP = "jsonp";
/**
* The preload type for json-based manifest files, usually with the "json" file extension. The JSON data is loaded
* and parsed into a JavaScript object. PreloadJS will then look for a "manifest" property in the JSON, which is an
* Array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
* method. If a "callback" is specified on the manifest object, then it will be loaded using JSONP instead,
* regardless of what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to.
* @property MANIFEST
* @type {String}
* @default manifest
* @static
* @since 0.6.0
*/
s.MANIFEST = "manifest";
/**
* The preload type for sound files, usually mp3, ogg, or wav. When loading via tags, audio is loaded into an
* <audio> tag.
* @property SOUND
* @type {String}
* @default sound
* @static
* @since 0.6.0
*/
s.SOUND = "sound";
/**
* The preload type for video files, usually mp4, ts, or ogg. When loading via tags, video is loaded into an
* <video> tag.
* @property VIDEO
* @type {String}
* @default video
* @static
* @since 0.6.0
*/
s.VIDEO = "video";
/**
* The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths.
* @property SPRITESHEET
* @type {String}
* @default spritesheet
* @static
* @since 0.6.0
*/
s.SPRITESHEET = "spritesheet";
/**
* The preload type for SVG files.
* @property SVG
* @type {String}
* @default svg
* @static
* @since 0.6.0
*/
s.SVG = "svg";
/**
* The preload type for text files, which is also the default file type if the type can not be determined. Text is
* loaded as raw text.
* @property TEXT
* @type {String}
* @default text
* @static
* @since 0.6.0
*/
s.TEXT = "text";
/**
* The preload type for xml files. XML is loaded into an XML document.
* @property XML
* @type {String}
* @default xml
* @static
* @since 0.6.0
*/
s.XML = "xml";
// Events
/**
* The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to
* version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}.
* @event progress
* @since 0.3.0
*/
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts.
* @event loadstart
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.3.1
*/
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded.
* @event complete
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type.
* @since 0.3.0
*/
/**
* The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was
* encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was
* just a regular {{#crossLink "Event"}}{{/crossLink}}.
* @event error
* @since 0.3.0
*/
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error.
* This enables loaders to maintain internal queues, and surface file load errors.
* @event fileerror
* @param {Object} target The object that dispatched the event.
* @param {String} type The even type ("fileerror")
* @param {LoadItem|object} The item that encountered the error
* @since 0.6.0
*/
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables
* loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s
* and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a
* slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event.
* @event fileload
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type ("fileload")
* @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
* or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
* object will contain that value as a `src` property.
* @param {Object} result The HTML tag or parsed result of the loaded item.
* @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
* to a usable object.
* @since 0.6.0
*/
/**
* The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load.
* This allows updates to the loader for specific loading needs, such as binary or XHR image loading.
* @event initialize
* @param {Object} target The object that dispatched the event.
* @param {String} type The event type ("initialize")
* @param {AbstractLoader} loader The loader that has been initialized.
*/
/**
* Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was
* passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
* {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will
* be a {{#crossLink "LoadItem"}}{{/crossLink}}.
* @method getItem
* @return {Object} The manifest item that this loader is responsible for loading.
* @since 0.6.0
*/
p.getItem = function () {
return this._item;
};
/**
* Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}}
* event is dispatched.
* @method getResult
* @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded
* data (if it exists).
* @return {Object}
* @since 0.6.0
*/
p.getResult = function (raw) {
return raw ? this._rawResult : this._result;
};
/**
* Return the `tag` this object creates or uses for loading.
* @method getTag
* @return {Object} The tag instance
* @since 0.6.0
*/
p.getTag = function () {
return this._tag;
};
/**
* Set the `tag` this item uses for loading.
* @method setTag
* @param {Object} tag The tag instance
* @since 0.6.0
*/
p.setTag = function(tag) {
this._tag = tag;
};
/**
* Begin loading the item. This method is required when using a loader by itself.
*
* <h4>Example</h4>
*
* var queue = new createjs.LoadQueue();
* queue.on("complete", handleComplete);
* queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet
* queue.load();
*
* @method load
*/
p.load = function () {
this._createRequest();
this._request.on("complete", this, this);
this._request.on("progress", this, this);
this._request.on("loadStart", this, this);
this._request.on("abort", this, this);
this._request.on("timeout", this, this);
this._request.on("error", this, this);
var evt = new createjs.Event("initialize");
evt.loader = this._request;
this.dispatchEvent(evt);
this._request.load();
};
/**
* Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in
* the background), but events will not longer be dispatched.
* @method cancel
*/
p.cancel = function () {
this.canceled = true;
this.destroy();
};
/**
* Clean up the loader.
* @method destroy
*/
p.destroy = function() {
if (this._request) {
this._request.removeAllEventListeners();
this._request.destroy();
}
this._request = null;
this._item = null;
this._rawResult = null;
this._result = null;
this._loadItems = null;
this.removeAllEventListeners();
};
/**
* Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}}
* to expose items it loads internally.
* @method getLoadedItems
* @return {Array} A list of the items loaded by the loader.
* @since 0.6.0
*/
p.getLoadedItems = function () {
return this._loadedItems;
};
// Private methods
/**
* Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or
* {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}.
* Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}},
* which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood.
* @method _createRequest
* @protected
*/
p._createRequest = function() {
if (!this._preferXHR) {
this._request = new createjs.TagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
} else {
this._request = new createjs.XHRRequest(this._item);
}
};
/**
* Create the HTML tag used for loading. This method does nothing by default, and needs to be implemented
* by loaders that require tag loading.
* @method _createTag
* @param {String} src The tag source
* @return {HTMLElement} The tag that was created
* @protected
*/
p._createTag = function(src) { return null; };
/**
* Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendLoadStart
* @protected
*/
p._sendLoadStart = function () {
if (this._isCanceled()) { return; }
this.dispatchEvent("loadstart");
};
/**
* Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}.
* @method _sendProgress
* @param {Number | Object} value The progress of the loaded item, or an object containing <code>loaded</code>
* and <code>total</code> properties.
* @protected
*/
p._sendProgress = function (value) {
if (this._isCanceled()) { return; }
var event = null;
if (typeof(value) == "number") {
this.progress = value;
event = new createjs.ProgressEvent(this.progress);
} else {
event = value;
this.progress = value.loaded / value.total;
event.progress = this.progress;
if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
}
this.hasEventListener("progress") && this.dispatchEvent(event);
};
/**
* Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
* @method _sendComplete
* @protected
*/
p._sendComplete = function () {
if (this._isCanceled()) { return; }
this.loaded = true;
var event = new createjs.Event("complete");
event.rawResult = this._rawResult;
if (this._result != null) {
event.result = this._result;
}
this.dispatchEvent(event);
};
/**
* Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
* event for details on the event payload.
* @method _sendError
* @param {ErrorEvent} event The event object containing specific error properties.
* @protected
*/
p._sendError = function (event) {
if (this._isCanceled() || !this.hasEventListener("error")) { return; }
if (event == null) {
event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
}
this.dispatchEvent(event);
};
/**
* Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
* do not cause issues after the queue has been cleaned up.
* @method _isCanceled
* @return {Boolean} If the loader has been canceled.
* @protected
*/
p._isCanceled = function () {
if (window.createjs == null || this.canceled) {
return true;
}
return false;
};
/**
* A custom result formatter function, which is called just before a request dispatches its complete event. Most
* loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
* formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
* @property resultFormatter
* @type Function
* @return {Object} The formatted result
* @since 0.6.0
*/
p.resultFormatter = null;
/**
* Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but
* this method can be overridden for custom behaviours.
* @method handleEvent
* @param {Event} event The event that the internal request dispatches.
* @protected
* @since 0.6.0
*/
p.handleEvent = function (event) {
switch (event.type) {
case "complete":
this._rawResult = event.target._response;
var result = this.resultFormatter && this.resultFormatter(this);
var _this = this;
if (result instanceof Function) {
result(function(result) {
_this._result = result;
_this._sendComplete();
});
} else {
this._result = result || this._rawResult;
this._sendComplete();
}
break;
case "progress":
this._sendProgress(event);
break;
case "error":
this._sendError(event);
break;
case "loadstart":
this._sendLoadStart();
break;
case "abort":
case "timeout":
if (!this._isCanceled()) {
this.dispatchEvent(event.type);
}
break;
}
};
/**
* @method buildPath
* @protected
* @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
* instead.
*/
p.buildPath = function (src, data) {
return createjs.RequestUtils.buildPath(src, data);
};
/**
* @method toString
* @return {String} a string representation of the instance.
*/
p.toString = function () {
return "[PreloadJS AbstractLoader]";
};
createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher");
}());