loaders.js 74 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944
  1. /*!
  2. * @pixi/loaders - v6.5.10
  3. * Compiled Thu, 06 Jul 2023 15:25:11 UTC
  4. *
  5. * @pixi/loaders is licensed under the MIT License.
  6. * http://www.opensource.org/licenses/mit-license
  7. */
  8. 'use strict';
  9. Object.defineProperty(exports, '__esModule', { value: true });
  10. var core = require('@pixi/core');
  11. var utils = require('@pixi/utils');
  12. /* jshint -W097 */
  13. /**
  14. * @memberof PIXI
  15. */
  16. var SignalBinding = /** @class */ (function () {
  17. /**
  18. * SignalBinding constructor.
  19. * @constructs SignalBinding
  20. * @param {Function} fn - Event handler to be called.
  21. * @param {boolean} [once=false] - Should this listener be removed after dispatch
  22. * @param {object} [thisArg] - The context of the callback function.
  23. * @api private
  24. */
  25. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  26. function SignalBinding(fn, once, thisArg) {
  27. if (once === void 0) { once = false; }
  28. this._fn = fn;
  29. this._once = once;
  30. this._thisArg = thisArg;
  31. this._next = this._prev = this._owner = null;
  32. }
  33. SignalBinding.prototype.detach = function () {
  34. if (this._owner === null)
  35. { return false; }
  36. this._owner.detach(this);
  37. return true;
  38. };
  39. return SignalBinding;
  40. }());
  41. /**
  42. * @param self
  43. * @param node
  44. * @private
  45. */
  46. function _addSignalBinding(self, node) {
  47. if (!self._head) {
  48. self._head = node;
  49. self._tail = node;
  50. }
  51. else {
  52. self._tail._next = node;
  53. node._prev = self._tail;
  54. self._tail = node;
  55. }
  56. node._owner = self;
  57. return node;
  58. }
  59. /**
  60. * @memberof PIXI
  61. */
  62. var Signal = /** @class */ (function () {
  63. /**
  64. * MiniSignal constructor.
  65. * @example
  66. * let mySignal = new Signal();
  67. * let binding = mySignal.add(onSignal);
  68. * mySignal.dispatch('foo', 'bar');
  69. * mySignal.detach(binding);
  70. */
  71. function Signal() {
  72. this._head = this._tail = undefined;
  73. }
  74. /**
  75. * Return an array of attached SignalBinding.
  76. * @param {boolean} [exists=false] - We only need to know if there are handlers.
  77. * @returns {PIXI.SignalBinding[] | boolean} Array of attached SignalBinding or Boolean if called with exists = true
  78. * @api public
  79. */
  80. Signal.prototype.handlers = function (exists) {
  81. if (exists === void 0) { exists = false; }
  82. var node = this._head;
  83. if (exists)
  84. { return !!node; }
  85. var ee = [];
  86. while (node) {
  87. ee.push(node);
  88. node = node._next;
  89. }
  90. return ee;
  91. };
  92. /**
  93. * Return true if node is a SignalBinding attached to this MiniSignal
  94. * @param {PIXI.SignalBinding} node - Node to check.
  95. * @returns {boolean} True if node is attache to mini-signal
  96. */
  97. Signal.prototype.has = function (node) {
  98. if (!(node instanceof SignalBinding)) {
  99. throw new Error('MiniSignal#has(): First arg must be a SignalBinding object.');
  100. }
  101. return node._owner === this;
  102. };
  103. /**
  104. * Dispaches a signal to all registered listeners.
  105. * @param {...any} args
  106. * @returns {boolean} Indication if we've emitted an event.
  107. */
  108. Signal.prototype.dispatch = function () {
  109. var arguments$1 = arguments;
  110. var args = [];
  111. for (var _i = 0; _i < arguments.length; _i++) {
  112. args[_i] = arguments$1[_i];
  113. }
  114. var node = this._head;
  115. if (!node)
  116. { return false; }
  117. while (node) {
  118. if (node._once)
  119. { this.detach(node); }
  120. node._fn.apply(node._thisArg, args);
  121. node = node._next;
  122. }
  123. return true;
  124. };
  125. /**
  126. * Register a new listener.
  127. * @param {Function} fn - Callback function.
  128. * @param {object} [thisArg] - The context of the callback function.
  129. * @returns {PIXI.SignalBinding} The SignalBinding node that was added.
  130. */
  131. Signal.prototype.add = function (fn, thisArg) {
  132. if (thisArg === void 0) { thisArg = null; }
  133. if (typeof fn !== 'function') {
  134. throw new Error('MiniSignal#add(): First arg must be a Function.');
  135. }
  136. return _addSignalBinding(this, new SignalBinding(fn, false, thisArg));
  137. };
  138. /**
  139. * Register a new listener that will be executed only once.
  140. * @param {Function} fn - Callback function.
  141. * @param {object} [thisArg] - The context of the callback function.
  142. * @returns {PIXI.SignalBinding} The SignalBinding node that was added.
  143. */
  144. Signal.prototype.once = function (fn, thisArg) {
  145. if (thisArg === void 0) { thisArg = null; }
  146. if (typeof fn !== 'function') {
  147. throw new Error('MiniSignal#once(): First arg must be a Function.');
  148. }
  149. return _addSignalBinding(this, new SignalBinding(fn, true, thisArg));
  150. };
  151. /**
  152. * Remove binding object.
  153. * @param {PIXI.SignalBinding} node - The binding node that will be removed.
  154. * @returns {Signal} The instance on which this method was called.
  155. @api public */
  156. Signal.prototype.detach = function (node) {
  157. if (!(node instanceof SignalBinding)) {
  158. throw new Error('MiniSignal#detach(): First arg must be a SignalBinding object.');
  159. }
  160. if (node._owner !== this)
  161. { return this; } // todo: or error?
  162. if (node._prev)
  163. { node._prev._next = node._next; }
  164. if (node._next)
  165. { node._next._prev = node._prev; }
  166. if (node === this._head) { // first node
  167. this._head = node._next;
  168. if (node._next === null) {
  169. this._tail = null;
  170. }
  171. }
  172. else if (node === this._tail) { // last node
  173. this._tail = node._prev;
  174. this._tail._next = null;
  175. }
  176. node._owner = null;
  177. return this;
  178. };
  179. /**
  180. * Detach all listeners.
  181. * @returns {Signal} The instance on which this method was called.
  182. */
  183. Signal.prototype.detachAll = function () {
  184. var node = this._head;
  185. if (!node)
  186. { return this; }
  187. this._head = this._tail = null;
  188. while (node) {
  189. node._owner = null;
  190. node = node._next;
  191. }
  192. return this;
  193. };
  194. return Signal;
  195. }());
  196. /**
  197. * function from npm package `parseUri`, converted to TS to avoid leftpad incident
  198. * @param {string} str
  199. * @param [opts] - options
  200. * @param {boolean} [opts.strictMode] - type of parser
  201. */
  202. function parseUri(str, opts) {
  203. opts = opts || {};
  204. var o = {
  205. // eslint-disable-next-line max-len
  206. key: ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'],
  207. q: {
  208. name: 'queryKey',
  209. parser: /(?:^|&)([^&=]*)=?([^&]*)/g
  210. },
  211. parser: {
  212. // eslint-disable-next-line max-len
  213. strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
  214. // eslint-disable-next-line max-len
  215. loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
  216. }
  217. };
  218. var m = o.parser[opts.strictMode ? 'strict' : 'loose'].exec(str);
  219. var uri = {};
  220. var i = 14;
  221. while (i--)
  222. { uri[o.key[i]] = m[i] || ''; }
  223. uri[o.q.name] = {};
  224. uri[o.key[12]].replace(o.q.parser, function (_t0, t1, t2) {
  225. if (t1)
  226. { uri[o.q.name][t1] = t2; }
  227. });
  228. return uri;
  229. }
  230. // tests if CORS is supported in XHR, if not we need to use XDR
  231. var useXdr;
  232. var tempAnchor = null;
  233. // some status constants
  234. var STATUS_NONE = 0;
  235. var STATUS_OK = 200;
  236. var STATUS_EMPTY = 204;
  237. var STATUS_IE_BUG_EMPTY = 1223;
  238. var STATUS_TYPE_OK = 2;
  239. // noop
  240. function _noop$1() { }
  241. /**
  242. * Quick helper to set a value on one of the extension maps. Ensures there is no
  243. * dot at the start of the extension.
  244. * @ignore
  245. * @param map - The map to set on.
  246. * @param extname - The extension (or key) to set.
  247. * @param val - The value to set.
  248. */
  249. function setExtMap(map, extname, val) {
  250. if (extname && extname.indexOf('.') === 0) {
  251. extname = extname.substring(1);
  252. }
  253. if (!extname) {
  254. return;
  255. }
  256. map[extname] = val;
  257. }
  258. /**
  259. * Quick helper to get string xhr type.
  260. * @ignore
  261. * @param xhr - The request to check.
  262. * @returns The type.
  263. */
  264. function reqType(xhr) {
  265. return xhr.toString().replace('object ', '');
  266. }
  267. /**
  268. * Manages the state and loading of a resource and all child resources.
  269. *
  270. * Can be extended in `GlobalMixins.LoaderResource`.
  271. * @memberof PIXI
  272. */
  273. exports.LoaderResource = /** @class */ (function () {
  274. /**
  275. * @param {string} name - The name of the resource to load.
  276. * @param {string|string[]} url - The url for this resource, for audio/video loads you can pass
  277. * an array of sources.
  278. * @param {object} [options] - The options for the load.
  279. * @param {string|boolean} [options.crossOrigin] - Is this request cross-origin? Default is to
  280. * determine automatically.
  281. * @param {number} [options.timeout=0] - A timeout in milliseconds for the load. If the load takes
  282. * longer than this time it is cancelled and the load is considered a failure. If this value is
  283. * set to `0` then there is no explicit timeout.
  284. * @param {PIXI.LoaderResource.LOAD_TYPE} [options.loadType=LOAD_TYPE.XHR] - How should this resource
  285. * be loaded?
  286. * @param {PIXI.LoaderResource.XHR_RESPONSE_TYPE} [options.xhrType=XHR_RESPONSE_TYPE.DEFAULT] - How
  287. * should the data being loaded be interpreted when using XHR?
  288. * @param {PIXI.LoaderResource.IMetadata} [options.metadata] - Extra configuration for middleware
  289. * and the Resource object.
  290. */
  291. function LoaderResource(name, url, options) {
  292. /**
  293. * The `dequeue` method that will be used a storage place for the async queue dequeue method
  294. * used privately by the loader.
  295. * @private
  296. * @member {Function}
  297. */
  298. this._dequeue = _noop$1;
  299. /**
  300. * Used a storage place for the on load binding used privately by the loader.
  301. * @private
  302. * @member {Function}
  303. */
  304. this._onLoadBinding = null;
  305. /**
  306. * The timer for element loads to check if they timeout.
  307. * @private
  308. */
  309. this._elementTimer = 0;
  310. /**
  311. * The `complete` function bound to this resource's context.
  312. * @private
  313. * @type {Function}
  314. */
  315. this._boundComplete = null;
  316. /**
  317. * The `_onError` function bound to this resource's context.
  318. * @private
  319. * @type {Function}
  320. */
  321. this._boundOnError = null;
  322. /**
  323. * The `_onProgress` function bound to this resource's context.
  324. * @private
  325. * @type {Function}
  326. */
  327. this._boundOnProgress = null;
  328. /**
  329. * The `_onTimeout` function bound to this resource's context.
  330. * @private
  331. * @type {Function}
  332. */
  333. this._boundOnTimeout = null;
  334. this._boundXhrOnError = null;
  335. this._boundXhrOnTimeout = null;
  336. this._boundXhrOnAbort = null;
  337. this._boundXhrOnLoad = null;
  338. if (typeof name !== 'string' || typeof url !== 'string') {
  339. throw new Error('Both name and url are required for constructing a resource.');
  340. }
  341. options = options || {};
  342. this._flags = 0;
  343. // set data url flag, needs to be set early for some _determineX checks to work.
  344. this._setFlag(LoaderResource.STATUS_FLAGS.DATA_URL, url.indexOf('data:') === 0);
  345. this.name = name;
  346. this.url = url;
  347. this.extension = this._getExtension();
  348. this.data = null;
  349. this.crossOrigin = options.crossOrigin === true ? 'anonymous' : options.crossOrigin;
  350. this.timeout = options.timeout || 0;
  351. this.loadType = options.loadType || this._determineLoadType();
  352. // The type used to load the resource via XHR. If unset, determined automatically.
  353. this.xhrType = options.xhrType;
  354. // Extra info for middleware, and controlling specifics about how the resource loads.
  355. // Note that if you pass in a `loadElement`, the Resource class takes ownership of it.
  356. // Meaning it will modify it as it sees fit.
  357. this.metadata = options.metadata || {};
  358. // The error that occurred while loading (if any).
  359. this.error = null;
  360. // The XHR object that was used to load this resource. This is only set
  361. // when `loadType` is `LoaderResource.LOAD_TYPE.XHR`.
  362. this.xhr = null;
  363. // The child resources this resource owns.
  364. this.children = [];
  365. // The resource type.
  366. this.type = LoaderResource.TYPE.UNKNOWN;
  367. // The progress chunk owned by this resource.
  368. this.progressChunk = 0;
  369. // The `dequeue` method that will be used a storage place for the async queue dequeue method
  370. // used privately by the loader.
  371. this._dequeue = _noop$1;
  372. // Used a storage place for the on load binding used privately by the loader.
  373. this._onLoadBinding = null;
  374. // The timer for element loads to check if they timeout.
  375. this._elementTimer = 0;
  376. this._boundComplete = this.complete.bind(this);
  377. this._boundOnError = this._onError.bind(this);
  378. this._boundOnProgress = this._onProgress.bind(this);
  379. this._boundOnTimeout = this._onTimeout.bind(this);
  380. // xhr callbacks
  381. this._boundXhrOnError = this._xhrOnError.bind(this);
  382. this._boundXhrOnTimeout = this._xhrOnTimeout.bind(this);
  383. this._boundXhrOnAbort = this._xhrOnAbort.bind(this);
  384. this._boundXhrOnLoad = this._xhrOnLoad.bind(this);
  385. // Dispatched when the resource beings to load.
  386. this.onStart = new Signal();
  387. // Dispatched each time progress of this resource load updates.
  388. // Not all resources types and loader systems can support this event
  389. // so sometimes it may not be available. If the resource
  390. // is being loaded on a modern browser, using XHR, and the remote server
  391. // properly sets Content-Length headers, then this will be available.
  392. this.onProgress = new Signal();
  393. // Dispatched once this resource has loaded, if there was an error it will
  394. // be in the `error` property.
  395. this.onComplete = new Signal();
  396. // Dispatched after this resource has had all the *after* middleware run on it.
  397. this.onAfterMiddleware = new Signal();
  398. }
  399. /**
  400. * Sets the load type to be used for a specific extension.
  401. * @static
  402. * @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
  403. * @param {PIXI.LoaderResource.LOAD_TYPE} loadType - The load type to set it to.
  404. */
  405. LoaderResource.setExtensionLoadType = function (extname, loadType) {
  406. setExtMap(LoaderResource._loadTypeMap, extname, loadType);
  407. };
  408. /**
  409. * Sets the load type to be used for a specific extension.
  410. * @static
  411. * @param {string} extname - The extension to set the type for, e.g. "png" or "fnt"
  412. * @param {PIXI.LoaderResource.XHR_RESPONSE_TYPE} xhrType - The xhr type to set it to.
  413. */
  414. LoaderResource.setExtensionXhrType = function (extname, xhrType) {
  415. setExtMap(LoaderResource._xhrTypeMap, extname, xhrType);
  416. };
  417. Object.defineProperty(LoaderResource.prototype, "isDataUrl", {
  418. /**
  419. * When the resource starts to load.
  420. * @memberof PIXI.LoaderResource
  421. * @callback OnStartSignal
  422. * @param {PIXI.Resource} resource - The resource that the event happened on.
  423. */
  424. /**
  425. * When the resource reports loading progress.
  426. * @memberof PIXI.LoaderResource
  427. * @callback OnProgressSignal
  428. * @param {PIXI.Resource} resource - The resource that the event happened on.
  429. * @param {number} percentage - The progress of the load in the range [0, 1].
  430. */
  431. /**
  432. * When the resource finishes loading.
  433. * @memberof PIXI.LoaderResource
  434. * @callback OnCompleteSignal
  435. * @param {PIXI.Resource} resource - The resource that the event happened on.
  436. */
  437. /**
  438. * @memberof PIXI.LoaderResource
  439. * @typedef {object} IMetadata
  440. * @property {HTMLImageElement|HTMLAudioElement|HTMLVideoElement} [loadElement=null] - The
  441. * element to use for loading, instead of creating one.
  442. * @property {boolean} [skipSource=false] - Skips adding source(s) to the load element. This
  443. * is useful if you want to pass in a `loadElement` that you already added load sources to.
  444. * @property {string|string[]} [mimeType] - The mime type to use for the source element
  445. * of a video/audio elment. If the urls are an array, you can pass this as an array as well
  446. * where each index is the mime type to use for the corresponding url index.
  447. */
  448. /**
  449. * Stores whether or not this url is a data url.
  450. * @readonly
  451. * @member {boolean}
  452. */
  453. get: function () {
  454. return this._hasFlag(LoaderResource.STATUS_FLAGS.DATA_URL);
  455. },
  456. enumerable: false,
  457. configurable: true
  458. });
  459. Object.defineProperty(LoaderResource.prototype, "isComplete", {
  460. /**
  461. * Describes if this resource has finished loading. Is true when the resource has completely
  462. * loaded.
  463. * @readonly
  464. * @member {boolean}
  465. */
  466. get: function () {
  467. return this._hasFlag(LoaderResource.STATUS_FLAGS.COMPLETE);
  468. },
  469. enumerable: false,
  470. configurable: true
  471. });
  472. Object.defineProperty(LoaderResource.prototype, "isLoading", {
  473. /**
  474. * Describes if this resource is currently loading. Is true when the resource starts loading,
  475. * and is false again when complete.
  476. * @readonly
  477. * @member {boolean}
  478. */
  479. get: function () {
  480. return this._hasFlag(LoaderResource.STATUS_FLAGS.LOADING);
  481. },
  482. enumerable: false,
  483. configurable: true
  484. });
  485. /** Marks the resource as complete. */
  486. LoaderResource.prototype.complete = function () {
  487. this._clearEvents();
  488. this._finish();
  489. };
  490. /**
  491. * Aborts the loading of this resource, with an optional message.
  492. * @param {string} message - The message to use for the error
  493. */
  494. LoaderResource.prototype.abort = function (message) {
  495. // abort can be called multiple times, ignore subsequent calls.
  496. if (this.error) {
  497. return;
  498. }
  499. // store error
  500. this.error = new Error(message);
  501. // clear events before calling aborts
  502. this._clearEvents();
  503. // abort the actual loading
  504. if (this.xhr) {
  505. this.xhr.abort();
  506. }
  507. else if (this.xdr) {
  508. this.xdr.abort();
  509. }
  510. else if (this.data) {
  511. // single source
  512. if (this.data.src) {
  513. this.data.src = LoaderResource.EMPTY_GIF;
  514. }
  515. // multi-source
  516. else {
  517. while (this.data.firstChild) {
  518. this.data.removeChild(this.data.firstChild);
  519. }
  520. }
  521. }
  522. // done now.
  523. this._finish();
  524. };
  525. /**
  526. * Kicks off loading of this resource. This method is asynchronous.
  527. * @param {PIXI.LoaderResource.OnCompleteSignal} [cb] - Optional callback to call once the resource is loaded.
  528. */
  529. LoaderResource.prototype.load = function (cb) {
  530. var _this = this;
  531. if (this.isLoading) {
  532. return;
  533. }
  534. if (this.isComplete) {
  535. if (cb) {
  536. setTimeout(function () { return cb(_this); }, 1);
  537. }
  538. return;
  539. }
  540. else if (cb) {
  541. this.onComplete.once(cb);
  542. }
  543. this._setFlag(LoaderResource.STATUS_FLAGS.LOADING, true);
  544. this.onStart.dispatch(this);
  545. // if unset, determine the value
  546. if (this.crossOrigin === false || typeof this.crossOrigin !== 'string') {
  547. this.crossOrigin = this._determineCrossOrigin(this.url);
  548. }
  549. switch (this.loadType) {
  550. case LoaderResource.LOAD_TYPE.IMAGE:
  551. this.type = LoaderResource.TYPE.IMAGE;
  552. this._loadElement('image');
  553. break;
  554. case LoaderResource.LOAD_TYPE.AUDIO:
  555. this.type = LoaderResource.TYPE.AUDIO;
  556. this._loadSourceElement('audio');
  557. break;
  558. case LoaderResource.LOAD_TYPE.VIDEO:
  559. this.type = LoaderResource.TYPE.VIDEO;
  560. this._loadSourceElement('video');
  561. break;
  562. case LoaderResource.LOAD_TYPE.XHR:
  563. /* falls through */
  564. default:
  565. if (typeof useXdr === 'undefined') {
  566. useXdr = !!(globalThis.XDomainRequest && !('withCredentials' in (new XMLHttpRequest())));
  567. }
  568. if (useXdr && this.crossOrigin) {
  569. this._loadXdr();
  570. }
  571. else {
  572. this._loadXhr();
  573. }
  574. break;
  575. }
  576. };
  577. /**
  578. * Checks if the flag is set.
  579. * @param flag - The flag to check.
  580. * @returns True if the flag is set.
  581. */
  582. LoaderResource.prototype._hasFlag = function (flag) {
  583. return (this._flags & flag) !== 0;
  584. };
  585. /**
  586. * (Un)Sets the flag.
  587. * @param flag - The flag to (un)set.
  588. * @param value - Whether to set or (un)set the flag.
  589. */
  590. LoaderResource.prototype._setFlag = function (flag, value) {
  591. this._flags = value ? (this._flags | flag) : (this._flags & ~flag);
  592. };
  593. /** Clears all the events from the underlying loading source. */
  594. LoaderResource.prototype._clearEvents = function () {
  595. clearTimeout(this._elementTimer);
  596. if (this.data && this.data.removeEventListener) {
  597. this.data.removeEventListener('error', this._boundOnError, false);
  598. this.data.removeEventListener('load', this._boundComplete, false);
  599. this.data.removeEventListener('progress', this._boundOnProgress, false);
  600. this.data.removeEventListener('canplaythrough', this._boundComplete, false);
  601. }
  602. if (this.xhr) {
  603. if (this.xhr.removeEventListener) {
  604. this.xhr.removeEventListener('error', this._boundXhrOnError, false);
  605. this.xhr.removeEventListener('timeout', this._boundXhrOnTimeout, false);
  606. this.xhr.removeEventListener('abort', this._boundXhrOnAbort, false);
  607. this.xhr.removeEventListener('progress', this._boundOnProgress, false);
  608. this.xhr.removeEventListener('load', this._boundXhrOnLoad, false);
  609. }
  610. else {
  611. this.xhr.onerror = null;
  612. this.xhr.ontimeout = null;
  613. this.xhr.onprogress = null;
  614. this.xhr.onload = null;
  615. }
  616. }
  617. };
  618. /** Finalizes the load. */
  619. LoaderResource.prototype._finish = function () {
  620. if (this.isComplete) {
  621. throw new Error('Complete called again for an already completed resource.');
  622. }
  623. this._setFlag(LoaderResource.STATUS_FLAGS.COMPLETE, true);
  624. this._setFlag(LoaderResource.STATUS_FLAGS.LOADING, false);
  625. this.onComplete.dispatch(this);
  626. };
  627. /**
  628. * Loads this resources using an element that has a single source,
  629. * like an HTMLImageElement.
  630. * @private
  631. * @param type - The type of element to use.
  632. */
  633. LoaderResource.prototype._loadElement = function (type) {
  634. if (this.metadata.loadElement) {
  635. this.data = this.metadata.loadElement;
  636. }
  637. else if (type === 'image' && typeof globalThis.Image !== 'undefined') {
  638. this.data = new Image();
  639. }
  640. else {
  641. this.data = document.createElement(type);
  642. }
  643. if (this.crossOrigin) {
  644. this.data.crossOrigin = this.crossOrigin;
  645. }
  646. if (!this.metadata.skipSource) {
  647. this.data.src = this.url;
  648. }
  649. this.data.addEventListener('error', this._boundOnError, false);
  650. this.data.addEventListener('load', this._boundComplete, false);
  651. this.data.addEventListener('progress', this._boundOnProgress, false);
  652. if (this.timeout) {
  653. this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout);
  654. }
  655. };
  656. /**
  657. * Loads this resources using an element that has multiple sources,
  658. * like an HTMLAudioElement or HTMLVideoElement.
  659. * @param type - The type of element to use.
  660. */
  661. LoaderResource.prototype._loadSourceElement = function (type) {
  662. if (this.metadata.loadElement) {
  663. this.data = this.metadata.loadElement;
  664. }
  665. else if (type === 'audio' && typeof globalThis.Audio !== 'undefined') {
  666. this.data = new Audio();
  667. }
  668. else {
  669. this.data = document.createElement(type);
  670. }
  671. if (this.data === null) {
  672. this.abort("Unsupported element: " + type);
  673. return;
  674. }
  675. if (this.crossOrigin) {
  676. this.data.crossOrigin = this.crossOrigin;
  677. }
  678. if (!this.metadata.skipSource) {
  679. // support for CocoonJS Canvas+ runtime, lacks document.createElement('source')
  680. if (navigator.isCocoonJS) {
  681. this.data.src = Array.isArray(this.url) ? this.url[0] : this.url;
  682. }
  683. else if (Array.isArray(this.url)) {
  684. var mimeTypes = this.metadata.mimeType;
  685. for (var i = 0; i < this.url.length; ++i) {
  686. this.data.appendChild(this._createSource(type, this.url[i], Array.isArray(mimeTypes) ? mimeTypes[i] : mimeTypes));
  687. }
  688. }
  689. else {
  690. var mimeTypes = this.metadata.mimeType;
  691. this.data.appendChild(this._createSource(type, this.url, Array.isArray(mimeTypes) ? mimeTypes[0] : mimeTypes));
  692. }
  693. }
  694. this.data.addEventListener('error', this._boundOnError, false);
  695. this.data.addEventListener('load', this._boundComplete, false);
  696. this.data.addEventListener('progress', this._boundOnProgress, false);
  697. this.data.addEventListener('canplaythrough', this._boundComplete, false);
  698. this.data.load();
  699. if (this.timeout) {
  700. this._elementTimer = setTimeout(this._boundOnTimeout, this.timeout);
  701. }
  702. };
  703. /** Loads this resources using an XMLHttpRequest. */
  704. LoaderResource.prototype._loadXhr = function () {
  705. // if unset, determine the value
  706. if (typeof this.xhrType !== 'string') {
  707. this.xhrType = this._determineXhrType();
  708. }
  709. var xhr = this.xhr = new XMLHttpRequest();
  710. // send credentials when crossOrigin with credentials requested
  711. if (this.crossOrigin === 'use-credentials') {
  712. xhr.withCredentials = true;
  713. }
  714. // set the request type and url
  715. xhr.open('GET', this.url, true);
  716. xhr.timeout = this.timeout;
  717. // load json as text and parse it ourselves. We do this because some browsers
  718. // *cough* safari *cough* can't deal with it.
  719. if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.JSON
  720. || this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT) {
  721. xhr.responseType = LoaderResource.XHR_RESPONSE_TYPE.TEXT;
  722. }
  723. else {
  724. xhr.responseType = this.xhrType;
  725. }
  726. xhr.addEventListener('error', this._boundXhrOnError, false);
  727. xhr.addEventListener('timeout', this._boundXhrOnTimeout, false);
  728. xhr.addEventListener('abort', this._boundXhrOnAbort, false);
  729. xhr.addEventListener('progress', this._boundOnProgress, false);
  730. xhr.addEventListener('load', this._boundXhrOnLoad, false);
  731. xhr.send();
  732. };
  733. /** Loads this resources using an XDomainRequest. This is here because we need to support IE9 (gross). */
  734. LoaderResource.prototype._loadXdr = function () {
  735. // if unset, determine the value
  736. if (typeof this.xhrType !== 'string') {
  737. this.xhrType = this._determineXhrType();
  738. }
  739. var xdr = this.xhr = new globalThis.XDomainRequest(); // eslint-disable-line no-undef
  740. // XDomainRequest has a few quirks. Occasionally it will abort requests
  741. // A way to avoid this is to make sure ALL callbacks are set even if not used
  742. // More info here: http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9
  743. xdr.timeout = this.timeout || 5000; // XDR needs a timeout value or it breaks in IE9
  744. xdr.onerror = this._boundXhrOnError;
  745. xdr.ontimeout = this._boundXhrOnTimeout;
  746. xdr.onprogress = this._boundOnProgress;
  747. xdr.onload = this._boundXhrOnLoad;
  748. xdr.open('GET', this.url, true);
  749. // Note: The xdr.send() call is wrapped in a timeout to prevent an
  750. // issue with the interface where some requests are lost if multiple
  751. // XDomainRequests are being sent at the same time.
  752. // Some info here: https://github.com/photonstorm/phaser/issues/1248
  753. setTimeout(function () { return xdr.send(); }, 1);
  754. };
  755. /**
  756. * Creates a source used in loading via an element.
  757. * @param type - The element type (video or audio).
  758. * @param url - The source URL to load from.
  759. * @param [mime] - The mime type of the video
  760. * @returns The source element.
  761. */
  762. LoaderResource.prototype._createSource = function (type, url, mime) {
  763. if (!mime) {
  764. mime = type + "/" + this._getExtension(url);
  765. }
  766. var source = document.createElement('source');
  767. source.src = url;
  768. source.type = mime;
  769. return source;
  770. };
  771. /**
  772. * Called if a load errors out.
  773. * @param event - The error event from the element that emits it.
  774. */
  775. LoaderResource.prototype._onError = function (event) {
  776. this.abort("Failed to load element using: " + event.target.nodeName);
  777. };
  778. /**
  779. * Called if a load progress event fires for an element or xhr/xdr.
  780. * @param event - Progress event.
  781. */
  782. LoaderResource.prototype._onProgress = function (event) {
  783. if (event && event.lengthComputable) {
  784. this.onProgress.dispatch(this, event.loaded / event.total);
  785. }
  786. };
  787. /** Called if a timeout event fires for an element. */
  788. LoaderResource.prototype._onTimeout = function () {
  789. this.abort("Load timed out.");
  790. };
  791. /** Called if an error event fires for xhr/xdr. */
  792. LoaderResource.prototype._xhrOnError = function () {
  793. var xhr = this.xhr;
  794. this.abort(reqType(xhr) + " Request failed. Status: " + xhr.status + ", text: \"" + xhr.statusText + "\"");
  795. };
  796. /** Called if an error event fires for xhr/xdr. */
  797. LoaderResource.prototype._xhrOnTimeout = function () {
  798. var xhr = this.xhr;
  799. this.abort(reqType(xhr) + " Request timed out.");
  800. };
  801. /** Called if an abort event fires for xhr/xdr. */
  802. LoaderResource.prototype._xhrOnAbort = function () {
  803. var xhr = this.xhr;
  804. this.abort(reqType(xhr) + " Request was aborted by the user.");
  805. };
  806. /** Called when data successfully loads from an xhr/xdr request. */
  807. LoaderResource.prototype._xhrOnLoad = function () {
  808. var xhr = this.xhr;
  809. var text = '';
  810. var status = typeof xhr.status === 'undefined' ? STATUS_OK : xhr.status; // XDR has no `.status`, assume 200.
  811. // responseText is accessible only if responseType is '' or 'text' and on older browsers
  812. if (xhr.responseType === '' || xhr.responseType === 'text' || typeof xhr.responseType === 'undefined') {
  813. text = xhr.responseText;
  814. }
  815. // status can be 0 when using the `file://` protocol so we also check if a response is set.
  816. // If it has a response, we assume 200; otherwise a 0 status code with no contents is an aborted request.
  817. if (status === STATUS_NONE && (text.length > 0 || xhr.responseType === LoaderResource.XHR_RESPONSE_TYPE.BUFFER)) {
  818. status = STATUS_OK;
  819. }
  820. // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
  821. else if (status === STATUS_IE_BUG_EMPTY) {
  822. status = STATUS_EMPTY;
  823. }
  824. var statusType = (status / 100) | 0;
  825. if (statusType === STATUS_TYPE_OK) {
  826. // if text, just return it
  827. if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.TEXT) {
  828. this.data = text;
  829. this.type = LoaderResource.TYPE.TEXT;
  830. }
  831. // if json, parse into json object
  832. else if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.JSON) {
  833. try {
  834. this.data = JSON.parse(text);
  835. this.type = LoaderResource.TYPE.JSON;
  836. }
  837. catch (e) {
  838. this.abort("Error trying to parse loaded json: " + e);
  839. return;
  840. }
  841. }
  842. // if xml, parse into an xml document or div element
  843. else if (this.xhrType === LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT) {
  844. try {
  845. if (globalThis.DOMParser) {
  846. var domparser = new DOMParser();
  847. this.data = domparser.parseFromString(text, 'text/xml');
  848. }
  849. else {
  850. var div = document.createElement('div');
  851. div.innerHTML = text;
  852. this.data = div;
  853. }
  854. this.type = LoaderResource.TYPE.XML;
  855. }
  856. catch (e$1) {
  857. this.abort("Error trying to parse loaded xml: " + e$1);
  858. return;
  859. }
  860. }
  861. // other types just return the response
  862. else {
  863. this.data = xhr.response || text;
  864. }
  865. }
  866. else {
  867. this.abort("[" + xhr.status + "] " + xhr.statusText + ": " + xhr.responseURL);
  868. return;
  869. }
  870. this.complete();
  871. };
  872. /**
  873. * Sets the `crossOrigin` property for this resource based on if the url
  874. * for this resource is cross-origin. If crossOrigin was manually set, this
  875. * function does nothing.
  876. * @private
  877. * @param url - The url to test.
  878. * @param [loc=globalThis.location] - The location object to test against.
  879. * @returns The crossOrigin value to use (or empty string for none).
  880. */
  881. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  882. LoaderResource.prototype._determineCrossOrigin = function (url, loc) {
  883. // data: and javascript: urls are considered same-origin
  884. if (url.indexOf('data:') === 0) {
  885. return '';
  886. }
  887. // A sandboxed iframe without the 'allow-same-origin' attribute will have a special
  888. // origin designed not to match globalThis.location.origin, and will always require
  889. // crossOrigin requests regardless of whether the location matches.
  890. if (globalThis.origin !== globalThis.location.origin) {
  891. return 'anonymous';
  892. }
  893. // default is globalThis.location
  894. loc = loc || globalThis.location;
  895. if (!tempAnchor) {
  896. tempAnchor = document.createElement('a');
  897. }
  898. // let the browser determine the full href for the url of this resource and then
  899. // parse with the node url lib, we can't use the properties of the anchor element
  900. // because they don't work in IE9 :(
  901. tempAnchor.href = url;
  902. var parsedUrl = parseUri(tempAnchor.href, { strictMode: true });
  903. var samePort = (!parsedUrl.port && loc.port === '') || (parsedUrl.port === loc.port);
  904. var protocol = parsedUrl.protocol ? parsedUrl.protocol + ":" : '';
  905. // if cross origin
  906. if (parsedUrl.host !== loc.hostname || !samePort || protocol !== loc.protocol) {
  907. return 'anonymous';
  908. }
  909. return '';
  910. };
  911. /**
  912. * Determines the responseType of an XHR request based on the extension of the
  913. * resource being loaded.
  914. * @private
  915. * @returns {PIXI.LoaderResource.XHR_RESPONSE_TYPE} The responseType to use.
  916. */
  917. LoaderResource.prototype._determineXhrType = function () {
  918. return LoaderResource._xhrTypeMap[this.extension] || LoaderResource.XHR_RESPONSE_TYPE.TEXT;
  919. };
  920. /**
  921. * Determines the loadType of a resource based on the extension of the
  922. * resource being loaded.
  923. * @private
  924. * @returns {PIXI.LoaderResource.LOAD_TYPE} The loadType to use.
  925. */
  926. LoaderResource.prototype._determineLoadType = function () {
  927. return LoaderResource._loadTypeMap[this.extension] || LoaderResource.LOAD_TYPE.XHR;
  928. };
  929. /**
  930. * Extracts the extension (sans '.') of the file being loaded by the resource.
  931. * @param [url] - url to parse, `this.url` by default.
  932. * @returns The extension.
  933. */
  934. LoaderResource.prototype._getExtension = function (url) {
  935. if (url === void 0) { url = this.url; }
  936. var ext = '';
  937. if (this.isDataUrl) {
  938. var slashIndex = url.indexOf('/');
  939. ext = url.substring(slashIndex + 1, url.indexOf(';', slashIndex));
  940. }
  941. else {
  942. var queryStart = url.indexOf('?');
  943. var hashStart = url.indexOf('#');
  944. var index = Math.min(queryStart > -1 ? queryStart : url.length, hashStart > -1 ? hashStart : url.length);
  945. url = url.substring(0, index);
  946. ext = url.substring(url.lastIndexOf('.') + 1);
  947. }
  948. return ext.toLowerCase();
  949. };
  950. /**
  951. * Determines the mime type of an XHR request based on the responseType of
  952. * resource being loaded.
  953. * @param type - The type to get a mime type for.
  954. * @private
  955. * @returns The mime type to use.
  956. */
  957. LoaderResource.prototype._getMimeFromXhrType = function (type) {
  958. switch (type) {
  959. case LoaderResource.XHR_RESPONSE_TYPE.BUFFER:
  960. return 'application/octet-binary';
  961. case LoaderResource.XHR_RESPONSE_TYPE.BLOB:
  962. return 'application/blob';
  963. case LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT:
  964. return 'application/xml';
  965. case LoaderResource.XHR_RESPONSE_TYPE.JSON:
  966. return 'application/json';
  967. case LoaderResource.XHR_RESPONSE_TYPE.DEFAULT:
  968. case LoaderResource.XHR_RESPONSE_TYPE.TEXT:
  969. /* falls through */
  970. default:
  971. return 'text/plain';
  972. }
  973. };
  974. return LoaderResource;
  975. }());
  976. // eslint-disable-next-line @typescript-eslint/no-namespace
  977. (function (LoaderResource) {
  978. (function (STATUS_FLAGS) {
  979. /** None */
  980. STATUS_FLAGS[STATUS_FLAGS["NONE"] = 0] = "NONE";
  981. /** Data URL */
  982. STATUS_FLAGS[STATUS_FLAGS["DATA_URL"] = 1] = "DATA_URL";
  983. /** Complete */
  984. STATUS_FLAGS[STATUS_FLAGS["COMPLETE"] = 2] = "COMPLETE";
  985. /** Loading */
  986. STATUS_FLAGS[STATUS_FLAGS["LOADING"] = 4] = "LOADING";
  987. })(LoaderResource.STATUS_FLAGS || (LoaderResource.STATUS_FLAGS = {}));
  988. (function (TYPE) {
  989. /** Unknown */
  990. TYPE[TYPE["UNKNOWN"] = 0] = "UNKNOWN";
  991. /** JSON */
  992. TYPE[TYPE["JSON"] = 1] = "JSON";
  993. /** XML */
  994. TYPE[TYPE["XML"] = 2] = "XML";
  995. /** Image */
  996. TYPE[TYPE["IMAGE"] = 3] = "IMAGE";
  997. /** Audio */
  998. TYPE[TYPE["AUDIO"] = 4] = "AUDIO";
  999. /** Video */
  1000. TYPE[TYPE["VIDEO"] = 5] = "VIDEO";
  1001. /** Plain text */
  1002. TYPE[TYPE["TEXT"] = 6] = "TEXT";
  1003. })(LoaderResource.TYPE || (LoaderResource.TYPE = {}));
  1004. (function (LOAD_TYPE) {
  1005. /** Uses XMLHttpRequest to load the resource. */
  1006. LOAD_TYPE[LOAD_TYPE["XHR"] = 1] = "XHR";
  1007. /** Uses an `Image` object to load the resource. */
  1008. LOAD_TYPE[LOAD_TYPE["IMAGE"] = 2] = "IMAGE";
  1009. /** Uses an `Audio` object to load the resource. */
  1010. LOAD_TYPE[LOAD_TYPE["AUDIO"] = 3] = "AUDIO";
  1011. /** Uses a `Video` object to load the resource. */
  1012. LOAD_TYPE[LOAD_TYPE["VIDEO"] = 4] = "VIDEO";
  1013. })(LoaderResource.LOAD_TYPE || (LoaderResource.LOAD_TYPE = {}));
  1014. (function (XHR_RESPONSE_TYPE) {
  1015. /** string */
  1016. XHR_RESPONSE_TYPE["DEFAULT"] = "text";
  1017. /** ArrayBuffer */
  1018. XHR_RESPONSE_TYPE["BUFFER"] = "arraybuffer";
  1019. /** Blob */
  1020. XHR_RESPONSE_TYPE["BLOB"] = "blob";
  1021. /** Document */
  1022. XHR_RESPONSE_TYPE["DOCUMENT"] = "document";
  1023. /** Object */
  1024. XHR_RESPONSE_TYPE["JSON"] = "json";
  1025. /** String */
  1026. XHR_RESPONSE_TYPE["TEXT"] = "text";
  1027. })(LoaderResource.XHR_RESPONSE_TYPE || (LoaderResource.XHR_RESPONSE_TYPE = {}));
  1028. LoaderResource._loadTypeMap = {
  1029. // images
  1030. gif: LoaderResource.LOAD_TYPE.IMAGE,
  1031. png: LoaderResource.LOAD_TYPE.IMAGE,
  1032. bmp: LoaderResource.LOAD_TYPE.IMAGE,
  1033. jpg: LoaderResource.LOAD_TYPE.IMAGE,
  1034. jpeg: LoaderResource.LOAD_TYPE.IMAGE,
  1035. tif: LoaderResource.LOAD_TYPE.IMAGE,
  1036. tiff: LoaderResource.LOAD_TYPE.IMAGE,
  1037. webp: LoaderResource.LOAD_TYPE.IMAGE,
  1038. tga: LoaderResource.LOAD_TYPE.IMAGE,
  1039. avif: LoaderResource.LOAD_TYPE.IMAGE,
  1040. svg: LoaderResource.LOAD_TYPE.IMAGE,
  1041. 'svg+xml': LoaderResource.LOAD_TYPE.IMAGE,
  1042. // audio
  1043. mp3: LoaderResource.LOAD_TYPE.AUDIO,
  1044. ogg: LoaderResource.LOAD_TYPE.AUDIO,
  1045. wav: LoaderResource.LOAD_TYPE.AUDIO,
  1046. // videos
  1047. mp4: LoaderResource.LOAD_TYPE.VIDEO,
  1048. webm: LoaderResource.LOAD_TYPE.VIDEO,
  1049. };
  1050. LoaderResource._xhrTypeMap = {
  1051. // xml
  1052. xhtml: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1053. html: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1054. htm: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1055. xml: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1056. tmx: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1057. svg: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1058. // This was added to handle Tiled Tileset XML, but .tsx is also a TypeScript React Component.
  1059. // Since it is way less likely for people to be loading TypeScript files instead of Tiled files,
  1060. // this should probably be fine.
  1061. tsx: LoaderResource.XHR_RESPONSE_TYPE.DOCUMENT,
  1062. // images
  1063. gif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1064. png: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1065. bmp: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1066. jpg: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1067. jpeg: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1068. tif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1069. tiff: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1070. webp: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1071. tga: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1072. avif: LoaderResource.XHR_RESPONSE_TYPE.BLOB,
  1073. // json
  1074. json: LoaderResource.XHR_RESPONSE_TYPE.JSON,
  1075. // text
  1076. text: LoaderResource.XHR_RESPONSE_TYPE.TEXT,
  1077. txt: LoaderResource.XHR_RESPONSE_TYPE.TEXT,
  1078. // fonts
  1079. ttf: LoaderResource.XHR_RESPONSE_TYPE.BUFFER,
  1080. otf: LoaderResource.XHR_RESPONSE_TYPE.BUFFER,
  1081. };
  1082. // We can't set the `src` attribute to empty string, so on abort we set it to this 1px transparent gif
  1083. LoaderResource.EMPTY_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
  1084. })(exports.LoaderResource || (exports.LoaderResource = {}));
  1085. /**
  1086. * Smaller version of the async library constructs.
  1087. * @ignore
  1088. */
  1089. function _noop() {
  1090. }
  1091. /**
  1092. * Ensures a function is only called once.
  1093. * @ignore
  1094. * @param {Function} fn - The function to wrap.
  1095. * @returns {Function} The wrapping function.
  1096. */
  1097. function onlyOnce(fn) {
  1098. return function onceWrapper() {
  1099. var arguments$1 = arguments;
  1100. var args = [];
  1101. for (var _i = 0; _i < arguments.length; _i++) {
  1102. args[_i] = arguments$1[_i];
  1103. }
  1104. if (fn === null) {
  1105. throw new Error('Callback was already called.');
  1106. }
  1107. var callFn = fn;
  1108. fn = null;
  1109. callFn.apply(this, args);
  1110. };
  1111. }
  1112. /**
  1113. * @private
  1114. * @memberof PIXI
  1115. */
  1116. var AsyncQueueItem = /** @class */ (function () {
  1117. /**
  1118. * @param data
  1119. * @param callback
  1120. * @private
  1121. */
  1122. function AsyncQueueItem(data, callback) {
  1123. this.data = data;
  1124. this.callback = callback;
  1125. }
  1126. return AsyncQueueItem;
  1127. }());
  1128. /**
  1129. * @private
  1130. * @memberof PIXI
  1131. */
  1132. var AsyncQueue = /** @class */ (function () {
  1133. /**
  1134. * @param worker
  1135. * @param concurrency
  1136. * @private
  1137. */
  1138. function AsyncQueue(worker, concurrency) {
  1139. var _this = this;
  1140. if (concurrency === void 0) { concurrency = 1; }
  1141. this.workers = 0;
  1142. this.saturated = _noop;
  1143. this.unsaturated = _noop;
  1144. this.empty = _noop;
  1145. this.drain = _noop;
  1146. this.error = _noop;
  1147. this.started = false;
  1148. this.paused = false;
  1149. this._tasks = [];
  1150. this._insert = function (data, insertAtFront, callback) {
  1151. if (callback && typeof callback !== 'function') {
  1152. throw new Error('task callback must be a function');
  1153. }
  1154. _this.started = true;
  1155. // eslint-disable-next-line no-eq-null,eqeqeq
  1156. if (data == null && _this.idle()) {
  1157. // call drain immediately if there are no tasks
  1158. setTimeout(function () { return _this.drain(); }, 1);
  1159. return;
  1160. }
  1161. var item = new AsyncQueueItem(data, typeof callback === 'function' ? callback : _noop);
  1162. if (insertAtFront) {
  1163. _this._tasks.unshift(item);
  1164. }
  1165. else {
  1166. _this._tasks.push(item);
  1167. }
  1168. setTimeout(_this.process, 1);
  1169. };
  1170. this.process = function () {
  1171. while (!_this.paused && _this.workers < _this.concurrency && _this._tasks.length) {
  1172. var task = _this._tasks.shift();
  1173. if (_this._tasks.length === 0) {
  1174. _this.empty();
  1175. }
  1176. _this.workers += 1;
  1177. if (_this.workers === _this.concurrency) {
  1178. _this.saturated();
  1179. }
  1180. _this._worker(task.data, onlyOnce(_this._next(task)));
  1181. }
  1182. };
  1183. this._worker = worker;
  1184. if (concurrency === 0) {
  1185. throw new Error('Concurrency must not be zero');
  1186. }
  1187. this.concurrency = concurrency;
  1188. this.buffer = concurrency / 4.0;
  1189. }
  1190. /**
  1191. * @param task
  1192. * @private
  1193. */
  1194. AsyncQueue.prototype._next = function (task) {
  1195. var _this = this;
  1196. return function () {
  1197. var arguments$1 = arguments;
  1198. var args = [];
  1199. for (var _i = 0; _i < arguments.length; _i++) {
  1200. args[_i] = arguments$1[_i];
  1201. }
  1202. _this.workers -= 1;
  1203. task.callback.apply(task, args);
  1204. // eslint-disable-next-line no-eq-null,eqeqeq
  1205. if (args[0] != null) {
  1206. _this.error(args[0], task.data);
  1207. }
  1208. if (_this.workers <= (_this.concurrency - _this.buffer)) {
  1209. _this.unsaturated();
  1210. }
  1211. if (_this.idle()) {
  1212. _this.drain();
  1213. }
  1214. _this.process();
  1215. };
  1216. };
  1217. // That was in object
  1218. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  1219. AsyncQueue.prototype.push = function (data, callback) {
  1220. this._insert(data, false, callback);
  1221. };
  1222. AsyncQueue.prototype.kill = function () {
  1223. this.workers = 0;
  1224. this.drain = _noop;
  1225. this.started = false;
  1226. this._tasks = [];
  1227. };
  1228. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  1229. AsyncQueue.prototype.unshift = function (data, callback) {
  1230. this._insert(data, true, callback);
  1231. };
  1232. AsyncQueue.prototype.length = function () {
  1233. return this._tasks.length;
  1234. };
  1235. AsyncQueue.prototype.running = function () {
  1236. return this.workers;
  1237. };
  1238. AsyncQueue.prototype.idle = function () {
  1239. return this._tasks.length + this.workers === 0;
  1240. };
  1241. AsyncQueue.prototype.pause = function () {
  1242. if (this.paused === true) {
  1243. return;
  1244. }
  1245. this.paused = true;
  1246. };
  1247. AsyncQueue.prototype.resume = function () {
  1248. if (this.paused === false) {
  1249. return;
  1250. }
  1251. this.paused = false;
  1252. // Need to call this.process once per concurrent
  1253. // worker to preserve full concurrency after pause
  1254. for (var w = 1; w <= this.concurrency; w++) {
  1255. this.process();
  1256. }
  1257. };
  1258. /**
  1259. * Iterates an array in series.
  1260. * @param {Array.<*>} array - Array to iterate.
  1261. * @param {Function} iterator - Function to call for each element.
  1262. * @param {Function} callback - Function to call when done, or on error.
  1263. * @param {boolean} [deferNext=false] - Break synchronous each loop by calling next with a setTimeout of 1.
  1264. */
  1265. AsyncQueue.eachSeries = function (array, iterator, callback, deferNext) {
  1266. var i = 0;
  1267. var len = array.length;
  1268. function next(err) {
  1269. if (err || i === len) {
  1270. if (callback) {
  1271. callback(err);
  1272. }
  1273. return;
  1274. }
  1275. if (deferNext) {
  1276. setTimeout(function () {
  1277. iterator(array[i++], next);
  1278. }, 1);
  1279. }
  1280. else {
  1281. iterator(array[i++], next);
  1282. }
  1283. }
  1284. next();
  1285. };
  1286. /**
  1287. * Async queue implementation,
  1288. * @param {Function} worker - The worker function to call for each task.
  1289. * @param {number} concurrency - How many workers to run in parrallel.
  1290. * @returns {*} The async queue object.
  1291. */
  1292. AsyncQueue.queue = function (worker, concurrency) {
  1293. return new AsyncQueue(worker, concurrency);
  1294. };
  1295. return AsyncQueue;
  1296. }());
  1297. // some constants
  1298. var MAX_PROGRESS = 100;
  1299. var rgxExtractUrlHash = /(#[\w-]+)?$/;
  1300. /**
  1301. * The new loader, forked from Resource Loader by Chad Engler: https://github.com/englercj/resource-loader
  1302. *
  1303. * ```js
  1304. * const loader = PIXI.Loader.shared; // PixiJS exposes a premade instance for you to use.
  1305. * // or
  1306. * const loader = new PIXI.Loader(); // You can also create your own if you want
  1307. *
  1308. * const sprites = {};
  1309. *
  1310. * // Chainable `add` to enqueue a resource
  1311. * loader.add('bunny', 'data/bunny.png')
  1312. * .add('spaceship', 'assets/spritesheet.json');
  1313. * loader.add('scoreFont', 'assets/score.fnt');
  1314. *
  1315. * // Chainable `pre` to add a middleware that runs for each resource, *before* loading that resource.
  1316. * // This is useful to implement custom caching modules (using filesystem, indexeddb, memory, etc).
  1317. * loader.pre(cachingMiddleware);
  1318. *
  1319. * // Chainable `use` to add a middleware that runs for each resource, *after* loading that resource.
  1320. * // This is useful to implement custom parsing modules (like spritesheet parsers, spine parser, etc).
  1321. * loader.use(parsingMiddleware);
  1322. *
  1323. * // The `load` method loads the queue of resources, and calls the passed in callback called once all
  1324. * // resources have loaded.
  1325. * loader.load((loader, resources) => {
  1326. * // resources is an object where the key is the name of the resource loaded and the value is the resource object.
  1327. * // They have a couple default properties:
  1328. * // - `url`: The URL that the resource was loaded from
  1329. * // - `error`: The error that happened when trying to load (if any)
  1330. * // - `data`: The raw data that was loaded
  1331. * // also may contain other properties based on the middleware that runs.
  1332. * sprites.bunny = new PIXI.TilingSprite(resources.bunny.texture);
  1333. * sprites.spaceship = new PIXI.TilingSprite(resources.spaceship.texture);
  1334. * sprites.scoreFont = new PIXI.TilingSprite(resources.scoreFont.texture);
  1335. * });
  1336. *
  1337. * // throughout the process multiple signals can be dispatched.
  1338. * loader.onProgress.add(() => {}); // called once per loaded/errored file
  1339. * loader.onError.add(() => {}); // called once per errored file
  1340. * loader.onLoad.add(() => {}); // called once per loaded file
  1341. * loader.onComplete.add(() => {}); // called once when the queued resources all load.
  1342. * ```
  1343. * @memberof PIXI
  1344. */
  1345. var Loader = /** @class */ (function () {
  1346. /**
  1347. * @param baseUrl - The base url for all resources loaded by this loader.
  1348. * @param concurrency - The number of resources to load concurrently.
  1349. */
  1350. function Loader(baseUrl, concurrency) {
  1351. var _this = this;
  1352. if (baseUrl === void 0) { baseUrl = ''; }
  1353. if (concurrency === void 0) { concurrency = 10; }
  1354. /** The progress percent of the loader going through the queue. */
  1355. this.progress = 0;
  1356. /** Loading state of the loader, true if it is currently loading resources. */
  1357. this.loading = false;
  1358. /**
  1359. * A querystring to append to every URL added to the loader.
  1360. *
  1361. * This should be a valid query string *without* the question-mark (`?`). The loader will
  1362. * also *not* escape values for you. Make sure to escape your parameters with
  1363. * [`encodeURIComponent`](https://mdn.io/encodeURIComponent) before assigning this property.
  1364. * @example
  1365. * const loader = new Loader();
  1366. *
  1367. * loader.defaultQueryString = 'user=me&password=secret';
  1368. *
  1369. * // This will request 'image.png?user=me&password=secret'
  1370. * loader.add('image.png').load();
  1371. *
  1372. * loader.reset();
  1373. *
  1374. * // This will request 'image.png?v=1&user=me&password=secret'
  1375. * loader.add('iamge.png?v=1').load();
  1376. */
  1377. this.defaultQueryString = '';
  1378. /** The middleware to run before loading each resource. */
  1379. this._beforeMiddleware = [];
  1380. /** The middleware to run after loading each resource. */
  1381. this._afterMiddleware = [];
  1382. /** The tracks the resources we are currently completing parsing for. */
  1383. this._resourcesParsing = [];
  1384. /**
  1385. * The `_loadResource` function bound with this object context.
  1386. * @param r - The resource to load
  1387. * @param d - The dequeue function
  1388. */
  1389. this._boundLoadResource = function (r, d) { return _this._loadResource(r, d); };
  1390. /** All the resources for this loader keyed by name. */
  1391. this.resources = {};
  1392. this.baseUrl = baseUrl;
  1393. this._beforeMiddleware = [];
  1394. this._afterMiddleware = [];
  1395. this._resourcesParsing = [];
  1396. this._boundLoadResource = function (r, d) { return _this._loadResource(r, d); };
  1397. this._queue = AsyncQueue.queue(this._boundLoadResource, concurrency);
  1398. this._queue.pause();
  1399. this.resources = {};
  1400. this.onProgress = new Signal();
  1401. this.onError = new Signal();
  1402. this.onLoad = new Signal();
  1403. this.onStart = new Signal();
  1404. this.onComplete = new Signal();
  1405. for (var i = 0; i < Loader._plugins.length; ++i) {
  1406. var plugin = Loader._plugins[i];
  1407. var pre = plugin.pre, use = plugin.use;
  1408. if (pre) {
  1409. this.pre(pre);
  1410. }
  1411. if (use) {
  1412. this.use(use);
  1413. }
  1414. }
  1415. this._protected = false;
  1416. }
  1417. /**
  1418. * Same as add, params have strict order
  1419. * @private
  1420. * @param name - The name of the resource to load.
  1421. * @param url - The url for this resource, relative to the baseUrl of this loader.
  1422. * @param options - The options for the load.
  1423. * @param callback - Function to call when this specific resource completes loading.
  1424. * @returns The loader itself.
  1425. */
  1426. Loader.prototype._add = function (name, url, options, callback) {
  1427. // if loading already you can only add resources that have a parent.
  1428. if (this.loading && (!options || !options.parentResource)) {
  1429. throw new Error('Cannot add resources while the loader is running.');
  1430. }
  1431. // check if resource already exists.
  1432. if (this.resources[name]) {
  1433. throw new Error("Resource named \"" + name + "\" already exists.");
  1434. }
  1435. // add base url if this isn't an absolute url
  1436. url = this._prepareUrl(url);
  1437. // create the store the resource
  1438. this.resources[name] = new exports.LoaderResource(name, url, options);
  1439. if (typeof callback === 'function') {
  1440. this.resources[name].onAfterMiddleware.once(callback);
  1441. }
  1442. // if actively loading, make sure to adjust progress chunks for that parent and its children
  1443. if (this.loading) {
  1444. var parent = options.parentResource;
  1445. var incompleteChildren = [];
  1446. for (var i = 0; i < parent.children.length; ++i) {
  1447. if (!parent.children[i].isComplete) {
  1448. incompleteChildren.push(parent.children[i]);
  1449. }
  1450. }
  1451. var fullChunk = parent.progressChunk * (incompleteChildren.length + 1); // +1 for parent
  1452. var eachChunk = fullChunk / (incompleteChildren.length + 2); // +2 for parent & new child
  1453. parent.children.push(this.resources[name]);
  1454. parent.progressChunk = eachChunk;
  1455. for (var i = 0; i < incompleteChildren.length; ++i) {
  1456. incompleteChildren[i].progressChunk = eachChunk;
  1457. }
  1458. this.resources[name].progressChunk = eachChunk;
  1459. }
  1460. // add the resource to the queue
  1461. this._queue.push(this.resources[name]);
  1462. return this;
  1463. };
  1464. /* eslint-enable require-jsdoc,valid-jsdoc */
  1465. /**
  1466. * Sets up a middleware function that will run *before* the
  1467. * resource is loaded.
  1468. * @param fn - The middleware function to register.
  1469. * @returns The loader itself.
  1470. */
  1471. Loader.prototype.pre = function (fn) {
  1472. this._beforeMiddleware.push(fn);
  1473. return this;
  1474. };
  1475. /**
  1476. * Sets up a middleware function that will run *after* the
  1477. * resource is loaded.
  1478. * @param fn - The middleware function to register.
  1479. * @returns The loader itself.
  1480. */
  1481. Loader.prototype.use = function (fn) {
  1482. this._afterMiddleware.push(fn);
  1483. return this;
  1484. };
  1485. /**
  1486. * Resets the queue of the loader to prepare for a new load.
  1487. * @returns The loader itself.
  1488. */
  1489. Loader.prototype.reset = function () {
  1490. this.progress = 0;
  1491. this.loading = false;
  1492. this._queue.kill();
  1493. this._queue.pause();
  1494. // abort all resource loads
  1495. for (var k in this.resources) {
  1496. var res = this.resources[k];
  1497. if (res._onLoadBinding) {
  1498. res._onLoadBinding.detach();
  1499. }
  1500. if (res.isLoading) {
  1501. res.abort('loader reset');
  1502. }
  1503. }
  1504. this.resources = {};
  1505. return this;
  1506. };
  1507. /**
  1508. * Starts loading the queued resources.
  1509. * @param cb - Optional callback that will be bound to the `complete` event.
  1510. * @returns The loader itself.
  1511. */
  1512. Loader.prototype.load = function (cb) {
  1513. utils.deprecation('6.5.0', '@pixi/loaders is being replaced with @pixi/assets in the next major release.');
  1514. // register complete callback if they pass one
  1515. if (typeof cb === 'function') {
  1516. this.onComplete.once(cb);
  1517. }
  1518. // if the queue has already started we are done here
  1519. if (this.loading) {
  1520. return this;
  1521. }
  1522. if (this._queue.idle()) {
  1523. this._onStart();
  1524. this._onComplete();
  1525. }
  1526. else {
  1527. // distribute progress chunks
  1528. var numTasks = this._queue._tasks.length;
  1529. var chunk = MAX_PROGRESS / numTasks;
  1530. for (var i = 0; i < this._queue._tasks.length; ++i) {
  1531. this._queue._tasks[i].data.progressChunk = chunk;
  1532. }
  1533. // notify we are starting
  1534. this._onStart();
  1535. // start loading
  1536. this._queue.resume();
  1537. }
  1538. return this;
  1539. };
  1540. Object.defineProperty(Loader.prototype, "concurrency", {
  1541. /**
  1542. * The number of resources to load concurrently.
  1543. * @default 10
  1544. */
  1545. get: function () {
  1546. return this._queue.concurrency;
  1547. },
  1548. set: function (concurrency) {
  1549. this._queue.concurrency = concurrency;
  1550. },
  1551. enumerable: false,
  1552. configurable: true
  1553. });
  1554. /**
  1555. * Prepares a url for usage based on the configuration of this object
  1556. * @param url - The url to prepare.
  1557. * @returns The prepared url.
  1558. */
  1559. Loader.prototype._prepareUrl = function (url) {
  1560. var parsedUrl = parseUri(url, { strictMode: true });
  1561. var result;
  1562. // absolute url, just use it as is.
  1563. if (parsedUrl.protocol || !parsedUrl.path || url.indexOf('//') === 0) {
  1564. result = url;
  1565. }
  1566. // if baseUrl doesn't end in slash and url doesn't start with slash, then add a slash inbetween
  1567. else if (this.baseUrl.length
  1568. && this.baseUrl.lastIndexOf('/') !== this.baseUrl.length - 1
  1569. && url.charAt(0) !== '/') {
  1570. result = this.baseUrl + "/" + url;
  1571. }
  1572. else {
  1573. result = this.baseUrl + url;
  1574. }
  1575. // if we need to add a default querystring, there is a bit more work
  1576. if (this.defaultQueryString) {
  1577. var hash = rgxExtractUrlHash.exec(result)[0];
  1578. result = result.slice(0, result.length - hash.length);
  1579. if (result.indexOf('?') !== -1) {
  1580. result += "&" + this.defaultQueryString;
  1581. }
  1582. else {
  1583. result += "?" + this.defaultQueryString;
  1584. }
  1585. result += hash;
  1586. }
  1587. return result;
  1588. };
  1589. /**
  1590. * Loads a single resource.
  1591. * @param resource - The resource to load.
  1592. * @param dequeue - The function to call when we need to dequeue this item.
  1593. */
  1594. Loader.prototype._loadResource = function (resource, dequeue) {
  1595. var _this = this;
  1596. resource._dequeue = dequeue;
  1597. // run before middleware
  1598. AsyncQueue.eachSeries(this._beforeMiddleware, function (fn, next) {
  1599. fn.call(_this, resource, function () {
  1600. // if the before middleware marks the resource as complete,
  1601. // break and don't process any more before middleware
  1602. next(resource.isComplete ? {} : null);
  1603. });
  1604. }, function () {
  1605. if (resource.isComplete) {
  1606. _this._onLoad(resource);
  1607. }
  1608. else {
  1609. resource._onLoadBinding = resource.onComplete.once(_this._onLoad, _this);
  1610. resource.load();
  1611. }
  1612. }, true);
  1613. };
  1614. /** Called once loading has started. */
  1615. Loader.prototype._onStart = function () {
  1616. this.progress = 0;
  1617. this.loading = true;
  1618. this.onStart.dispatch(this);
  1619. };
  1620. /** Called once each resource has loaded. */
  1621. Loader.prototype._onComplete = function () {
  1622. this.progress = MAX_PROGRESS;
  1623. this.loading = false;
  1624. this.onComplete.dispatch(this, this.resources);
  1625. };
  1626. /**
  1627. * Called each time a resources is loaded.
  1628. * @param resource - The resource that was loaded
  1629. */
  1630. Loader.prototype._onLoad = function (resource) {
  1631. var _this = this;
  1632. resource._onLoadBinding = null;
  1633. // remove this resource from the async queue, and add it to our list of resources that are being parsed
  1634. this._resourcesParsing.push(resource);
  1635. resource._dequeue();
  1636. // run all the after middleware for this resource
  1637. AsyncQueue.eachSeries(this._afterMiddleware, function (fn, next) {
  1638. fn.call(_this, resource, next);
  1639. }, function () {
  1640. resource.onAfterMiddleware.dispatch(resource);
  1641. _this.progress = Math.min(MAX_PROGRESS, _this.progress + resource.progressChunk);
  1642. _this.onProgress.dispatch(_this, resource);
  1643. if (resource.error) {
  1644. _this.onError.dispatch(resource.error, _this, resource);
  1645. }
  1646. else {
  1647. _this.onLoad.dispatch(_this, resource);
  1648. }
  1649. _this._resourcesParsing.splice(_this._resourcesParsing.indexOf(resource), 1);
  1650. // do completion check
  1651. if (_this._queue.idle() && _this._resourcesParsing.length === 0) {
  1652. _this._onComplete();
  1653. }
  1654. }, true);
  1655. };
  1656. /** Destroy the loader, removes references. */
  1657. Loader.prototype.destroy = function () {
  1658. if (!this._protected) {
  1659. this.reset();
  1660. }
  1661. };
  1662. Object.defineProperty(Loader, "shared", {
  1663. /** A premade instance of the loader that can be used to load resources. */
  1664. get: function () {
  1665. var shared = Loader._shared;
  1666. if (!shared) {
  1667. shared = new Loader();
  1668. shared._protected = true;
  1669. Loader._shared = shared;
  1670. }
  1671. return shared;
  1672. },
  1673. enumerable: false,
  1674. configurable: true
  1675. });
  1676. /**
  1677. * Use the {@link PIXI.extensions.add} API to register plugins.
  1678. * @deprecated since 6.5.0
  1679. * @param plugin - The plugin to add
  1680. * @returns Reference to PIXI.Loader for chaining
  1681. */
  1682. Loader.registerPlugin = function (plugin) {
  1683. utils.deprecation('6.5.0', 'Loader.registerPlugin() is deprecated, use extensions.add() instead.');
  1684. core.extensions.add({
  1685. type: core.ExtensionType.Loader,
  1686. ref: plugin,
  1687. });
  1688. return Loader;
  1689. };
  1690. Loader._plugins = [];
  1691. return Loader;
  1692. }());
  1693. core.extensions.handleByList(core.ExtensionType.Loader, Loader._plugins);
  1694. Loader.prototype.add = function add(name, url, options, callback) {
  1695. // special case of an array of objects or urls
  1696. if (Array.isArray(name)) {
  1697. for (var i = 0; i < name.length; ++i) {
  1698. this.add(name[i]);
  1699. }
  1700. return this;
  1701. }
  1702. // if an object is passed instead of params
  1703. if (typeof name === 'object') {
  1704. options = name;
  1705. callback = url || options.callback || options.onComplete;
  1706. url = options.url;
  1707. name = options.name || options.key || options.url;
  1708. }
  1709. // case where no name is passed shift all args over by one.
  1710. if (typeof url !== 'string') {
  1711. callback = options;
  1712. options = url;
  1713. url = name;
  1714. }
  1715. // now that we shifted make sure we have a proper url.
  1716. if (typeof url !== 'string') {
  1717. throw new Error('No url passed to add resource to loader.');
  1718. }
  1719. // options are optional so people might pass a function and no options
  1720. if (typeof options === 'function') {
  1721. callback = options;
  1722. options = null;
  1723. }
  1724. return this._add(name, url, options, callback);
  1725. };
  1726. /**
  1727. * Application plugin for supporting loader option. Installing the LoaderPlugin
  1728. * is not necessary if using **pixi.js** or **pixi.js-legacy**.
  1729. * @example
  1730. * import {AppLoaderPlugin} from '@pixi/loaders';
  1731. * import {extensions} from '@pixi/core';
  1732. * extensions.add(AppLoaderPlugin);
  1733. * @memberof PIXI
  1734. */
  1735. var AppLoaderPlugin = /** @class */ (function () {
  1736. function AppLoaderPlugin() {
  1737. }
  1738. /**
  1739. * Called on application constructor
  1740. * @param options
  1741. * @private
  1742. */
  1743. AppLoaderPlugin.init = function (options) {
  1744. options = Object.assign({
  1745. sharedLoader: false,
  1746. }, options);
  1747. this.loader = options.sharedLoader ? Loader.shared : new Loader();
  1748. };
  1749. /**
  1750. * Called when application destroyed
  1751. * @private
  1752. */
  1753. AppLoaderPlugin.destroy = function () {
  1754. if (this.loader) {
  1755. this.loader.destroy();
  1756. this.loader = null;
  1757. }
  1758. };
  1759. /** @ignore */
  1760. AppLoaderPlugin.extension = core.ExtensionType.Application;
  1761. return AppLoaderPlugin;
  1762. }());
  1763. /**
  1764. * Loader plugin for handling Texture resources.
  1765. * @memberof PIXI
  1766. */
  1767. var TextureLoader = /** @class */ (function () {
  1768. function TextureLoader() {
  1769. }
  1770. /** Handle SVG elements a text, render with SVGResource. */
  1771. TextureLoader.add = function () {
  1772. exports.LoaderResource.setExtensionLoadType('svg', exports.LoaderResource.LOAD_TYPE.XHR);
  1773. exports.LoaderResource.setExtensionXhrType('svg', exports.LoaderResource.XHR_RESPONSE_TYPE.TEXT);
  1774. };
  1775. /**
  1776. * Called after a resource is loaded.
  1777. * @see PIXI.Loader.loaderMiddleware
  1778. * @param resource
  1779. * @param {Function} next
  1780. */
  1781. TextureLoader.use = function (resource, next) {
  1782. // create a new texture if the data is an Image object
  1783. if (resource.data && (resource.type === exports.LoaderResource.TYPE.IMAGE || resource.extension === 'svg')) {
  1784. var data = resource.data, url = resource.url, name = resource.name, metadata = resource.metadata;
  1785. core.Texture.fromLoader(data, url, name, metadata).then(function (texture) {
  1786. resource.texture = texture;
  1787. next();
  1788. })
  1789. // TODO: handle errors in Texture.fromLoader
  1790. // so we can pass them to the Loader
  1791. .catch(next);
  1792. }
  1793. else {
  1794. next();
  1795. }
  1796. };
  1797. /** @ignore */
  1798. TextureLoader.extension = core.ExtensionType.Loader;
  1799. return TextureLoader;
  1800. }());
  1801. var _keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  1802. /**
  1803. * Encodes binary into base64.
  1804. * @function encodeBinary
  1805. * @param {string} input - The input data to encode.
  1806. * @returns {string} The encoded base64 string
  1807. */
  1808. function encodeBinary(input) {
  1809. var output = '';
  1810. var inx = 0;
  1811. while (inx < input.length) {
  1812. // Fill byte buffer array
  1813. var bytebuffer = [0, 0, 0];
  1814. var encodedCharIndexes = [0, 0, 0, 0];
  1815. for (var jnx = 0; jnx < bytebuffer.length; ++jnx) {
  1816. if (inx < input.length) {
  1817. // throw away high-order byte, as documented at:
  1818. // https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data
  1819. bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff;
  1820. }
  1821. else {
  1822. bytebuffer[jnx] = 0;
  1823. }
  1824. }
  1825. // Get each encoded character, 6 bits at a time
  1826. // index 1: first 6 bits
  1827. encodedCharIndexes[0] = bytebuffer[0] >> 2;
  1828. // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2)
  1829. encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4);
  1830. // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3)
  1831. encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6);
  1832. // index 3: forth 6 bits (6 least significant bits from input byte 3)
  1833. encodedCharIndexes[3] = bytebuffer[2] & 0x3f;
  1834. // Determine whether padding happened, and adjust accordingly
  1835. var paddingBytes = inx - (input.length - 1);
  1836. switch (paddingBytes) {
  1837. case 2:
  1838. // Set last 2 characters to padding char
  1839. encodedCharIndexes[3] = 64;
  1840. encodedCharIndexes[2] = 64;
  1841. break;
  1842. case 1:
  1843. // Set last character to padding char
  1844. encodedCharIndexes[3] = 64;
  1845. break;
  1846. }
  1847. // Now we will grab each appropriate character out of our keystring
  1848. // based on our index array and append it to the output string
  1849. for (var jnx = 0; jnx < encodedCharIndexes.length; ++jnx) {
  1850. output += _keyStr.charAt(encodedCharIndexes[jnx]);
  1851. }
  1852. }
  1853. return output;
  1854. }
  1855. /**
  1856. * A middleware for transforming XHR loaded Blobs into more useful objects
  1857. * @ignore
  1858. * @function parsing
  1859. * @example
  1860. * import { Loader, middleware } from 'resource-loader';
  1861. * const loader = new Loader();
  1862. * loader.use(middleware.parsing);
  1863. * @param resource - Current Resource
  1864. * @param next - Callback when complete
  1865. */
  1866. function parsing(resource, next) {
  1867. if (!resource.data) {
  1868. next();
  1869. return;
  1870. }
  1871. // if this was an XHR load of a blob
  1872. if (resource.xhr && resource.xhrType === exports.LoaderResource.XHR_RESPONSE_TYPE.BLOB) {
  1873. // if there is no blob support we probably got a binary string back
  1874. if (!self.Blob || typeof resource.data === 'string') {
  1875. var type = resource.xhr.getResponseHeader('content-type');
  1876. // this is an image, convert the binary string into a data url
  1877. if (type && type.indexOf('image') === 0) {
  1878. resource.data = new Image();
  1879. resource.data.src = "data:" + type + ";base64," + encodeBinary(resource.xhr.responseText);
  1880. resource.type = exports.LoaderResource.TYPE.IMAGE;
  1881. // wait until the image loads and then callback
  1882. resource.data.onload = function () {
  1883. resource.data.onload = null;
  1884. next();
  1885. };
  1886. // next will be called on load
  1887. return;
  1888. }
  1889. }
  1890. // if content type says this is an image, then we should transform the blob into an Image object
  1891. else if (resource.data.type.indexOf('image') === 0) {
  1892. var Url_1 = globalThis.URL || globalThis.webkitURL;
  1893. var src_1 = Url_1.createObjectURL(resource.data);
  1894. resource.blob = resource.data;
  1895. resource.data = new Image();
  1896. resource.data.src = src_1;
  1897. resource.type = exports.LoaderResource.TYPE.IMAGE;
  1898. // cleanup the no longer used blob after the image loads
  1899. // TODO: Is this correct? Will the image be invalid after revoking?
  1900. resource.data.onload = function () {
  1901. Url_1.revokeObjectURL(src_1);
  1902. resource.data.onload = null;
  1903. next();
  1904. };
  1905. // next will be called on load.
  1906. return;
  1907. }
  1908. }
  1909. next();
  1910. }
  1911. /**
  1912. * Parse any blob into more usable objects (e.g. Image).
  1913. * @memberof PIXI
  1914. */
  1915. var ParsingLoader = /** @class */ (function () {
  1916. function ParsingLoader() {
  1917. }
  1918. /** @ignore */
  1919. ParsingLoader.extension = core.ExtensionType.Loader;
  1920. ParsingLoader.use = parsing;
  1921. return ParsingLoader;
  1922. }());
  1923. core.extensions.add(TextureLoader, ParsingLoader);
  1924. exports.AppLoaderPlugin = AppLoaderPlugin;
  1925. exports.Loader = Loader;
  1926. exports.TextureLoader = TextureLoader;
  1927. //# sourceMappingURL=loaders.js.map