{"version":3,"file":"loader-base.mjs","sources":["../src/atlasLoader.ts","../src/SpineLoaderAbstract.ts"],"sourcesContent":["import { TextureAtlas, settings as spine_settings } from '@pixi-spine/base';\nimport { type AssetExtension, LoaderParserPriority, LoadAsset, Loader, checkExtension } from '@pixi/assets';\nimport { BaseTexture, extensions, ExtensionType, settings, Texture, utils } from '@pixi/core';\nimport type { ISpineMetadata } from './SpineLoaderAbstract';\n\ntype RawAtlas = string;\n\nconst spineTextureAtlasLoader: AssetExtension<RawAtlas | TextureAtlas, ISpineMetadata> = {\n    extension: ExtensionType.Asset,\n\n    // cache: {\n    //     test: (asset: RawAtlas | TextureAtlas) => asset instanceof TextureAtlas,\n    //     getCacheableAssets: (keys: string[], asset: RawAtlas | TextureAtlas) => getCacheableAssets(keys, asset),\n    // },\n\n    loader: {\n        extension: {\n            type: ExtensionType.LoadParser,\n            priority: LoaderParserPriority.Normal,\n        },\n\n        test(url: string): boolean {\n            return checkExtension(url, '.atlas');\n        },\n\n        async load(url: string): Promise<RawAtlas> {\n            const response = await settings.ADAPTER.fetch(url);\n\n            const txt = await response.text();\n\n            return txt as RawAtlas;\n        },\n\n        testParse(asset: unknown, options: LoadAsset): Promise<boolean> {\n            const isExtensionRight = checkExtension(options.src, '.atlas');\n            const isString = typeof asset === 'string';\n\n            return Promise.resolve(isExtensionRight && isString);\n        },\n\n        async parse(asset: RawAtlas, options: LoadAsset, loader: Loader): Promise<TextureAtlas> {\n            const metadata: ISpineMetadata = options.data;\n            let basePath = utils.path.dirname(options.src);\n\n            if (basePath && basePath.lastIndexOf('/') !== basePath.length - 1) {\n                basePath += '/';\n            }\n\n            let resolve = null;\n            let reject = null;\n            const retPromise = new Promise<TextureAtlas>((res, rej) => {\n                resolve = res;\n                reject = rej;\n            });\n\n            // Retval is going to be a texture atlas. However we need to wait for it's callback to resolve this promise.\n            let retval;\n            const resolveCallback = (newAtlas: TextureAtlas): void => {\n                if (!newAtlas) {\n                    reject('Something went terribly wrong loading a spine .atlas file\\nMost likely your texture failed to load.');\n                }\n                resolve(retval);\n            };\n\n            // if we have an already loaded pixi image in the image field, use that.\n            if (metadata.image || metadata.images) {\n                // merge the objects\n                const pages = Object.assign(metadata.image ? { default: metadata.image } : {}, metadata.images);\n\n                retval = new TextureAtlas(\n                    asset as RawAtlas,\n                    (line: any, callback: any) => {\n                        const page = pages[line] || (pages.default as any);\n\n                        if (page && page.baseTexture) callback(page.baseTexture);\n                        else callback(page);\n                    },\n                    resolveCallback\n                );\n            } else {\n                // We don't have ready to use pixi textures, we need to load them now!\n                retval = new TextureAtlas(asset as RawAtlas, makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject(loader, basePath, metadata.imageMetadata), resolveCallback);\n            }\n\n            return (await retPromise) as TextureAtlas;\n        },\n\n        unload(atlas: TextureAtlas) {\n            atlas.dispose();\n        },\n    },\n} as AssetExtension<RawAtlas | TextureAtlas, ISpineMetadata>;\n\n/**\n * Ugly function to promisify the spine texture atlas loader function.\n * @public\n */\nexport const makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject = (loader: Loader, atlasBasePath: string, imageMetadata: any) => {\n    return async (pageName: string, textureLoadedCallback: (tex: BaseTexture) => any): Promise<void> => {\n        try {\n            // const url = utils.path.join(...atlasBasePath.split(utils.path.sep), pageName); // Broken in upstream\n\n            const url = utils.path.normalize([...atlasBasePath.split(utils.path.sep), pageName].join(utils.path.sep));\n\n            const texture = await loader.load<Texture>({ src: url, data: imageMetadata });\n\n            textureLoadedCallback(texture.baseTexture);\n        } catch (e) {\n            if (spine_settings.REPORT_TEXTURE_LOADER_ERROR) {\n                console.error('Spine: error in texture loader', e);\n            }\n            textureLoadedCallback(null);\n        }\n    };\n};\n\nextensions.add(spineTextureAtlasLoader);\n","import { ISkeletonData, ISkeletonParser, TextureAtlas } from '@pixi-spine/base';\nimport { AssetExtension, checkExtension, LoadAsset, Loader, LoaderParserPriority } from '@pixi/assets';\nimport { BaseTexture, extensions, ExtensionType, settings, Texture, utils } from '@pixi/core';\nimport { makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject } from './atlasLoader';\n\ntype SPINEJSON = any;\ntype SPINEBINARY = ArrayBuffer;\n\nfunction isJson(resource: unknown): resource is SPINEJSON {\n    return resource.hasOwnProperty('bones');\n}\n\nfunction isBuffer(resource: unknown): resource is SPINEBINARY {\n    return resource instanceof ArrayBuffer;\n}\n\n/**\n * This abstract class is used to create a spine loader specifically for a needed version\n * @public\n */\nexport abstract class SpineLoaderAbstract<SKD extends ISkeletonData> {\n    constructor() {}\n\n    abstract createJsonParser(): ISkeletonParser;\n\n    abstract createBinaryParser(): ISkeletonParser;\n\n    abstract parseData(parser: ISkeletonParser, atlas: TextureAtlas, dataToParse: any): ISpineResource<SKD>;\n\n    public installLoader(): any {\n        // eslint-disable-next-line @typescript-eslint/no-this-alias\n        const spineAdapter = this;\n        const spineLoaderExtension: AssetExtension<SPINEJSON | SPINEBINARY | ISpineResource<SKD>, ISpineMetadata> = {\n            extension: ExtensionType.Asset,\n\n            loader: {\n                extension: {\n                    type: ExtensionType.LoadParser,\n                    priority: LoaderParserPriority.Normal,\n                },\n\n                // #region Downloading skel buffer data\n                test(url) {\n                    return checkExtension(url, '.skel');\n                },\n\n                async load<SPINEBINARY>(url: string): Promise<SPINEBINARY> {\n                    const response = await settings.ADAPTER.fetch(url);\n\n                    const buffer = await response.arrayBuffer();\n\n                    return buffer as SPINEBINARY;\n                },\n                // #endregion\n\n                // #region Parsing spine data\n                testParse(asset: unknown, options: LoadAsset): Promise<boolean> {\n                    const isJsonSpineModel = checkExtension(options.src, '.json') && isJson(asset);\n                    const isBinarySpineModel = checkExtension(options.src, '.skel') && isBuffer(asset);\n\n                    // From 6.x loader. If the atlas is strictly false we bail\n                    const isMetadataAngry = options.data?.spineAtlas === false;\n\n                    return Promise.resolve((isJsonSpineModel && !isMetadataAngry) || isBinarySpineModel);\n                },\n\n                async parse(asset: SPINEJSON | SPINEBINARY, loadAsset, loader): Promise<ISpineResource<SKD>> {\n                    const fileExt = utils.path.extname(loadAsset.src).toLowerCase();\n                    const fileName = utils.path.basename(loadAsset.src, fileExt);\n                    let basePath = utils.path.dirname(loadAsset.src);\n\n                    if (basePath && basePath.lastIndexOf('/') !== basePath.length - 1) {\n                        basePath += '/';\n                    }\n\n                    const isJsonSpineModel = checkExtension(loadAsset.src, '.json') && isJson(asset);\n                    // const isBinarySpineModel = fileExt === 'slel' && isBuffer(asset);\n\n                    let parser: ISkeletonParser = null;\n                    let dataToParse = asset;\n\n                    if (isJsonSpineModel) {\n                        parser = spineAdapter.createJsonParser();\n                    } else {\n                        parser = spineAdapter.createBinaryParser();\n                        dataToParse = new Uint8Array(asset);\n                    }\n\n                    const metadata = (loadAsset.data || {}) as ISpineMetadata;\n                    const metadataSkeletonScale = metadata?.spineSkeletonScale ?? null;\n\n                    if (metadataSkeletonScale) {\n                        parser.scale = metadataSkeletonScale;\n                    }\n\n                    // if metadataAtlas is a TextureAtlas, use it directly\n                    const metadataAtlas: TextureAtlas = metadata.spineAtlas as TextureAtlas;\n\n                    if (metadataAtlas && metadataAtlas.pages) {\n                        return spineAdapter.parseData(parser, metadataAtlas, dataToParse);\n                    }\n\n                    // if for some odd reason, you dumped the text information of the atlas into the metadata...\n                    const textAtlas = metadata.atlasRawData;\n\n                    if (textAtlas) {\n                        let auxResolve = null;\n                        let auxReject = null;\n                        const atlasPromise = new Promise<TextureAtlas>((resolve, reject) => {\n                            auxResolve = resolve;\n                            auxReject = reject;\n                        });\n                        const atlas = new TextureAtlas(textAtlas, makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject(loader, basePath, metadata.imageMetadata), (newAtlas) => {\n                            if (!newAtlas) {\n                                auxReject('Something went terribly wrong loading a spine .atlas file\\nMost likely your texture failed to load.');\n                            }\n                            auxResolve(atlas);\n                        });\n                        const textureAtlas = await atlasPromise;\n\n                        return spineAdapter.parseData(parser, textureAtlas, dataToParse);\n                    }\n\n                    // Maybe you told us where to find the file? (I sure hope you remembered to add the .atlas extension)\n                    let atlasPath = metadata.spineAtlasFile;\n\n                    // Finally, if no information at all about the atlas, we guess the atlas file name\n                    if (!atlasPath) {\n                        atlasPath = `${basePath + fileName}.atlas`;\n                    }\n\n                    const textureAtlas = await loader.load<TextureAtlas>({ src: atlasPath, data: metadata, alias: metadata.spineAtlasAlias });\n\n                    return spineAdapter.parseData(parser, textureAtlas, dataToParse);\n                },\n\n                // #endregion\n\n                // unload(asset: ISpineResource<SKD>, loadAsset, loader) {\n                // \t???\n                // },\n            },\n        } as AssetExtension<SPINEJSON | SPINEBINARY | ISpineResource<SKD>, ISpineMetadata>;\n\n        extensions.add(spineLoaderExtension);\n\n        return spineLoaderExtension;\n    }\n}\n\n/**\n * The final spineData+spineAtlas object that can be used to create a Spine.\n * @public\n */\nexport interface ISpineResource<SKD extends ISkeletonData> {\n    spineData: SKD;\n    spineAtlas: TextureAtlas;\n}\n\n/**\n * Metadata for loading spine assets\n * @public\n */\nexport interface ISpineMetadata {\n    // Passed directly to Spine's SkeletonJson/BinaryParser\n    spineSkeletonScale?: number;\n    // If you already have a TextureAtlas, you can pass it directly\n    spineAtlas?: Partial<TextureAtlas>;\n    // If you are going to download an .atlas file, you can specify an alias here for cache/future lookup\n    spineAtlasAlias?: string[];\n    // If you want to use a custom .atlas file, you can specify the path here. **It must be a .atlas file or you need your own parser!**\n    spineAtlasFile?: string;\n    // If for some reason, you have the raw text content of an .atlas file, and want to use it dump it here\n    atlasRawData?: string;\n    // If you are hardcore and can write your own loader function to load the textures for the atlas, you can pass it here\n    imageLoader?: (loader: Loader, path: string) => (path: string, callback: (tex: BaseTexture) => any) => any;\n    // If you are downloading an .atlas file, this metadata will go to the Texture loader\n    imageMetadata?: any;\n    // If you already have atlas pages loaded as pixi textures and want to use that to create the atlas, you can pass them here\n    images?: Record<string, Texture | BaseTexture>;\n    // If your spine only uses one atlas page and you have it as a pixi texture, you can pass it here\n    image?: Texture | BaseTexture;\n}\n"],"names":["spineTextureAtlasLoader","ExtensionType","LoaderParserPriority","url","checkExtension","settings","asset","options","isExtensionRight","isString","loader","metadata","basePath","utils","resolve","reject","retPromise","res","rej","retval","resolveCallback","newAtlas","pages","TextureAtlas","line","callback","page","makeSpineTextureAtlasLoaderFunctionFromPixiLoaderObject","atlas","atlasBasePath","imageMetadata","pageName","textureLoadedCallback","texture","e","spine_settings","extensions","isJson","resource","isBuffer","SpineLoaderAbstract","spineAdapter","spineLoaderExtension","_a","isJsonSpineModel","isBinarySpineModel","isMetadataAngry","loadAsset","fileExt","fileName","parser","dataToParse","metadataSkeletonScale","metadataAtlas","textAtlas","auxResolve","auxReject","atlasPromise","textureAtlas","atlasPath"],"mappings":"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"}