mirror of
https://github.com/Buriburizaem0n/live2d.git
synced 2025-12-17 08:31:07 +00:00
2009 lines
83 KiB
JavaScript
2009 lines
83 KiB
JavaScript
/*!
|
|
* @pixi/loaders - v6.5.10
|
|
* Compiled Thu, 06 Jul 2023 15:25:11 UTC
|
|
*
|
|
* @pixi/loaders is licensed under the MIT License.
|
|
* http://www.opensource.org/licenses/mit-license
|
|
*/
|
|
this.PIXI = this.PIXI || {};
|
|
var _pixi_loaders = (function (exports, core, utils) {
|
|
'use strict';
|
|
|
|
/* jshint -W097 */
|
|
/**
|
|
* @memberof PIXI
|
|
*/
|
|
var SignalBinding = /** @class */ (function () {
|
|
/**
|
|
* SignalBinding constructor.
|
|
* @constructs SignalBinding
|
|
* @param {Function} fn - Event handler to be called.
|
|
* @param {boolean} [once=false] - Should this listener be removed after dispatch
|
|
* @param {object} [thisArg] - The context of the callback function.
|
|
* @api private
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
function SignalBinding(fn, once, thisArg) {
|
|
if (once === void 0) { once = false; }
|
|
this._fn = fn;
|
|
this._once = once;
|
|
this._thisArg = thisArg;
|
|
this._next = this._prev = this._owner = null;
|
|
}
|
|
SignalBinding.prototype.detach = function () {
|
|
if (this._owner === null)
|
|
{ return false; }
|
|
this._owner.detach(this);
|
|
return true;
|
|
};
|
|
return SignalBinding;
|
|
}());
|
|
/**
|
|
* @param self
|
|
* @param node
|
|
* @private
|
|
*/
|
|
function _addSignalBinding(self, node) {
|
|
if (!self._head) {
|
|
self._head = node;
|
|
self._tail = node;
|
|
}
|
|
else {
|
|
self._tail._next = node;
|
|
node._prev = self._tail;
|
|
self._tail = node;
|
|
}
|
|
node._owner = self;
|
|
return node;
|
|
}
|
|
/**
|
|
* @memberof PIXI
|
|
*/
|
|
var Signal = /** @class */ (function () {
|
|
/**
|
|
* MiniSignal constructor.
|
|
* @example
|
|
* let mySignal = new Signal();
|
|
* let binding = mySignal.add(onSignal);
|
|
* mySignal.dispatch('foo', 'bar');
|
|
* mySignal.detach(binding);
|
|
*/
|
|
function Signal() {
|
|
this._head = this._tail = undefined;
|
|
}
|
|
/**
|
|
* Return an array of attached SignalBinding.
|
|
* @param {boolean} [exists=false] - We only need to know if there are handlers.
|
|
* @returns {PIXI.SignalBinding[] | boolean} Array of attached SignalBinding or Boolean if called with exists = true
|
|
* @api public
|
|
*/
|
|
Signal.prototype.handlers = function (exists) {
|
|
if (exists === void 0) { exists = false; }
|
|
var node = this._head;
|
|
if (exists)
|
|
{ return !!node; }
|
|
var ee = [];
|
|
while (node) {
|
|
ee.push(node);
|
|
node = node._next;
|
|
}
|
|
return ee;
|
|
};
|
|
/**
|
|
* Return true if node is a SignalBinding attached to this MiniSignal
|
|
* @param {PIXI.SignalBinding} node - Node to check.
|
|
* @returns {boolean} True if node is attache to mini-signal
|
|
*/
|
|
Signal.prototype.has = function (node) {
|
|
if (!(node instanceof SignalBinding)) {
|
|
throw new Error('MiniSignal#has(): First arg must be a SignalBinding object.');
|
|
}
|
|
return node._owner === this;
|
|
};
|
|
/**
|
|
* Dispaches a signal to all registered listeners.
|
|
* @param {...any} args
|
|
* @returns {boolean} Indication if we've emitted an event.
|
|
*/
|
|
Signal.prototype.dispatch = function () {
|
|
var arguments$1 = arguments;
|
|
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments$1[_i];
|
|
}
|
|
var node = this._head;
|
|
if (!node)
|
|
{ return false; }
|
|
while (node) {
|
|
if (node._once)
|
|
{ this.detach(node); }
|
|
node._fn.apply(node._thisArg, args);
|
|
node = node._next;
|
|
}
|
|
return true;
|
|
};
|
|
/**
|
|
* Register a new listener.
|
|
* @param {Function} fn - Callback function.
|
|
* @param {object} [thisArg] - The context of the callback function.
|
|
* @returns {PIXI.SignalBinding} The SignalBinding node that was added.
|
|
*/
|
|
Signal.prototype.add = function (fn, thisArg) {
|
|
if (thisArg === void 0) { thisArg = null; }
|
|
if (typeof fn !== 'function') {
|
|
throw new Error('MiniSignal#add(): First arg must be a Function.');
|
|
}
|
|
return _addSignalBinding(this, new SignalBinding(fn, false, thisArg));
|
|
};
|
|
/**
|
|
* Register a new listener that will be executed only once.
|
|
* @param {Function} fn - Callback function.
|
|
* @param {object} [thisArg] - The context of the callback function.
|
|
* @returns {PIXI.SignalBinding} The SignalBinding node that was added.
|
|
*/
|
|
Signal.prototype.once = function (fn, thisArg) {
|
|
if (thisArg === void 0) { thisArg = null; }
|
|
if (typeof fn !== 'function') {
|
|
throw new Error('MiniSignal#once(): First arg must be a Function.');
|
|
}
|
|
return _addSignalBinding(this, new SignalBinding(fn, true, thisArg));
|
|
};
|
|
/**
|
|
* Remove binding object.
|
|
* @param {PIXI.SignalBinding} node - The binding node that will be removed.
|
|
* @returns {Signal} The instance on which this method was called.
|
|
@api public */
|
|
Signal.prototype.detach = function (node) {
|
|
if (!(node instanceof SignalBinding)) {
|
|
throw new Error('MiniSignal#detach(): First arg must be a SignalBinding object.');
|
|
}
|
|
if (node._owner !== this)
|
|
{ return this; } // todo: or error?
|
|
if (node._prev)
|
|
{ node._prev._next = node._next; }
|
|
if (node._next)
|
|
{ node._next._prev = node._prev; }
|
|
if (node === this._head) { // first node
|
|
this._head = node._next;
|
|
if (node._next === null) {
|
|
this._tail = null;
|
|
}
|
|
}
|
|
else if (node === this._tail) { // last node
|
|
this._tail = node._prev;
|
|
this._tail._next = null;
|
|
}
|
|
node._owner = null;
|
|
return this;
|
|
};
|
|
/**
|
|
* Detach all listeners.
|
|
* @returns {Signal} The instance on which this method was called.
|
|
*/
|
|
Signal.prototype.detachAll = function () {
|
|
var node = this._head;
|
|
if (!node)
|
|
{ return this; }
|
|
this._head = this._tail = null;
|
|
while (node) {
|
|
node._owner = null;
|
|
node = node._next;
|
|
}
|
|
return this;
|
|
};
|
|
return Signal;
|
|
}());
|
|
|
|
/**
|
|
* function from npm package `parseUri`, converted to TS to avoid leftpad incident
|
|
* @param {string} str
|
|
* @param [opts] - options
|
|
* @param {boolean} [opts.strictMode] - type of parser
|
|
*/
|
|
function parseUri(str, opts) {
|
|
opts = opts || {};
|
|
var o = {
|
|
// eslint-disable-next-line max-len
|
|
key: ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'],
|
|
q: {
|
|
name: 'queryKey',
|
|
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
|
|
},
|
|
parser: {
|
|
// eslint-disable-next-line max-len
|
|
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
|
|
// eslint-disable-next-line max-len
|
|
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
|
|
}
|
|
};
|
|
var m = o.parser[opts.strictMode ? 'strict' : 'loose'].exec(str);
|
|
var uri = {};
|
|
var i = 14;
|
|
while (i--)
|
|
{ uri[o.key[i]] = m[i] || ''; }
|
|
uri[o.q.name] = {};
|
|
uri[o.key[12]].replace(o.q.parser, function (_t0, t1, t2) {
|
|
if (t1)
|
|
{ uri[o.q.name][t1] = t2; }
|
|
});
|
|
return uri;
|
|
}
|
|
|
|
// tests if CORS is supported in XHR, if not we need to use XDR
|
|
var useXdr;
|
|
var tempAnchor = null;
|
|
// some status constants
|
|
var STATUS_NONE = 0;
|
|
var STATUS_OK = 200;
|
|
var STATUS_EMPTY = 204;
|
|
var STATUS_IE_BUG_EMPTY = 1223;
|
|
var STATUS_TYPE_OK = 2;
|
|
// noop
|
|
function _noop$1() { }
|
|
/**
|
|
* Quick helper to set a value on one of the extension maps. Ensures there is no
|
|
* dot at the start of the extension.
|
|
* @ignore
|
|
* @param map - The map to set on.
|
|
* @param extname - The extension (or key) to set.
|
|
* @param val - The value to set.
|
|
*/
|
|
function setExtMap(map, extname, val) {
|
|
if (extname && extname.indexOf('.') === 0) {
|
|
extname = extname.substring(1);
|
|
}
|
|
if (!extname) {
|
|
return;
|
|
}
|
|
map[extname] = val;
|
|
}
|
|
/**
|
|
* Quick helper to get string xhr type.
|
|
* @ignore
|
|
* @param xhr - The request to check.
|
|
* @returns The type.
|
|
*/
|
|
function reqType(xhr) {
|
|
return xhr.toString().replace('object ', '');
|
|
}
|
|
/**
|
|
* Manages the state and loading of a resource and all child resources.
|
|
*
|
|
* Can be extended in `GlobalMixins.LoaderResource`.
|
|
* @memberof PIXI
|
|
*/
|
|
exports.LoaderResource = /** @class */ (function () {
|
|
/**
|
|
* @param {string} name - The name of the resource to load.
|
|
* @param {string|string[]} url - The url for this resource, for audio/video loads you can pass
|
|
* an array of sources.
|
|
* @param {object} [options] - The options for the load.
|
|
* @param {string|boolean} [options.crossOrigin] - Is this request cross-origin? Default is to
|
|
* determine automatically.
|
|
* @param {number} [options.timeout=0] - A timeout in milliseconds for the load. If the load takes
|
|
* longer than this time it is cancelled and the load is considered a failure. If this value is
|
|
* set to `0` then there is no explicit timeout.
|
|
* @param {PIXI.LoaderResource.LOAD_TYPE} [options.loadType=LOAD_TYPE.XHR] - How should this resource
|
|
* be loaded?
|
|
* @param {PIXI.LoaderResource.XHR_RESPONSE_TYPE} [options.xhrType=XHR_RESPONSE_TYPE.DEFAULT] - How
|
|
* should the data being loaded be interpreted when using XHR?
|
|
* @param {PIXI.LoaderResource.IMetadata} [options.metadata] - Extra configuration for middleware
|
|
* and the Resource object.
|
|
*/
|
|
function LoaderResource(name, url, options) {
|
|
/**
|
|
* The `dequeue` method that will be used a storage place for the async queue dequeue method
|
|
* used privately by the loader.
|
|
* @private
|
|
* @member {Function}
|
|
*/
|
|
this._dequeue = _noop$1;
|
|
/**
|
|
* Used a storage place for the on load binding used privately by the loader.
|
|
* @private
|
|
* @member {Function}
|
|
*/
|
|
this._onLoadBinding = null;
|
|
/**
|
|
* The timer for element loads to check if they timeout.
|
|
* @private
|
|
*/
|
|
this._elementTimer = 0;
|
|
/**
|
|
* The `complete` function bound to this resource's context.
|
|
* @private
|
|
* @type {Function}
|
|
*/
|
|
this._boundComplete = null;
|
|
/**
|
|
* The `_onError` function bound to this resource's context.
|
|
* @private
|
|
* @type {Function}
|
|
*/
|
|
this._boundOnError = null;
|
|
/**
|
|
* The `_onProgress` function bound to this resource's context.
|
|
* @private
|
|
* @type {Function}
|
|
*/
|
|
this._boundOnProgress = null;
|
|
/**
|
|
* The `_onTimeout` function bound to this resource's context.
|
|
* @private
|
|
* @type {Function}
|
|
*/
|
|
this._boundOnTimeout = null;
|
|
this._boundXhrOnError = null;
|
|
this._boundXhrOnTimeout = null;
|
|
this._boundXhrOnAbort = null;
|
|
this._boundXhrOnLoad = null;
|
|
if (typeof name !== 'string' || typeof url !== 'string') {
|
|
throw new Error('Both name and url are required for constructing a resource.');
|
|
}
|
|
options = options || {};
|
|
this._flags = 0;
|
|
// set data url flag, needs to be set early for some _determineX checks to work.
|
|
this._setFlag(LoaderResource.STATUS_FLAGS.DATA_URL, url.indexOf('data:') === 0);
|
|
this.name = name;
|
|
this.url = url;
|
|
this.extension = this._getExtension();
|
|
this.data = null;
|
|
this.crossOrigin = options.crossOrigin === true ? 'anonymous' : options.crossOrigin;
|
|
this.timeout = options.timeout || 0;
|
|
this.loadType = options.loadType || this._determineLoadType();
|
|
// The type used to load the resource via XHR. If unset, determined automatically.
|
|
this.xhrType = options.xhrType;
|
|
// Extra info for middleware, and controlling specifics about how the resource loads.
|
|
// Note that if you pass in a `loadElement`, the Resource class takes ownership of it.
|
|
// Meaning it will modify it as it sees fit.
|
|
this.metadata = options.metadata || {};
|
|
// The error that occurred while loading (if any).
|
|
this.error = null;
|
|
// The XHR object that was used to load this resource. This is only set
|
|
// when `loadType` is `LoaderResource.LOAD_TYPE.XHR`.
|
|
this.xhr = null;
|
|
// The child resources this resource owns.
|
|
this.children = [];
|
|
// The resource type.
|
|
this.type = LoaderResource.TYPE.UNKNOWN;
|
|
// The progress chunk owned by this resource.
|
|
this.progressChunk = 0;
|
|
// The `dequeue` method that will be used a storage place for the async queue dequeue method
|
|
// used privately by the loader.
|
|
this._dequeue = _noop$1;
|
|
// Used a storage place for the on load binding used privately by the loader.
|
|
this._onLoadBinding = null;
|
|
// The timer for element loads to check if they timeout.
|
|
this._elementTimer = 0;
|
|
this._boundComplete = this.complete.bind(this);
|
|
this._boundOnError = this._onError.bind(this);
|
|
this._boundOnProgress = this._onProgress.bind(this);
|
|
this._boundOnTimeout = this._onTimeout.bind(this);
|
|
// xhr callbacks
|
|
this._boundXhrOnError = this._xhrOnError.bind(this);
|
|
this._boundXhrOnTimeout = this._xhrOnTimeout.bind(this);
|
|
this._boundXhrOnAbort = this._xhrOnAbort.bind(this);
|
|
this._boundXhrOnLoad = this._xhrOnLoad.bind(this);
|
|
// Dispatched when the resource beings to load.
|
|
this.onStart = new Signal();
|
|
// Dispatched each time progress of this resource load updates.
|
|
// Not all resources types and loader systems can support this event
|
|
// so sometimes it may not be available. If the resource
|
|
// is being loaded on a modern browser, using XHR, and the remote server
|
|
// properly sets Content-Length headers, then this will be available.
|
|
this.onProgress = new Signal();
|
|
// Dispatched once this resource has loaded, if there was an error it will
|
|
// be in the `error` property.
|
|
this.onComplete = new Signal();
|
|
// Dispatched after this resource has had all the *after* middleware run on it.
|
|
this.onAfterMiddleware = new Signal();
|
|
}
|
|
/**
|
|
* Sets the load type to be used for a specific extension.
|
|
* @static
|
|
* @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
|
|
* @param {PIXI.LoaderResource.LOAD_TYPE} loadType - The load type to set it to.
|
|
*/
|
|
LoaderResource.setExtensionLoadType = function (extname, loadType) {
|
|
setExtMap(LoaderResource._loadTypeMap, extname, loadType);
|
|
};
|
|
/**
|
|
* Sets the load type to be used for a specific extension.
|
|
* @static
|
|
* @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
|
|
* @param {PIXI.LoaderResource.XHR_RESPONSE_TYPE} xhrType - The xhr type to set it to.
|
|
*/
|
|
LoaderResource.setExtensionXhrType = function (extname, xhrType) {
|
|
setExtMap(LoaderResource._xhrTypeMap, extname, xhrType);
|
|
};
|
|
Object.defineProperty(LoaderResource.prototype, "isDataUrl", {
|
|
/**
|
|
* When the resource starts to load.
|
|
* @memberof PIXI.LoaderResource
|
|
* @callback OnStartSignal
|
|
* @param {PIXI.Resource} resource - The resource that the event happened on.
|
|
*/
|
|
/**
|
|
* When the resource reports loading progress.
|
|
* @memberof PIXI.LoaderResource
|
|
* @callback OnProgressSignal
|
|
* @param {PIXI.Resource} resource - The resource that the event happened on.
|
|
* @param {number} percentage - The progress of the load in the range [0, 1].
|
|
*/
|
|
/**
|
|
* When the resource finishes loading.
|
|
* @memberof PIXI.LoaderResource
|
|
* @callback OnCompleteSignal
|
|
* @param {PIXI.Resource} resource - The resource that the event happened on.
|
|
*/
|
|
/**
|
|
* @memberof PIXI.LoaderResource
|
|
* @typedef {object} IMetadata
|
|
* @property {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [loadElement=null] - The
|
|
* element to use for loading, instead of creating one.
|
|
* @property {boolean} [skipSource=false] - Skips adding source(s) to the load element. This
|
|
* is useful if you want to pass in a `loadElement` that you already added load sources to.
|
|
* @property {string|string[]} [mimeType] - The mime type to use for the source element
|
|
* of a video/audio elment. If the urls are an array, you can pass this as an array as well
|
|
* where each index is the mime type to use for the corresponding url index.
|
|
*/
|
|
/**
|
|
* Stores whether or not this url is a data url.
|
|
* @readonly
|
|
* @member {boolean}
|
|
*/
|
|
get: function () {
|
|
return this._hasFlag(LoaderResource.STATUS_FLAGS.DATA_URL);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(LoaderResource.prototype, "isComplete", {
|
|
/**
|
|
* Describes if this resource has finished loading. Is true when the resource has completely
|
|
* loaded.
|
|
* @readonly
|
|
* @member {boolean}
|
|
*/
|
|
get: function () {
|
|
return this._hasFlag(LoaderResource.STATUS_FLAGS.COMPLETE);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(LoaderResource.prototype, "isLoading", {
|
|
/**
|
|
* Describes if this resource is currently loading. Is true when the resource starts loading,
|
|
* and is false again when complete.
|
|
* @readonly
|
|
* @member {boolean}
|
|
*/
|
|
get: function () {
|
|
return this._hasFlag(LoaderResource.STATUS_FLAGS.LOADING);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
/** Marks the resource as complete. */
|
|
LoaderResource.prototype.complete = function () {
|
|
this._clearEvents();
|
|
this._finish();
|
|
};
|
|
/**
|
|
* Aborts the loading of this resource, with an optional message.
|
|
* @param {string} message - The message to use for the error
|
|
*/
|
|
LoaderResource.prototype.abort = function (message) {
|
|
// abort can be called multiple times, ignore subsequent calls.
|
|
if (this.error) {
|
|
return;
|
|
}
|
|
// store error
|
|
this.error = new Error(message);
|
|
// clear events before calling aborts
|
|
this._clearEvents();
|
|
// abort the actual loading
|
|
if (this.xhr) {
|
|
this.xhr.abort();
|
|
}
|
|
else if (this.xdr) {
|
|
this.xdr.abort();
|
|
}
|
|
else if (this.data) {
|
|
// single source
|
|
if (this.data.src) {
|
|
this.data.src = LoaderResource.EMPTY_GIF;
|
|
}
|
|
// multi-source
|
|
else {
|
|
while (this.data.firstChild) {
|
|
this.data.removeChild(this.data.firstChild);
|
|
}
|
|
}
|
|
}
|
|
// done now.
|
|
this._finish();
|
|
};
|
|
/**
|
|
* Kicks off loading of this resource. This method is asynchronous.
|
|
* @param {PIXI.LoaderResource.OnCompleteSignal} [cb] - Optional callback to call once the resource is loaded.
|
|
*/
|
|
LoaderResource.prototype.load = function (cb) {
|
|
var _this = this;
|
|
if (this.isLoading) {
|
|
return;
|
|
}
|
|
if (this.isComplete) {
|
|
if (cb) {
|
|
setTimeout(function () { return cb(_this); }, 1);
|
|
}
|
|
return;
|
|
}
|
|
else if (cb) {
|
|
this.onComplete.once(cb);
|
|
}
|
|
this._setFlag(LoaderResource.STATUS_FLAGS.LOADING, true);
|
|
this.onStart.dispatch(this);
|
|
// if unset, determine the value
|
|
if (this.crossOrigin === false || typeof this.crossOrigin !== 'string') {
|
|
this.crossOrigin = this._determineCrossOrigin(this.url);
|
|
}
|
|
switch (this.loadType) {
|
|
case LoaderResource.LOAD_TYPE.IMAGE:
|
|
this.type = LoaderResource.TYPE.IMAGE;
|
|
this._loadElement('image');
|
|
break;
|
|
case LoaderResource.LOAD_TYPE.AUDIO:
|
|
this.type = LoaderResource.TYPE.AUDIO;
|
|
this._loadSourceElement('audio');
|
|
break;
|
|
case LoaderResource.LOAD_TYPE.VIDEO:
|
|
this.type = LoaderResource.TYPE.VIDEO;
|
|
this._loadSourceElement('video');
|
|
break;
|
|
case LoaderResource.LOAD_TYPE.XHR:
|
|
/* falls through */
|
|
default:
|
|
if (typeof useXdr === 'undefined') {
|
|
useXdr = !!(globalThis.XDomainRequest && !('withCredentials' in (new XMLHttpRequest())));
|
|
}
|
|
if (useXdr && this.crossOrigin) {
|
|
this._loadXdr();
|
|
}
|
|
else {
|
|
this._loadXhr();
|
|
}
|
|
break;
|
|
}
|
|
};
|
|
/**
|
|
* Checks if the flag is set.
|
|
* @param flag - The flag to check.
|
|
* @returns True if the flag is set.
|
|
*/
|
|
LoaderResource.prototype._hasFlag = function (flag) {
|
|
return (this._flags & flag) !== 0;
|
|
};
|
|
/**
|
|
* (Un)Sets the flag.
|
|
* @param flag - The flag to (un)set.
|
|
* @param value - Whether to set or (un)set the flag.
|
|
*/
|
|
LoaderResource.prototype._setFlag = function (flag, value) {
|
|
this._flags = value ? (this._flags | flag) : (this._flags & ~flag);
|
|
};
|
|
/** Clears all the events from the underlying loading source. */
|
|
LoaderResource.prototype._clearEvents = function () {
|
|
clearTimeout(this._elementTimer);
|
|
if (this.data && this.data.removeEventListener) {
|
|
this.data.removeEventListener('error', this._boundOnError, false);
|
|
this.data.removeEventListener('load', this._boundComplete, false);
|
|
this.data.removeEventListener('progress', this._boundOnProgress, false);
|
|
this.data.removeEventListener('canplaythrough', this._boundComplete, false);
|
|
}
|
|
if (this.xhr) {
|
|
if (this.xhr.removeEventListener) {
|
|
this.xhr.removeEventListener('error', this._boundXhrOnError, false);
|
|
this.xhr.removeEventListener('timeout', this._boundXhrOnTimeout, false);
|
|
this.xhr.removeEventListener('abort', this._boundXhrOnAbort, false);
|
|
this.xhr.removeEventListener('progress', this._boundOnProgress, false);
|
|
this.xhr.removeEventListener('load', this._boundXhrOnLoad, false);
|
|
}
|
|
else {
|
|
this.xhr.onerror = null;
|
|
this.xhr.ontimeout = null;
|
|
this.xhr.onprogress = null;
|
|
this.xhr.onload = null;
|
|
}
|
|
}
|
|
};
|
|
/** Finalizes the load. */
|
|
LoaderResource.prototype._finish = function () {
|
|
if (this.isComplete) {
|
|
throw new Error('Complete called again for an already completed resource.');
|
|
}
|
|
this._setFlag(LoaderResource.STATUS_FLAGS.COMPLETE, true);
|
|
this._setFlag(LoaderResource.STATUS_FLAGS.LOADING, false);
|
|
this.onComplete.dispatch(this);
|
|
};
|
|
/**
|
|
* Loads this resources using an element that has a single source,
|
|
* like an HTMLImageElement.
|
|
* @private
|
|
* @param type - The type of element to use.
|
|
*/
|
|
LoaderResource.prototype._loadElement = function (type) {
|
|
if (this.metadata.loadElement) {
|
|
this.data = this.metadata.loadElement;
|
|
}
|
|
else if (type === 'image' && typeof globalThis.Image !== 'undefined') {
|
|
this.data = new Image();
|
|
}
|
|
else {
|
|
this.data = document.createElement(type);
|
|
}
|
|
if (this.crossOrigin) {
|
|
this.data.crossOrigin = this.crossOrigin;
|
|
}
|
|
if (!this.metadata.skipSource) {
|
|
this.data.src = this.url;
|
|
}
|
|
this.data.addEventListener('error', this._boundOnError, false);
|
|
this.data.addEventListener('load', this._boundComplete, false);
|
|
this.data.addEventListener('progress', this._boundOnProgress, false);
|
|
if (this.timeout) {
|
|
this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout);
|
|
}
|
|
};
|
|
/**
|
|
* Loads this resources using an element that has multiple sources,
|
|
* like an HTMLAudioElement or HTMLVideoElement.
|
|
* @param type - The type of element to use.
|
|
*/
|
|
LoaderResource.prototype._loadSourceElement = function (type) {
|
|
if (this.metadata.loadElement) {
|
|
this.data = this.metadata.loadElement;
|
|
}
|
|
else if (type === 'audio' && typeof globalThis.Audio !== 'undefined') {
|
|
this.data = new Audio();
|
|
}
|
|
else {
|
|
this.data = document.createElement(type);
|
|
}
|
|
if (this.data === null) {
|
|
this.abort("Unsupported element: " + type);
|
|
return;
|
|
}
|
|
if (this.crossOrigin) {
|
|
this.data.crossOrigin = this.crossOrigin;
|
|
}
|
|
if (!this.metadata.skipSource) {
|
|
// support for CocoonJS Canvas+ runtime, lacks document.createElement('source')
|
|
if (navigator.isCocoonJS) {
|
|
this.data.src = Array.isArray(this.url) ? this.url[0] : this.url;
|
|
}
|
|
else if (Array.isArray(this.url)) {
|
|
var mimeTypes = this.metadata.mimeType;
|
|
for (var i = 0; i < this.url.length; ++i) {
|
|
this.data.appendChild(this._createSource(type, this.url[i], Array.isArray(mimeTypes) ? mimeTypes[i] : mimeTypes));
|
|
}
|
|
}
|
|
else {
|
|
var mimeTypes = this.metadata.mimeType;
|
|
this.data.appendChild(this._createSource(type, this.url, Array.isArray(mimeTypes) ? mimeTypes[0] : mimeTypes));
|
|
}
|
|
}
|
|
this.data.addEventListener('error', this._boundOnError, false);
|
|
this.data.addEventListener('load', this._boundComplete, false);
|
|
this.data.addEventListener('progress', this._boundOnProgress, false);
|
|
this.data.addEventListener('canplaythrough', this._boundComplete, false);
|
|
this.data.load();
|
|
if (this.timeout) {
|
|
this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout);
|
|
}
|
|
};
|
|
/** Loads this resources using an XMLHttpRequest. */
|
|
LoaderResource.prototype._loadXhr = function () {
|
|
// if unset, determine the value
|
|
if (typeof this.xhrType !== 'string') {
|
|
this.xhrType = this._determineXhrType();
|
|
}
|
|
var xhr = this.xhr = new XMLHttpRequest();
|
|
// send credentials when crossOrigin with credentials requested
|
|
if (this.crossOrigin === 'use-credentials') {
|
|
xhr.withCredentials = true;
|
|
}
|
|
// set the request type and url
|
|
xhr.open('GET', this.url, true);
|
|
xhr.timeout = this.timeout;
|
|
// load json as text and parse it ourselves. We do this because some browsers
|
|
// *cough* safari *cough* can't deal with it.
|
|
if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.JSON
|
|
|| this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT) {
|
|
xhr.responseType = LoaderResource.XHR_RESPONSE_TYPE.TEXT;
|
|
}
|
|
else {
|
|
xhr.responseType = this.xhrType;
|
|
}
|
|
xhr.addEventListener('error', this._boundXhrOnError, false);
|
|
xhr.addEventListener('timeout', this._boundXhrOnTimeout, false);
|
|
xhr.addEventListener('abort', this._boundXhrOnAbort, false);
|
|
xhr.addEventListener('progress', this._boundOnProgress, false);
|
|
xhr.addEventListener('load', this._boundXhrOnLoad, false);
|
|
xhr.send();
|
|
};
|
|
/** Loads this resources using an XDomainRequest. This is here because we need to support IE9 (gross). */
|
|
LoaderResource.prototype._loadXdr = function () {
|
|
// if unset, determine the value
|
|
if (typeof this.xhrType !== 'string') {
|
|
this.xhrType = this._determineXhrType();
|
|
}
|
|
var xdr = this.xhr = new globalThis.XDomainRequest(); // eslint-disable-line no-undef
|
|
// XDomainRequest has a few quirks. Occasionally it will abort requests
|
|
// A way to avoid this is to make sure ALL callbacks are set even if not used
|
|
// More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9
|
|
xdr.timeout = this.timeout || 5000; // XDR needs a timeout value or it breaks in IE9
|
|
xdr.onerror = this._boundXhrOnError;
|
|
xdr.ontimeout = this._boundXhrOnTimeout;
|
|
xdr.onprogress = this._boundOnProgress;
|
|
xdr.onload = this._boundXhrOnLoad;
|
|
xdr.open('GET', this.url, true);
|
|
// Note: The xdr.send() call is wrapped in a timeout to prevent an
|
|
// issue with the interface where some requests are lost if multiple
|
|
// XDomainRequests are being sent at the same time.
|
|
// Some info here: https://github.com/photonstorm/phaser/issues/1248
|
|
setTimeout(function () { return xdr.send(); }, 1);
|
|
};
|
|
/**
|
|
* Creates a source used in loading via an element.
|
|
* @param type - The element type (video or audio).
|
|
* @param url - The source URL to load from.
|
|
* @param [mime] - The mime type of the video
|
|
* @returns The source element.
|
|
*/
|
|
LoaderResource.prototype._createSource = function (type, url, mime) {
|
|
if (!mime) {
|
|
mime = type + "/" + this._getExtension(url);
|
|
}
|
|
var source = document.createElement('source');
|
|
source.src = url;
|
|
source.type = mime;
|
|
return source;
|
|
};
|
|
/**
|
|
* Called if a load errors out.
|
|
* @param event - The error event from the element that emits it.
|
|
*/
|
|
LoaderResource.prototype._onError = function (event) {
|
|
this.abort("Failed to load element using: " + event.target.nodeName);
|
|
};
|
|
/**
|
|
* Called if a load progress event fires for an element or xhr/xdr.
|
|
* @param event - Progress event.
|
|
*/
|
|
LoaderResource.prototype._onProgress = function (event) {
|
|
if (event && event.lengthComputable) {
|
|
this.onProgress.dispatch(this, event.loaded / event.total);
|
|
}
|
|
};
|
|
/** Called if a timeout event fires for an element. */
|
|
LoaderResource.prototype._onTimeout = function () {
|
|
this.abort("Load timed out.");
|
|
};
|
|
/** Called if an error event fires for xhr/xdr. */
|
|
LoaderResource.prototype._xhrOnError = function () {
|
|
var xhr = this.xhr;
|
|
this.abort(reqType(xhr) + " Request failed. Status: " + xhr.status + ", text: \"" + xhr.statusText + "\"");
|
|
};
|
|
/** Called if an error event fires for xhr/xdr. */
|
|
LoaderResource.prototype._xhrOnTimeout = function () {
|
|
var xhr = this.xhr;
|
|
this.abort(reqType(xhr) + " Request timed out.");
|
|
};
|
|
/** Called if an abort event fires for xhr/xdr. */
|
|
LoaderResource.prototype._xhrOnAbort = function () {
|
|
var xhr = this.xhr;
|
|
this.abort(reqType(xhr) + " Request was aborted by the user.");
|
|
};
|
|
/** Called when data successfully loads from an xhr/xdr request. */
|
|
LoaderResource.prototype._xhrOnLoad = function () {
|
|
var xhr = this.xhr;
|
|
var text = '';
|
|
var status = typeof xhr.status === 'undefined' ? STATUS_OK : xhr.status; // XDR has no `.status`, assume 200.
|
|
// responseText is accessible only if responseType is '' or 'text' and on older browsers
|
|
if (xhr.responseType === '' || xhr.responseType === 'text' || typeof xhr.responseType === 'undefined') {
|
|
text = xhr.responseText;
|
|
}
|
|
// status can be 0 when using the `file://` protocol so we also check if a response is set.
|
|
// If it has a response, we assume 200; otherwise a 0 status code with no contents is an aborted request.
|
|
if (status === STATUS_NONE && (text.length > 0 || xhr.responseType === LoaderResource.XHR_RESPONSE_TYPE.BUFFER)) {
|
|
status = STATUS_OK;
|
|
}
|
|
// handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
|
|
else if (status === STATUS_IE_BUG_EMPTY) {
|
|
status = STATUS_EMPTY;
|
|
}
|
|
var statusType = (status / 100) | 0;
|
|
if (statusType === STATUS_TYPE_OK) {
|
|
// if text, just return it
|
|
if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.TEXT) {
|
|
this.data = text;
|
|
this.type = LoaderResource.TYPE.TEXT;
|
|
}
|
|
// if json, parse into json object
|
|
else if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.JSON) {
|
|
try {
|
|
this.data = JSON.parse(text);
|
|
this.type = LoaderResource.TYPE.JSON;
|
|
}
|
|
catch (e) {
|
|
this.abort("Error trying to parse loaded json: " + e);
|
|
return;
|
|
}
|
|
}
|
|
// if xml, parse into an xml document or div element
|
|
else if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT) {
|
|
try {
|
|
if (globalThis.DOMParser) {
|
|
var domparser = new DOMParser();
|
|
this.data = domparser.parseFromString(text, 'text/xml');
|
|
}
|
|
else {
|
|
var div = document.createElement('div');
|
|
div.innerHTML = text;
|
|
this.data = div;
|
|
}
|
|
this.type = LoaderResource.TYPE.XML;
|
|
}
|
|
catch (e$1) {
|
|
this.abort("Error trying to parse loaded xml: " + e$1);
|
|
return;
|
|
}
|
|
}
|
|
// other types just return the response
|
|
else {
|
|
this.data = xhr.response || text;
|
|
}
|
|
}
|
|
else {
|
|
this.abort("[" + xhr.status + "] " + xhr.statusText + ": " + xhr.responseURL);
|
|
return;
|
|
}
|
|
this.complete();
|
|
};
|
|
/**
|
|
* Sets the `crossOrigin` property for this resource based on if the url
|
|
* for this resource is cross-origin. If crossOrigin was manually set, this
|
|
* function does nothing.
|
|
* @private
|
|
* @param url - The url to test.
|
|
* @param [loc=globalThis.location] - The location object to test against.
|
|
* @returns The crossOrigin value to use (or empty string for none).
|
|
*/
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
LoaderResource.prototype._determineCrossOrigin = function (url, loc) {
|
|
// data: and javascript: urls are considered same-origin
|
|
if (url.indexOf('data:') === 0) {
|
|
return '';
|
|
}
|
|
// A sandboxed iframe without the 'allow-same-origin' attribute will have a special
|
|
// origin designed not to match globalThis.location.origin, and will always require
|
|
// crossOrigin requests regardless of whether the location matches.
|
|
if (globalThis.origin !== globalThis.location.origin) {
|
|
return 'anonymous';
|
|
}
|
|
// default is globalThis.location
|
|
loc = loc || globalThis.location;
|
|
if (!tempAnchor) {
|
|
tempAnchor = document.createElement('a');
|
|
}
|
|
// let the browser determine the full href for the url of this resource and then
|
|
// parse with the node url lib, we can't use the properties of the anchor element
|
|
// because they don't work in IE9 :(
|
|
tempAnchor.href = url;
|
|
var parsedUrl = parseUri(tempAnchor.href, { strictMode: true });
|
|
var samePort = (!parsedUrl.port && loc.port === '') || (parsedUrl.port === loc.port);
|
|
var protocol = parsedUrl.protocol ? parsedUrl.protocol + ":" : '';
|
|
// if cross origin
|
|
if (parsedUrl.host !== loc.hostname || !samePort || protocol !== loc.protocol) {
|
|
return 'anonymous';
|
|
}
|
|
return '';
|
|
};
|
|
/**
|
|
* Determines the responseType of an XHR request based on the extension of the
|
|
* resource being loaded.
|
|
* @private
|
|
* @returns {PIXI.LoaderResource.XHR_RESPONSE_TYPE} The responseType to use.
|
|
*/
|
|
LoaderResource.prototype._determineXhrType = function () {
|
|
return LoaderResource._xhrTypeMap[this.extension] || LoaderResource.XHR_RESPONSE_TYPE.TEXT;
|
|
};
|
|
/**
|
|
* Determines the loadType of a resource based on the extension of the
|
|
* resource being loaded.
|
|
* @private
|
|
* @returns {PIXI.LoaderResource.LOAD_TYPE} The loadType to use.
|
|
*/
|
|
LoaderResource.prototype._determineLoadType = function () {
|
|
return LoaderResource._loadTypeMap[this.extension] || LoaderResource.LOAD_TYPE.XHR;
|
|
};
|
|
/**
|
|
* Extracts the extension (sans '.') of the file being loaded by the resource.
|
|
* @param [url] - url to parse, `this.url` by default.
|
|
* @returns The extension.
|
|
*/
|
|
LoaderResource.prototype._getExtension = function (url) {
|
|
if (url === void 0) { url = this.url; }
|
|
var ext = '';
|
|
if (this.isDataUrl) {
|
|
var slashIndex = url.indexOf('/');
|
|
ext = url.substring(slashIndex + 1, url.indexOf(';', slashIndex));
|
|
}
|
|
else {
|
|
var queryStart = url.indexOf('?');
|
|
var hashStart = url.indexOf('#');
|
|
var index = Math.min(queryStart > -1 ? queryStart : url.length, hashStart > -1 ? hashStart : url.length);
|
|
url = url.substring(0, index);
|
|
ext = url.substring(url.lastIndexOf('.') + 1);
|
|
}
|
|
return ext.toLowerCase();
|
|
};
|
|
/**
|
|
* Determines the mime type of an XHR request based on the responseType of
|
|
* resource being loaded.
|
|
* @param type - The type to get a mime type for.
|
|
* @private
|
|
* @returns The mime type to use.
|
|
*/
|
|
LoaderResource.prototype._getMimeFromXhrType = function (type) {
|
|
switch (type) {
|
|
case LoaderResource.XHR_RESPONSE_TYPE.BUFFER:
|
|
return 'application/octet-binary';
|
|
case LoaderResource.XHR_RESPONSE_TYPE.BLOB:
|
|
return 'application/blob';
|
|
case LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT:
|
|
return 'application/xml';
|
|
case LoaderResource.XHR_RESPONSE_TYPE.JSON:
|
|
return 'application/json';
|
|
case LoaderResource.XHR_RESPONSE_TYPE.DEFAULT:
|
|
case LoaderResource.XHR_RESPONSE_TYPE.TEXT:
|
|
/* falls through */
|
|
default:
|
|
return 'text/plain';
|
|
}
|
|
};
|
|
return LoaderResource;
|
|
}());
|
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
(function (LoaderResource) {
|
|
/**
|
|
* The types of resources a resource could represent.
|
|
* @static
|
|
* @readonly
|
|
* @enum {number}
|
|
* @memberof PIXI.LoaderResource
|
|
*/
|
|
var STATUS_FLAGS;
|
|
(function (STATUS_FLAGS) {
|
|
/** None */
|
|
STATUS_FLAGS[STATUS_FLAGS["NONE"] = 0] = "NONE";
|
|
/** Data URL */
|
|
STATUS_FLAGS[STATUS_FLAGS["DATA_URL"] = 1] = "DATA_URL";
|
|
/** Complete */
|
|
STATUS_FLAGS[STATUS_FLAGS["COMPLETE"] = 2] = "COMPLETE";
|
|
/** Loading */
|
|
STATUS_FLAGS[STATUS_FLAGS["LOADING"] = 4] = "LOADING";
|
|
})(STATUS_FLAGS = LoaderResource.STATUS_FLAGS || (LoaderResource.STATUS_FLAGS = {}));
|
|
/**
|
|
* The types of resources a resource could represent.
|
|
* @static
|
|
* @readonly
|
|
* @enum {number}
|
|
* @memberof PIXI.LoaderResource
|
|
*/
|
|
var TYPE;
|
|
(function (TYPE) {
|
|
/** Unknown */
|
|
TYPE[TYPE["UNKNOWN"] = 0] = "UNKNOWN";
|
|
/** JSON */
|
|
TYPE[TYPE["JSON"] = 1] = "JSON";
|
|
/** XML */
|
|
TYPE[TYPE["XML"] = 2] = "XML";
|
|
/** Image */
|
|
TYPE[TYPE["IMAGE"] = 3] = "IMAGE";
|
|
/** Audio */
|
|
TYPE[TYPE["AUDIO"] = 4] = "AUDIO";
|
|
/** Video */
|
|
TYPE[TYPE["VIDEO"] = 5] = "VIDEO";
|
|
/** Plain text */
|
|
TYPE[TYPE["TEXT"] = 6] = "TEXT";
|
|
})(TYPE = LoaderResource.TYPE || (LoaderResource.TYPE = {}));
|
|
/**
|
|
* The types of loading a resource can use.
|
|
* @static
|
|
* @readonly
|
|
* @enum {number}
|
|
* @memberof PIXI.LoaderResource
|
|
*/
|
|
var LOAD_TYPE;
|
|
(function (LOAD_TYPE) {
|
|
/** Uses XMLHttpRequest to load the resource. */
|
|
LOAD_TYPE[LOAD_TYPE["XHR"] = 1] = "XHR";
|
|
/** Uses an `Image` object to load the resource. */
|
|
LOAD_TYPE[LOAD_TYPE["IMAGE"] = 2] = "IMAGE";
|
|
/** Uses an `Audio` object to load the resource. */
|
|
LOAD_TYPE[LOAD_TYPE["AUDIO"] = 3] = "AUDIO";
|
|
/** Uses a `Video` object to load the resource. */
|
|
LOAD_TYPE[LOAD_TYPE["VIDEO"] = 4] = "VIDEO";
|
|
})(LOAD_TYPE = LoaderResource.LOAD_TYPE || (LoaderResource.LOAD_TYPE = {}));
|
|
/**
|
|
* The XHR ready states, used internally.
|
|
* @static
|
|
* @readonly
|
|
* @enum {string}
|
|
* @memberof PIXI.LoaderResource
|
|
*/
|
|
var XHR_RESPONSE_TYPE;
|
|
(function (XHR_RESPONSE_TYPE) {
|
|
/** string */
|
|
XHR_RESPONSE_TYPE["DEFAULT"] = "text";
|
|
/** ArrayBuffer */
|
|
XHR_RESPONSE_TYPE["BUFFER"] = "arraybuffer";
|
|
/** Blob */
|
|
XHR_RESPONSE_TYPE["BLOB"] = "blob";
|
|
/** Document */
|
|
XHR_RESPONSE_TYPE["DOCUMENT"] = "document";
|
|
/** Object */
|
|
XHR_RESPONSE_TYPE["JSON"] = "json";
|
|
/** String */
|
|
XHR_RESPONSE_TYPE["TEXT"] = "text";
|
|
})(XHR_RESPONSE_TYPE = LoaderResource.XHR_RESPONSE_TYPE || (LoaderResource.XHR_RESPONSE_TYPE = {}));
|
|
LoaderResource._loadTypeMap = {
|
|
// images
|
|
gif: LoaderResource.LOAD_TYPE.IMAGE,
|
|
png: LoaderResource.LOAD_TYPE.IMAGE,
|
|
bmp: LoaderResource.LOAD_TYPE.IMAGE,
|
|
jpg: LoaderResource.LOAD_TYPE.IMAGE,
|
|
jpeg: LoaderResource.LOAD_TYPE.IMAGE,
|
|
tif: LoaderResource.LOAD_TYPE.IMAGE,
|
|
tiff: LoaderResource.LOAD_TYPE.IMAGE,
|
|
webp: LoaderResource.LOAD_TYPE.IMAGE,
|
|
tga: LoaderResource.LOAD_TYPE.IMAGE,
|
|
avif: LoaderResource.LOAD_TYPE.IMAGE,
|
|
svg: LoaderResource.LOAD_TYPE.IMAGE,
|
|
'svg+xml': LoaderResource.LOAD_TYPE.IMAGE,
|
|
// audio
|
|
mp3: LoaderResource.LOAD_TYPE.AUDIO,
|
|
ogg: LoaderResource.LOAD_TYPE.AUDIO,
|
|
wav: LoaderResource.LOAD_TYPE.AUDIO,
|
|
// videos
|
|
mp4: LoaderResource.LOAD_TYPE.VIDEO,
|
|
webm: LoaderResource.LOAD_TYPE.VIDEO,
|
|
};
|
|
LoaderResource._xhrTypeMap = {
|
|
// xml
|
|
xhtml: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
html: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
htm: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
xml: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
tmx: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
svg: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
// This was added to handle Tiled Tileset XML, but .tsx is also a TypeScript React Component.
|
|
// Since it is way less likely for people to be loading TypeScript files instead of Tiled files,
|
|
// this should probably be fine.
|
|
tsx: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
|
|
// images
|
|
gif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
png: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
bmp: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
jpg: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
jpeg: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
tif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
tiff: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
webp: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
tga: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
avif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
|
|
// json
|
|
json: LoaderResource.XHR_RESPONSE_TYPE.JSON,
|
|
// text
|
|
text: LoaderResource.XHR_RESPONSE_TYPE.TEXT,
|
|
txt: LoaderResource.XHR_RESPONSE_TYPE.TEXT,
|
|
// fonts
|
|
ttf: LoaderResource.XHR_RESPONSE_TYPE.BUFFER,
|
|
otf: LoaderResource.XHR_RESPONSE_TYPE.BUFFER,
|
|
};
|
|
// We can't set the `src` attribute to empty string, so on abort we set it to this 1px transparent gif
|
|
LoaderResource.EMPTY_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
|
|
})(exports.LoaderResource || (exports.LoaderResource = {}));
|
|
|
|
/**
|
|
* Smaller version of the async library constructs.
|
|
* @ignore
|
|
*/
|
|
function _noop() {
|
|
}
|
|
/**
|
|
* Ensures a function is only called once.
|
|
* @ignore
|
|
* @param {Function} fn - The function to wrap.
|
|
* @returns {Function} The wrapping function.
|
|
*/
|
|
function onlyOnce(fn) {
|
|
return function onceWrapper() {
|
|
var arguments$1 = arguments;
|
|
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments$1[_i];
|
|
}
|
|
if (fn === null) {
|
|
throw new Error('Callback was already called.');
|
|
}
|
|
var callFn = fn;
|
|
fn = null;
|
|
callFn.apply(this, args);
|
|
};
|
|
}
|
|
/**
|
|
* @private
|
|
* @memberof PIXI
|
|
*/
|
|
var AsyncQueueItem = /** @class */ (function () {
|
|
/**
|
|
* @param data
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
function AsyncQueueItem(data, callback) {
|
|
this.data = data;
|
|
this.callback = callback;
|
|
}
|
|
return AsyncQueueItem;
|
|
}());
|
|
/**
|
|
* @private
|
|
* @memberof PIXI
|
|
*/
|
|
var AsyncQueue = /** @class */ (function () {
|
|
/**
|
|
* @param worker
|
|
* @param concurrency
|
|
* @private
|
|
*/
|
|
function AsyncQueue(worker, concurrency) {
|
|
var _this = this;
|
|
if (concurrency === void 0) { concurrency = 1; }
|
|
this.workers = 0;
|
|
this.saturated = _noop;
|
|
this.unsaturated = _noop;
|
|
this.empty = _noop;
|
|
this.drain = _noop;
|
|
this.error = _noop;
|
|
this.started = false;
|
|
this.paused = false;
|
|
this._tasks = [];
|
|
this._insert = function (data, insertAtFront, callback) {
|
|
if (callback && typeof callback !== 'function') {
|
|
throw new Error('task callback must be a function');
|
|
}
|
|
_this.started = true;
|
|
// eslint-disable-next-line no-eq-null,eqeqeq
|
|
if (data == null && _this.idle()) {
|
|
// call drain immediately if there are no tasks
|
|
setTimeout(function () { return _this.drain(); }, 1);
|
|
return;
|
|
}
|
|
var item = new AsyncQueueItem(data, typeof callback === 'function' ? callback : _noop);
|
|
if (insertAtFront) {
|
|
_this._tasks.unshift(item);
|
|
}
|
|
else {
|
|
_this._tasks.push(item);
|
|
}
|
|
setTimeout(_this.process, 1);
|
|
};
|
|
this.process = function () {
|
|
while (!_this.paused && _this.workers < _this.concurrency && _this._tasks.length) {
|
|
var task = _this._tasks.shift();
|
|
if (_this._tasks.length === 0) {
|
|
_this.empty();
|
|
}
|
|
_this.workers += 1;
|
|
if (_this.workers === _this.concurrency) {
|
|
_this.saturated();
|
|
}
|
|
_this._worker(task.data, onlyOnce(_this._next(task)));
|
|
}
|
|
};
|
|
this._worker = worker;
|
|
if (concurrency === 0) {
|
|
throw new Error('Concurrency must not be zero');
|
|
}
|
|
this.concurrency = concurrency;
|
|
this.buffer = concurrency / 4.0;
|
|
}
|
|
/**
|
|
* @param task
|
|
* @private
|
|
*/
|
|
AsyncQueue.prototype._next = function (task) {
|
|
var _this = this;
|
|
return function () {
|
|
var arguments$1 = arguments;
|
|
|
|
var args = [];
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
args[_i] = arguments$1[_i];
|
|
}
|
|
_this.workers -= 1;
|
|
task.callback.apply(task, args);
|
|
// eslint-disable-next-line no-eq-null,eqeqeq
|
|
if (args[0] != null) {
|
|
_this.error(args[0], task.data);
|
|
}
|
|
if (_this.workers <= (_this.concurrency - _this.buffer)) {
|
|
_this.unsaturated();
|
|
}
|
|
if (_this.idle()) {
|
|
_this.drain();
|
|
}
|
|
_this.process();
|
|
};
|
|
};
|
|
// That was in object
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
AsyncQueue.prototype.push = function (data, callback) {
|
|
this._insert(data, false, callback);
|
|
};
|
|
AsyncQueue.prototype.kill = function () {
|
|
this.workers = 0;
|
|
this.drain = _noop;
|
|
this.started = false;
|
|
this._tasks = [];
|
|
};
|
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
AsyncQueue.prototype.unshift = function (data, callback) {
|
|
this._insert(data, true, callback);
|
|
};
|
|
AsyncQueue.prototype.length = function () {
|
|
return this._tasks.length;
|
|
};
|
|
AsyncQueue.prototype.running = function () {
|
|
return this.workers;
|
|
};
|
|
AsyncQueue.prototype.idle = function () {
|
|
return this._tasks.length + this.workers === 0;
|
|
};
|
|
AsyncQueue.prototype.pause = function () {
|
|
if (this.paused === true) {
|
|
return;
|
|
}
|
|
this.paused = true;
|
|
};
|
|
AsyncQueue.prototype.resume = function () {
|
|
if (this.paused === false) {
|
|
return;
|
|
}
|
|
this.paused = false;
|
|
// Need to call this.process once per concurrent
|
|
// worker to preserve full concurrency after pause
|
|
for (var w = 1; w <= this.concurrency; w++) {
|
|
this.process();
|
|
}
|
|
};
|
|
/**
|
|
* Iterates an array in series.
|
|
* @param {Array.<*>} array - Array to iterate.
|
|
* @param {Function} iterator - Function to call for each element.
|
|
* @param {Function} callback - Function to call when done, or on error.
|
|
* @param {boolean} [deferNext=false] - Break synchronous each loop by calling next with a setTimeout of 1.
|
|
*/
|
|
AsyncQueue.eachSeries = function (array, iterator, callback, deferNext) {
|
|
var i = 0;
|
|
var len = array.length;
|
|
function next(err) {
|
|
if (err || i === len) {
|
|
if (callback) {
|
|
callback(err);
|
|
}
|
|
return;
|
|
}
|
|
if (deferNext) {
|
|
setTimeout(function () {
|
|
iterator(array[i++], next);
|
|
}, 1);
|
|
}
|
|
else {
|
|
iterator(array[i++], next);
|
|
}
|
|
}
|
|
next();
|
|
};
|
|
/**
|
|
* Async queue implementation,
|
|
* @param {Function} worker - The worker function to call for each task.
|
|
* @param {number} concurrency - How many workers to run in parrallel.
|
|
* @returns {*} The async queue object.
|
|
*/
|
|
AsyncQueue.queue = function (worker, concurrency) {
|
|
return new AsyncQueue(worker, concurrency);
|
|
};
|
|
return AsyncQueue;
|
|
}());
|
|
|
|
// some constants
|
|
var MAX_PROGRESS = 100;
|
|
var rgxExtractUrlHash = /(#[\w-]+)?$/;
|
|
/**
|
|
* The new loader, forked from Resource Loader by Chad Engler: https://github.com/englercj/resource-loader
|
|
*
|
|
* ```js
|
|
* const loader = PIXI.Loader.shared; // PixiJS exposes a premade instance for you to use.
|
|
* // or
|
|
* const loader = new PIXI.Loader(); // You can also create your own if you want
|
|
*
|
|
* const sprites = {};
|
|
*
|
|
* // Chainable `add` to enqueue a resource
|
|
* loader.add('bunny', 'data/bunny.png')
|
|
* .add('spaceship', 'assets/spritesheet.json');
|
|
* loader.add('scoreFont', 'assets/score.fnt');
|
|
*
|
|
* // Chainable `pre` to add a middleware that runs for each resource, *before* loading that resource.
|
|
* // This is useful to implement custom caching modules (using filesystem, indexeddb, memory, etc).
|
|
* loader.pre(cachingMiddleware);
|
|
*
|
|
* // Chainable `use` to add a middleware that runs for each resource, *after* loading that resource.
|
|
* // This is useful to implement custom parsing modules (like spritesheet parsers, spine parser, etc).
|
|
* loader.use(parsingMiddleware);
|
|
*
|
|
* // The `load` method loads the queue of resources, and calls the passed in callback called once all
|
|
* // resources have loaded.
|
|
* loader.load((loader, resources) => {
|
|
* // resources is an object where the key is the name of the resource loaded and the value is the resource object.
|
|
* // They have a couple default properties:
|
|
* // - `url`: The URL that the resource was loaded from
|
|
* // - `error`: The error that happened when trying to load (if any)
|
|
* // - `data`: The raw data that was loaded
|
|
* // also may contain other properties based on the middleware that runs.
|
|
* sprites.bunny = new PIXI.TilingSprite(resources.bunny.texture);
|
|
* sprites.spaceship = new PIXI.TilingSprite(resources.spaceship.texture);
|
|
* sprites.scoreFont = new PIXI.TilingSprite(resources.scoreFont.texture);
|
|
* });
|
|
*
|
|
* // throughout the process multiple signals can be dispatched.
|
|
* loader.onProgress.add(() => {}); // called once per loaded/errored file
|
|
* loader.onError.add(() => {}); // called once per errored file
|
|
* loader.onLoad.add(() => {}); // called once per loaded file
|
|
* loader.onComplete.add(() => {}); // called once when the queued resources all load.
|
|
* ```
|
|
* @memberof PIXI
|
|
*/
|
|
var Loader = /** @class */ (function () {
|
|
/**
|
|
* @param baseUrl - The base url for all resources loaded by this loader.
|
|
* @param concurrency - The number of resources to load concurrently.
|
|
*/
|
|
function Loader(baseUrl, concurrency) {
|
|
var _this = this;
|
|
if (baseUrl === void 0) { baseUrl = ''; }
|
|
if (concurrency === void 0) { concurrency = 10; }
|
|
/** The progress percent of the loader going through the queue. */
|
|
this.progress = 0;
|
|
/** Loading state of the loader, true if it is currently loading resources. */
|
|
this.loading = false;
|
|
/**
|
|
* A querystring to append to every URL added to the loader.
|
|
*
|
|
* This should be a valid query string *without* the question-mark (`?`). The loader will
|
|
* also *not* escape values for you. Make sure to escape your parameters with
|
|
* [`encodeURIComponent`](https://mdn.io/encodeURIComponent) before assigning this property.
|
|
* @example
|
|
* const loader = new Loader();
|
|
*
|
|
* loader.defaultQueryString = 'user=me&password=secret';
|
|
*
|
|
* // This will request 'image.png?user=me&password=secret'
|
|
* loader.add('image.png').load();
|
|
*
|
|
* loader.reset();
|
|
*
|
|
* // This will request 'image.png?v=1&user=me&password=secret'
|
|
* loader.add('iamge.png?v=1').load();
|
|
*/
|
|
this.defaultQueryString = '';
|
|
/** The middleware to run before loading each resource. */
|
|
this._beforeMiddleware = [];
|
|
/** The middleware to run after loading each resource. */
|
|
this._afterMiddleware = [];
|
|
/** The tracks the resources we are currently completing parsing for. */
|
|
this._resourcesParsing = [];
|
|
/**
|
|
* The `_loadResource` function bound with this object context.
|
|
* @param r - The resource to load
|
|
* @param d - The dequeue function
|
|
*/
|
|
this._boundLoadResource = function (r, d) { return _this._loadResource(r, d); };
|
|
/** All the resources for this loader keyed by name. */
|
|
this.resources = {};
|
|
this.baseUrl = baseUrl;
|
|
this._beforeMiddleware = [];
|
|
this._afterMiddleware = [];
|
|
this._resourcesParsing = [];
|
|
this._boundLoadResource = function (r, d) { return _this._loadResource(r, d); };
|
|
this._queue = AsyncQueue.queue(this._boundLoadResource, concurrency);
|
|
this._queue.pause();
|
|
this.resources = {};
|
|
this.onProgress = new Signal();
|
|
this.onError = new Signal();
|
|
this.onLoad = new Signal();
|
|
this.onStart = new Signal();
|
|
this.onComplete = new Signal();
|
|
for (var i = 0; i < Loader._plugins.length; ++i) {
|
|
var plugin = Loader._plugins[i];
|
|
var pre = plugin.pre, use = plugin.use;
|
|
if (pre) {
|
|
this.pre(pre);
|
|
}
|
|
if (use) {
|
|
this.use(use);
|
|
}
|
|
}
|
|
this._protected = false;
|
|
}
|
|
/**
|
|
* Same as add, params have strict order
|
|
* @private
|
|
* @param name - The name of the resource to load.
|
|
* @param url - The url for this resource, relative to the baseUrl of this loader.
|
|
* @param options - The options for the load.
|
|
* @param callback - Function to call when this specific resource completes loading.
|
|
* @returns The loader itself.
|
|
*/
|
|
Loader.prototype._add = function (name, url, options, callback) {
|
|
// if loading already you can only add resources that have a parent.
|
|
if (this.loading && (!options || !options.parentResource)) {
|
|
throw new Error('Cannot add resources while the loader is running.');
|
|
}
|
|
// check if resource already exists.
|
|
if (this.resources[name]) {
|
|
throw new Error("Resource named \"" + name + "\" already exists.");
|
|
}
|
|
// add base url if this isn't an absolute url
|
|
url = this._prepareUrl(url);
|
|
// create the store the resource
|
|
this.resources[name] = new exports.LoaderResource(name, url, options);
|
|
if (typeof callback === 'function') {
|
|
this.resources[name].onAfterMiddleware.once(callback);
|
|
}
|
|
// if actively loading, make sure to adjust progress chunks for that parent and its children
|
|
if (this.loading) {
|
|
var parent = options.parentResource;
|
|
var incompleteChildren = [];
|
|
for (var i = 0; i < parent.children.length; ++i) {
|
|
if (!parent.children[i].isComplete) {
|
|
incompleteChildren.push(parent.children[i]);
|
|
}
|
|
}
|
|
var fullChunk = parent.progressChunk * (incompleteChildren.length + 1); // +1 for parent
|
|
var eachChunk = fullChunk / (incompleteChildren.length + 2); // +2 for parent & new child
|
|
parent.children.push(this.resources[name]);
|
|
parent.progressChunk = eachChunk;
|
|
for (var i = 0; i < incompleteChildren.length; ++i) {
|
|
incompleteChildren[i].progressChunk = eachChunk;
|
|
}
|
|
this.resources[name].progressChunk = eachChunk;
|
|
}
|
|
// add the resource to the queue
|
|
this._queue.push(this.resources[name]);
|
|
return this;
|
|
};
|
|
/* eslint-enable require-jsdoc,valid-jsdoc */
|
|
/**
|
|
* Sets up a middleware function that will run *before* the
|
|
* resource is loaded.
|
|
* @param fn - The middleware function to register.
|
|
* @returns The loader itself.
|
|
*/
|
|
Loader.prototype.pre = function (fn) {
|
|
this._beforeMiddleware.push(fn);
|
|
return this;
|
|
};
|
|
/**
|
|
* Sets up a middleware function that will run *after* the
|
|
* resource is loaded.
|
|
* @param fn - The middleware function to register.
|
|
* @returns The loader itself.
|
|
*/
|
|
Loader.prototype.use = function (fn) {
|
|
this._afterMiddleware.push(fn);
|
|
return this;
|
|
};
|
|
/**
|
|
* Resets the queue of the loader to prepare for a new load.
|
|
* @returns The loader itself.
|
|
*/
|
|
Loader.prototype.reset = function () {
|
|
this.progress = 0;
|
|
this.loading = false;
|
|
this._queue.kill();
|
|
this._queue.pause();
|
|
// abort all resource loads
|
|
for (var k in this.resources) {
|
|
var res = this.resources[k];
|
|
if (res._onLoadBinding) {
|
|
res._onLoadBinding.detach();
|
|
}
|
|
if (res.isLoading) {
|
|
res.abort('loader reset');
|
|
}
|
|
}
|
|
this.resources = {};
|
|
return this;
|
|
};
|
|
/**
|
|
* Starts loading the queued resources.
|
|
* @param cb - Optional callback that will be bound to the `complete` event.
|
|
* @returns The loader itself.
|
|
*/
|
|
Loader.prototype.load = function (cb) {
|
|
utils.deprecation('6.5.0', '@pixi/loaders is being replaced with @pixi/assets in the next major release.');
|
|
// register complete callback if they pass one
|
|
if (typeof cb === 'function') {
|
|
this.onComplete.once(cb);
|
|
}
|
|
// if the queue has already started we are done here
|
|
if (this.loading) {
|
|
return this;
|
|
}
|
|
if (this._queue.idle()) {
|
|
this._onStart();
|
|
this._onComplete();
|
|
}
|
|
else {
|
|
// distribute progress chunks
|
|
var numTasks = this._queue._tasks.length;
|
|
var chunk = MAX_PROGRESS / numTasks;
|
|
for (var i = 0; i < this._queue._tasks.length; ++i) {
|
|
this._queue._tasks[i].data.progressChunk = chunk;
|
|
}
|
|
// notify we are starting
|
|
this._onStart();
|
|
// start loading
|
|
this._queue.resume();
|
|
}
|
|
return this;
|
|
};
|
|
Object.defineProperty(Loader.prototype, "concurrency", {
|
|
/**
|
|
* The number of resources to load concurrently.
|
|
* @default 10
|
|
*/
|
|
get: function () {
|
|
return this._queue.concurrency;
|
|
},
|
|
set: function (concurrency) {
|
|
this._queue.concurrency = concurrency;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
/**
|
|
* Prepares a url for usage based on the configuration of this object
|
|
* @param url - The url to prepare.
|
|
* @returns The prepared url.
|
|
*/
|
|
Loader.prototype._prepareUrl = function (url) {
|
|
var parsedUrl = parseUri(url, { strictMode: true });
|
|
var result;
|
|
// absolute url, just use it as is.
|
|
if (parsedUrl.protocol || !parsedUrl.path || url.indexOf('//') === 0) {
|
|
result = url;
|
|
}
|
|
// if baseUrl doesn't end in slash and url doesn't start with slash, then add a slash inbetween
|
|
else if (this.baseUrl.length
|
|
&& this.baseUrl.lastIndexOf('/') !== this.baseUrl.length - 1
|
|
&& url.charAt(0) !== '/') {
|
|
result = this.baseUrl + "/" + url;
|
|
}
|
|
else {
|
|
result = this.baseUrl + url;
|
|
}
|
|
// if we need to add a default querystring, there is a bit more work
|
|
if (this.defaultQueryString) {
|
|
var hash = rgxExtractUrlHash.exec(result)[0];
|
|
result = result.slice(0, result.length - hash.length);
|
|
if (result.indexOf('?') !== -1) {
|
|
result += "&" + this.defaultQueryString;
|
|
}
|
|
else {
|
|
result += "?" + this.defaultQueryString;
|
|
}
|
|
result += hash;
|
|
}
|
|
return result;
|
|
};
|
|
/**
|
|
* Loads a single resource.
|
|
* @param resource - The resource to load.
|
|
* @param dequeue - The function to call when we need to dequeue this item.
|
|
*/
|
|
Loader.prototype._loadResource = function (resource, dequeue) {
|
|
var _this = this;
|
|
resource._dequeue = dequeue;
|
|
// run before middleware
|
|
AsyncQueue.eachSeries(this._beforeMiddleware, function (fn, next) {
|
|
fn.call(_this, resource, function () {
|
|
// if the before middleware marks the resource as complete,
|
|
// break and don't process any more before middleware
|
|
next(resource.isComplete ? {} : null);
|
|
});
|
|
}, function () {
|
|
if (resource.isComplete) {
|
|
_this._onLoad(resource);
|
|
}
|
|
else {
|
|
resource._onLoadBinding = resource.onComplete.once(_this._onLoad, _this);
|
|
resource.load();
|
|
}
|
|
}, true);
|
|
};
|
|
/** Called once loading has started. */
|
|
Loader.prototype._onStart = function () {
|
|
this.progress = 0;
|
|
this.loading = true;
|
|
this.onStart.dispatch(this);
|
|
};
|
|
/** Called once each resource has loaded. */
|
|
Loader.prototype._onComplete = function () {
|
|
this.progress = MAX_PROGRESS;
|
|
this.loading = false;
|
|
this.onComplete.dispatch(this, this.resources);
|
|
};
|
|
/**
|
|
* Called each time a resources is loaded.
|
|
* @param resource - The resource that was loaded
|
|
*/
|
|
Loader.prototype._onLoad = function (resource) {
|
|
var _this = this;
|
|
resource._onLoadBinding = null;
|
|
// remove this resource from the async queue, and add it to our list of resources that are being parsed
|
|
this._resourcesParsing.push(resource);
|
|
resource._dequeue();
|
|
// run all the after middleware for this resource
|
|
AsyncQueue.eachSeries(this._afterMiddleware, function (fn, next) {
|
|
fn.call(_this, resource, next);
|
|
}, function () {
|
|
resource.onAfterMiddleware.dispatch(resource);
|
|
_this.progress = Math.min(MAX_PROGRESS, _this.progress + resource.progressChunk);
|
|
_this.onProgress.dispatch(_this, resource);
|
|
if (resource.error) {
|
|
_this.onError.dispatch(resource.error, _this, resource);
|
|
}
|
|
else {
|
|
_this.onLoad.dispatch(_this, resource);
|
|
}
|
|
_this._resourcesParsing.splice(_this._resourcesParsing.indexOf(resource), 1);
|
|
// do completion check
|
|
if (_this._queue.idle() && _this._resourcesParsing.length === 0) {
|
|
_this._onComplete();
|
|
}
|
|
}, true);
|
|
};
|
|
/** Destroy the loader, removes references. */
|
|
Loader.prototype.destroy = function () {
|
|
if (!this._protected) {
|
|
this.reset();
|
|
}
|
|
};
|
|
Object.defineProperty(Loader, "shared", {
|
|
/** A premade instance of the loader that can be used to load resources. */
|
|
get: function () {
|
|
var shared = Loader._shared;
|
|
if (!shared) {
|
|
shared = new Loader();
|
|
shared._protected = true;
|
|
Loader._shared = shared;
|
|
}
|
|
return shared;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
/**
|
|
* Use the {@link PIXI.extensions.add} API to register plugins.
|
|
* @deprecated since 6.5.0
|
|
* @param plugin - The plugin to add
|
|
* @returns Reference to PIXI.Loader for chaining
|
|
*/
|
|
Loader.registerPlugin = function (plugin) {
|
|
utils.deprecation('6.5.0', 'Loader.registerPlugin() is deprecated, use extensions.add() instead.');
|
|
core.extensions.add({
|
|
type: core.ExtensionType.Loader,
|
|
ref: plugin,
|
|
});
|
|
return Loader;
|
|
};
|
|
Loader._plugins = [];
|
|
return Loader;
|
|
}());
|
|
core.extensions.handleByList(core.ExtensionType.Loader, Loader._plugins);
|
|
Loader.prototype.add = function add(name, url, options, callback) {
|
|
// special case of an array of objects or urls
|
|
if (Array.isArray(name)) {
|
|
for (var i = 0; i < name.length; ++i) {
|
|
this.add(name[i]);
|
|
}
|
|
return this;
|
|
}
|
|
// if an object is passed instead of params
|
|
if (typeof name === 'object') {
|
|
options = name;
|
|
callback = url || options.callback || options.onComplete;
|
|
url = options.url;
|
|
name = options.name || options.key || options.url;
|
|
}
|
|
// case where no name is passed shift all args over by one.
|
|
if (typeof url !== 'string') {
|
|
callback = options;
|
|
options = url;
|
|
url = name;
|
|
}
|
|
// now that we shifted make sure we have a proper url.
|
|
if (typeof url !== 'string') {
|
|
throw new Error('No url passed to add resource to loader.');
|
|
}
|
|
// options are optional so people might pass a function and no options
|
|
if (typeof options === 'function') {
|
|
callback = options;
|
|
options = null;
|
|
}
|
|
return this._add(name, url, options, callback);
|
|
};
|
|
|
|
/**
|
|
* Application plugin for supporting loader option. Installing the LoaderPlugin
|
|
* is not necessary if using **pixi.js** or **pixi.js-legacy**.
|
|
* @example
|
|
* import {AppLoaderPlugin} from '@pixi/loaders';
|
|
* import {extensions} from '@pixi/core';
|
|
* extensions.add(AppLoaderPlugin);
|
|
* @memberof PIXI
|
|
*/
|
|
var AppLoaderPlugin = /** @class */ (function () {
|
|
function AppLoaderPlugin() {
|
|
}
|
|
/**
|
|
* Called on application constructor
|
|
* @param options
|
|
* @private
|
|
*/
|
|
AppLoaderPlugin.init = function (options) {
|
|
options = Object.assign({
|
|
sharedLoader: false,
|
|
}, options);
|
|
this.loader = options.sharedLoader ? Loader.shared : new Loader();
|
|
};
|
|
/**
|
|
* Called when application destroyed
|
|
* @private
|
|
*/
|
|
AppLoaderPlugin.destroy = function () {
|
|
if (this.loader) {
|
|
this.loader.destroy();
|
|
this.loader = null;
|
|
}
|
|
};
|
|
/** @ignore */
|
|
AppLoaderPlugin.extension = core.ExtensionType.Application;
|
|
return AppLoaderPlugin;
|
|
}());
|
|
|
|
/**
|
|
* Loader plugin for handling Texture resources.
|
|
* @memberof PIXI
|
|
*/
|
|
var TextureLoader = /** @class */ (function () {
|
|
function TextureLoader() {
|
|
}
|
|
/** Handle SVG elements a text, render with SVGResource. */
|
|
TextureLoader.add = function () {
|
|
exports.LoaderResource.setExtensionLoadType('svg', exports.LoaderResource.LOAD_TYPE.XHR);
|
|
exports.LoaderResource.setExtensionXhrType('svg', exports.LoaderResource.XHR_RESPONSE_TYPE.TEXT);
|
|
};
|
|
/**
|
|
* Called after a resource is loaded.
|
|
* @see PIXI.Loader.loaderMiddleware
|
|
* @param resource
|
|
* @param {Function} next
|
|
*/
|
|
TextureLoader.use = function (resource, next) {
|
|
// create a new texture if the data is an Image object
|
|
if (resource.data && (resource.type === exports.LoaderResource.TYPE.IMAGE || resource.extension === 'svg')) {
|
|
var data = resource.data, url = resource.url, name = resource.name, metadata = resource.metadata;
|
|
core.Texture.fromLoader(data, url, name, metadata).then(function (texture) {
|
|
resource.texture = texture;
|
|
next();
|
|
})
|
|
// TODO: handle errors in Texture.fromLoader
|
|
// so we can pass them to the Loader
|
|
.catch(next);
|
|
}
|
|
else {
|
|
next();
|
|
}
|
|
};
|
|
/** @ignore */
|
|
TextureLoader.extension = core.ExtensionType.Loader;
|
|
return TextureLoader;
|
|
}());
|
|
|
|
// a simple in-memory cache for resources
|
|
var cache = {};
|
|
/**
|
|
* A simple in-memory cache for resource.
|
|
* @ignore
|
|
* @function caching
|
|
* @example
|
|
* import { Loader, middleware } from 'resource-loader';
|
|
* const loader = new Loader();
|
|
* loader.use(middleware.caching);
|
|
* @param resource - Current Resource
|
|
* @param next - Callback when complete
|
|
*/
|
|
function caching(resource, next) {
|
|
// if cached, then set data and complete the resource
|
|
if (cache[resource.url]) {
|
|
resource.data = cache[resource.url];
|
|
resource.complete(); // marks resource load complete and stops processing before middlewares
|
|
}
|
|
// if not cached, wait for complete and store it in the cache.
|
|
else {
|
|
resource.onComplete.once(function doCache() { cache[this.url] = this.data; });
|
|
}
|
|
next();
|
|
}
|
|
|
|
var _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
/**
|
|
* Encodes binary into base64.
|
|
* @function encodeBinary
|
|
* @param {string} input - The input data to encode.
|
|
* @returns {string} The encoded base64 string
|
|
*/
|
|
function encodeBinary(input) {
|
|
var output = '';
|
|
var inx = 0;
|
|
while (inx < input.length) {
|
|
// Fill byte buffer array
|
|
var bytebuffer = [0, 0, 0];
|
|
var encodedCharIndexes = [0, 0, 0, 0];
|
|
for (var jnx = 0; jnx < bytebuffer.length; ++jnx) {
|
|
if (inx < input.length) {
|
|
// throw away high-order byte, as documented at:
|
|
// https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
|
|
bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
|
|
}
|
|
else {
|
|
bytebuffer[jnx] = 0;
|
|
}
|
|
}
|
|
// Get each encoded character, 6 bits at a time
|
|
// index 1: first 6 bits
|
|
encodedCharIndexes[0] = bytebuffer[0] >> 2;
|
|
// index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
|
|
encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
|
|
// index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
|
|
encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
|
|
// index 3: forth 6 bits (6 least significant bits from input byte 3)
|
|
encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
|
|
// Determine whether padding happened, and adjust accordingly
|
|
var paddingBytes = inx - (input.length - 1);
|
|
switch (paddingBytes) {
|
|
case 2:
|
|
// Set last 2 characters to padding char
|
|
encodedCharIndexes[3] = 64;
|
|
encodedCharIndexes[2] = 64;
|
|
break;
|
|
case 1:
|
|
// Set last character to padding char
|
|
encodedCharIndexes[3] = 64;
|
|
break;
|
|
default:
|
|
break; // No padding - proceed
|
|
}
|
|
// Now we will grab each appropriate character out of our keystring
|
|
// based on our index array and append it to the output string
|
|
for (var jnx = 0; jnx < encodedCharIndexes.length; ++jnx) {
|
|
output += _keyStr.charAt(encodedCharIndexes[jnx]);
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* A middleware for transforming XHR loaded Blobs into more useful objects
|
|
* @ignore
|
|
* @function parsing
|
|
* @example
|
|
* import { Loader, middleware } from 'resource-loader';
|
|
* const loader = new Loader();
|
|
* loader.use(middleware.parsing);
|
|
* @param resource - Current Resource
|
|
* @param next - Callback when complete
|
|
*/
|
|
function parsing(resource, next) {
|
|
if (!resource.data) {
|
|
next();
|
|
return;
|
|
}
|
|
// if this was an XHR load of a blob
|
|
if (resource.xhr && resource.xhrType === exports.LoaderResource.XHR_RESPONSE_TYPE.BLOB) {
|
|
// if there is no blob support we probably got a binary string back
|
|
if (!self.Blob || typeof resource.data === 'string') {
|
|
var type = resource.xhr.getResponseHeader('content-type');
|
|
// this is an image, convert the binary string into a data url
|
|
if (type && type.indexOf('image') === 0) {
|
|
resource.data = new Image();
|
|
resource.data.src = "data:" + type + ";base64," + encodeBinary(resource.xhr.responseText);
|
|
resource.type = exports.LoaderResource.TYPE.IMAGE;
|
|
// wait until the image loads and then callback
|
|
resource.data.onload = function () {
|
|
resource.data.onload = null;
|
|
next();
|
|
};
|
|
// next will be called on load
|
|
return;
|
|
}
|
|
}
|
|
// if content type says this is an image, then we should transform the blob into an Image object
|
|
else if (resource.data.type.indexOf('image') === 0) {
|
|
var Url_1 = globalThis.URL || globalThis.webkitURL;
|
|
var src_1 = Url_1.createObjectURL(resource.data);
|
|
resource.blob = resource.data;
|
|
resource.data = new Image();
|
|
resource.data.src = src_1;
|
|
resource.type = exports.LoaderResource.TYPE.IMAGE;
|
|
// cleanup the no longer used blob after the image loads
|
|
// TODO: Is this correct? Will the image be invalid after revoking?
|
|
resource.data.onload = function () {
|
|
Url_1.revokeObjectURL(src_1);
|
|
resource.data.onload = null;
|
|
next();
|
|
};
|
|
// next will be called on load.
|
|
return;
|
|
}
|
|
}
|
|
next();
|
|
}
|
|
|
|
/**
|
|
* Parse any blob into more usable objects (e.g. Image).
|
|
* @memberof PIXI
|
|
*/
|
|
var ParsingLoader = /** @class */ (function () {
|
|
function ParsingLoader() {
|
|
}
|
|
/** @ignore */
|
|
ParsingLoader.extension = core.ExtensionType.Loader;
|
|
ParsingLoader.use = parsing;
|
|
return ParsingLoader;
|
|
}());
|
|
|
|
core.extensions.add(TextureLoader, ParsingLoader);
|
|
|
|
exports.AppLoaderPlugin = AppLoaderPlugin;
|
|
exports.Loader = Loader;
|
|
exports.TextureLoader = TextureLoader;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
return exports;
|
|
|
|
})({}, PIXI, PIXI.utils);
|
|
Object.assign(this.PIXI, _pixi_loaders);
|
|
//# sourceMappingURL=loaders.js.map
|