{"version":3,"file":"Loader.mjs","sources":["../../src/loader/Loader.ts"],"sourcesContent":["import { utils } from '@pixi/core';\nimport { convertToList, isSingleItem } from '../utils';\n\nimport type { ResolvedAsset } from '../types';\nimport type { LoaderParser } from './parsers/LoaderParser';\nimport type { PromiseAndParser } from './types';\n\n/**\n * The Loader is responsible for loading all assets, such as images, spritesheets, audio files, etc.\n * It does not do anything clever with URLs - it just loads stuff!\n * Behind the scenes all things are cached using promises. This means it's impossible to load an asset more than once.\n * Through the use of LoaderParsers, the loader can understand how to load any kind of file!\n *\n * It is not intended that this class is created by developers - its part of the Asset class\n * This is the second major system of PixiJS' main Assets class\n * @memberof PIXI\n * @class AssetLoader\n */\nexport class Loader\n{\n    private _parsers: LoaderParser[] = [];\n    private _parserHash: Record<string, LoaderParser>;\n\n    private _parsersValidated = false;\n\n    /** All loader parsers registered */\n    public parsers = new Proxy(this._parsers, {\n        set: (target, key, value) =>\n        {\n            this._parsersValidated = false;\n\n            target[key as any as number] = value;\n\n            return true;\n        }\n    });\n\n    /** Cache loading promises that ae currently active */\n    public promiseCache: Record<string, PromiseAndParser> = {};\n\n    /** function used for testing */\n    public reset(): void\n    {\n        this._parsersValidated = false;\n        this.promiseCache = {};\n    }\n\n    /**\n     * Used internally to generate a promise for the asset to be loaded.\n     * @param url - The URL to be loaded\n     * @param data - any custom additional information relevant to the asset being loaded\n     * @returns - a promise that will resolve to an Asset for example a Texture of a JSON object\n     */\n    private _getLoadPromiseAndParser(url: string, data?: ResolvedAsset): PromiseAndParser\n    {\n        const result: PromiseAndParser = {\n            promise: null,\n            parser: null\n        };\n\n        result.promise = (async () =>\n        {\n            let asset = null;\n\n            let parser: LoaderParser = null;\n\n            // first check to see if the user has specified a parser\n            if (data.loadParser)\n            {\n                // they have? lovely, lets use it\n                parser = this._parserHash[data.loadParser];\n\n                if (!parser)\n                {\n                    if (process.env.DEBUG)\n                    {\n                        // eslint-disable-next-line max-len\n                        console.warn(`[Assets] specified load parser \"${data.loadParser}\" not found while loading ${url}`);\n                    }\n                }\n            }\n\n            // no parser specified, so lets try and find one using the tests\n            if (!parser)\n            {\n                for (let i = 0; i < this.parsers.length; i++)\n                {\n                    const parserX = this.parsers[i];\n\n                    if (parserX.load && parserX.test?.(url, data, this))\n                    {\n                        parser = parserX;\n                        break;\n                    }\n                }\n\n                if (!parser)\n                {\n                    if (process.env.DEBUG)\n                    {\n                        // eslint-disable-next-line max-len\n                        console.warn(`[Assets] ${url} could not be loaded as we don't know how to parse it, ensure the correct parser has been added`);\n                    }\n\n                    return null;\n                }\n            }\n\n            asset = await parser.load(url, data, this);\n            result.parser = parser;\n\n            for (let i = 0; i < this.parsers.length; i++)\n            {\n                const parser = this.parsers[i];\n\n                if (parser.parse)\n                {\n                    if (parser.parse && await parser.testParse?.(asset, data, this))\n                    {\n                        // transform the asset..\n                        asset = await parser.parse(asset, data, this) || asset;\n\n                        result.parser = parser;\n                    }\n                }\n            }\n\n            return asset;\n        })();\n\n        return result;\n    }\n\n    /**\n     * Loads one or more assets using the parsers added to the Loader.\n     * @example\n     * // Single asset:\n     * const asset = await Loader.load('cool.png');\n     * console.log(asset);\n     *\n     * // Multiple assets:\n     * const assets = await Loader.load(['cool.png', 'cooler.png']);\n     * console.log(assets);\n     * @param assetsToLoadIn - urls that you want to load, or a single one!\n     * @param onProgress - For multiple asset loading only, an optional function that is called\n     * when progress on asset loading is made. The function is passed a single parameter, `progress`,\n     * which represents the percentage (0.0 - 1.0) of the assets loaded. Do not use this function\n     * to detect when assets are complete and available, instead use the Promise returned by this function.\n     */\n    public async load<T = any>(\n        assetsToLoadIn: string | ResolvedAsset,\n        onProgress?: (progress: number) => void,\n    ): Promise<T>;\n    public async load<T = any>(\n        assetsToLoadIn: string[] | ResolvedAsset[],\n        onProgress?: (progress: number) => void,\n    ): Promise<Record<string, T>>;\n    public async load<T = any>(\n        assetsToLoadIn: string | string[] | ResolvedAsset | ResolvedAsset[],\n        onProgress?: (progress: number) => void,\n    ): Promise<T | Record<string, T>>\n    {\n        if (!this._parsersValidated)\n        {\n            this._validateParsers();\n        }\n\n        let count = 0;\n\n        const assets: Record<string, Promise<any>> = {};\n\n        const singleAsset = isSingleItem(assetsToLoadIn);\n\n        const assetsToLoad = convertToList<ResolvedAsset>(assetsToLoadIn, (item) => ({\n            alias: [item],\n            src: item,\n        }));\n\n        const total = assetsToLoad.length;\n\n        const promises: Promise<void>[] = assetsToLoad.map(async (asset: ResolvedAsset) =>\n        {\n            const url = utils.path.toAbsolute(asset.src);\n\n            if (!assets[asset.src])\n            {\n                try\n                {\n                    if (!this.promiseCache[url])\n                    {\n                        this.promiseCache[url] = this._getLoadPromiseAndParser(url, asset);\n                    }\n\n                    assets[asset.src] = await this.promiseCache[url].promise;\n\n                    // Only progress if nothing goes wrong\n                    if (onProgress) onProgress(++count / total);\n                }\n                catch (e)\n                {\n                    // Delete eventually registered file and promises from internal cache\n                    // so they can be eligible for another loading attempt\n                    delete this.promiseCache[url];\n                    delete assets[asset.src];\n\n                    // Stop further execution\n                    throw new Error(`[Loader.load] Failed to load ${url}.\\n${e}`);\n                }\n            }\n        });\n\n        await Promise.all(promises);\n\n        return singleAsset ? assets[assetsToLoad[0].src] : assets;\n    }\n\n    /**\n     * Unloads one or more assets. Any unloaded assets will be destroyed, freeing up memory for your app.\n     * The parser that created the asset, will be the one that unloads it.\n     * @example\n     * // Single asset:\n     * const asset = await Loader.load('cool.png');\n     *\n     * await Loader.unload('cool.png');\n     *\n     * console.log(asset.destroyed); // true\n     * @param assetsToUnloadIn - urls that you want to unload, or a single one!\n     */\n    public async unload(\n        assetsToUnloadIn: string | string[] | ResolvedAsset | ResolvedAsset[],\n    ): Promise<void>\n    {\n        const assetsToUnload = convertToList<ResolvedAsset>(assetsToUnloadIn, (item) => ({\n            alias: [item],\n            src: item,\n        }));\n\n        const promises: Promise<void>[] = assetsToUnload.map(async (asset: ResolvedAsset) =>\n        {\n            const url = utils.path.toAbsolute(asset.src);\n\n            const loadPromise = this.promiseCache[url];\n\n            if (loadPromise)\n            {\n                const loadedAsset = await loadPromise.promise;\n\n                delete this.promiseCache[url];\n\n                loadPromise.parser?.unload?.(loadedAsset, asset, this);\n            }\n        });\n\n        await Promise.all(promises);\n    }\n\n    /** validates our parsers, right now it only checks for name conflicts but we can add more here as required! */\n    private _validateParsers()\n    {\n        this._parsersValidated = true;\n\n        this._parserHash = this._parsers\n            .filter((parser) => parser.name)\n            .reduce((hash, parser) =>\n            {\n                if (hash[parser.name])\n                {\n                    if (process.env.DEBUG)\n                    {\n                        console.warn(`[Assets] loadParser name conflict \"${parser.name}\"`);\n                    }\n                }\n\n                return { ...hash, [parser.name]: parser };\n            }, {} as Record<string, LoaderParser>);\n    }\n}\n"],"names":["parser"],"mappings":"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"}