loaders.js 83 KB

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