loaders.mjs 74 KB

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