UNPKG

assetsm

Version:

Assets Manager. Tilemaps, tilesets, images and audio files loading and managing.

1 lines 13.5 kB
const PROGRESS_EVENT_TYPE={loadstart:"loadstart",progress:"progress",abort:"abort",error:"error",load:"load",timeout:"timeout"},ERROR_MESSAGES={LOADER_NOT_REGISTERED:" loader is not registered.",RECURSION_ERROR:"Too much recursion. Stop iteration.",NOT_CORRECT_METHOD_TYPE:"uploadMethod should be instance of Promise and return upload result value",XML_FILE_EXTENSION_INCORRECT:" AtlasXML file extension is incorrect, only .xml file supported",TILESET_FILE_EXTENSION_INCORRECT:" tileset file extension is not correct, only .tsj, .json, .tsx, .xml files are supported",TILEMAP_FILE_EXTENSION_INCORRECT:" tilemap file extension is not correct, only .tmj, .json, .tmx, .xml files are supported",INPUT_PARAMS_ARE_INCORRECT:" fileKey and url should be provided",ATLAS_IMAGE_LOADING_FAILED:"Error loading atlas image ",TILESET_LOADING_FAILED:"Error loading related tileset ",TILEMAP_LOADING_FAILED:"Error loading tilemap ",AUDIO_LOADING_FAILED:"Error loading audio ",IMAGE_LOADING_FAILED:"Error loading image ",XML_FORMAT_INCORRECT:" XML format is not correct."},FILE_FORMAT={JSON:"JSON",XML:"XML",UNKNOWN:"UNKNOWN"};class Loader{#e;#t;#i=new Map;#r=new Map;constructor(e,t){this.#e=e,this.#t=(e,i,...r)=>{const s=t(e,i,...r);if(s instanceof Promise)return s.then((t=>this.#s(t,e)));throw new TypeError(ERROR_MESSAGES.NOT_CORRECT_METHOD_TYPE)}}#s=(e,t)=>new Promise(((i,r)=>{e||null===e||Warning("AssetsManager: uploadMethod for "+this.#e+" returns incorrect value"),this.#a(t,e),this.#o(t),i()}));#a(e,t){this.#r.set(e,t)}#o(e){this.#i.delete(e)}get filesWaitingForUpload(){return this.#i.size}get loadingQueue(){return this.#i}get uploadMethod(){return this.#t}_addFile=(e,t)=>{this.#i.has(e)&&Warning("AssetsManager: File "+this.#e+" with key "+e+" is already added"),this.#i.set(e,t)};_isFileInQueue=e=>this.#i.has(e);_getFile=e=>this.#r.get(e)}export default class AssetsManager{#l=5;#n=new EventTarget;#u=new Map;#d=0;constructor(){this.registerLoader("Audio",this._loadAudio),this.registerLoader("Image",this._loadImage),this.registerLoader("TileMap",this._loadTileMap),this.registerLoader("TileSet",this._loadTileSet),this.registerLoader("AtlasImageMap",this._loadAtlasImage),this.registerLoader("AtlasXML",this._loadAtlasXml)}get filesWaitingForUpload(){let e=0;return Array.from(this.#u.values()).map((t=>e+=t.filesWaitingForUpload)),e}registerLoader=(e,t=this._defaultUploadMethod)=>{this["add"+e]=(t,i,...r)=>{this.addFile(e,t,i,...r)},this["get"+e]=t=>this.getFile(e,t),this["is"+e+["InQueue"]]=t=>this.isFileInQueue(e,t);const i=this.#u.get(e)||new Loader(e,t);this.#u.set(e,i)};preload(){return this.#h(),new Promise((async(e,t)=>{this.#E().then((()=>{this.#c(),e()})).catch((e=>{t(e)}))}))}#E(e=0){return this.#m().then((()=>{if(0===this.filesWaitingForUpload)return Promise.resolve();if(++e>this.#l){const e=new Error(ERROR_MESSAGES.RECURSION_ERROR);return this.#g(e),Promise.reject(new Error(ERROR_MESSAGES.RECURSION_ERROR))}return this.#E(e)}))}#m(){return new Promise(((e,t)=>{let i=[];Array.from(this.#u.values()).forEach((e=>{Array.from(e.loadingQueue.entries()).forEach((t=>{const r=new Promise(((i,r)=>e.uploadMethod(t[0],...t[1]).then((()=>i()))));i.push(r)}))})),Promise.allSettled(i).then((i=>{for(const e of i)if("rejected"===e.status){const i=e.reason;this.#p(i)?t(i):(Warning("AssetsManager: "+i.message),this.#g(i))}e()}))}))}addEventListener(e,t,...i){PROGRESS_EVENT_TYPE[e]?this.#n.addEventListener(e,t,...i):Warning("AssetsManager: Event type should be one of the ProgressEvent.type")}removeEventListener(e,t,...i){this.#n.removeEventListener(e,t,...i)}_loadAtlasXml=(e,t)=>(this.#_(t),fetch(t).then((e=>e.text())).then((e=>(new window.DOMParser).parseFromString(e,"text/xml"))).then((i=>{const r=i.documentElement||i.activeElement,s=r.attributes.getNamedItem("imagePath"),a=r.children;if(s){const i=this.#R(t);return this.addAtlasImageMap(e,i+s.value,a,i),r}{const t=new Error(e+ERROR_MESSAGES.XML_FORMAT_INCORRECT);return this.#g(t),t}})));_loadAtlasImage=(e,t,i,r="anonymous")=>new Promise(((e,s)=>{const a=new Image,o=new Map,l=document.createElement("canvas"),n=l.getContext("2d");a.crossOrigin=r,a.onload=()=>{const t=[];let r=[];l.width=a.width,l.height=a.height,n.drawImage(a,0,0);for(let e of i){const i=e.attributes,s=i.getNamedItem("name").value,a=s.includes(".")?s.split(".")[0]:s,o=i.getNamedItem("x").value,l=i.getNamedItem("y").value,u=i.getNamedItem("width").value,d=i.getNamedItem("height").value;t.push(createImageBitmap(n.getImageData(o,l,u,d),{premultiplyAlpha:"premultiply"})),r.push(a)}this.#N(),Promise.all(t).then((t=>{t.forEach(((e,t)=>{const i=r[t];o.set(i,e),this.addImage(i,"empty url",e)})),l.remove(),e(o)}))},a.onerror=()=>{const i=new Error(ERROR_MESSAGES.ATLAS_IMAGE_LOADING_FAILED+t);this.#g(i),e(null)},a.src=t}));_loadTileSet=(e,t,i=1,r)=>{const s=this.#b(t),a=r?r+t:t;return s===FILE_FORMAT.JSON?fetch(a).then((e=>e.json())).then((e=>this._processTilesetData(e,r,i,t))).catch((()=>{const e=new Error(ERROR_MESSAGES.TILESET_LOADING_FAILED+t);return this.#g(e),Promise.resolve(null)})):s===FILE_FORMAT.XML?fetch(a).then((e=>e.text())).then((e=>(new window.DOMParser).parseFromString(e,"text/xml"))).then((e=>this._processTilesetXmlData(e.documentElement))).then((e=>this._processTilesetData(e,r,i,t))).catch((()=>{const e=new Error(ERROR_MESSAGES.TILESET_LOADING_FAILED+t);return this.#g(e),Promise.resolve(null)})):Promise.reject(a+ERROR_MESSAGES.TILEMAP_FILE_EXTENSION_INCORRECT)};_processTilesetXmlData=e=>{const t={columns:Number(e.attributes?.columns?.value),name:e.attributes?.name?.value,tilecount:Number(e.attributes?.tilecount?.value),tiledversion:e.attributes?.tiledversion?.value,tileheight:Number(e.attributes?.tileheight?.value),tilewidth:Number(e.attributes?.tilewidth?.value),version:e.attributes?.version?.value,margin:e.attributes?.margin?Number(e.attributes.margin.value):0,spacing:e.attributes?.spacing?Number(e.attributes.margin.value):0,type:e.tagName};return this._processTilesetXmlChildData(t,e.childNodes),t};_processTilesetXmlChildData(e,t){for(let i=0;i<t.length;i++){const r=t[i],s=r.nodeName;if("image"===s)e.image=r?.attributes?.source?.value,e.imagewidth=r?.attributes?.width?Number(r.attributes.width.value):0,e.imageheight=r?.attributes?.height?Number(r.attributes.height.value):0;else if("tileoffset"===s)e.tileoffset={x:Number(r.attributes.x.value),y:Number(r.attributes.y.value)};else if("tile"===s){e.tiles||(e.tiles=[]);const t={id:Number(r.attributes?.id?.value)},i=r.childNodes;for(let e=0;e<i.length;e++){const r=i[e],s=r.nodeName;if("objectgroup"===s){t.objectgroup={type:s},r.attributes?.id&&(t.objectgroup.id=Number(r.attributes?.id?.value)),r.attributes?.draworder&&(t.objectgroup.draworder=r.attributes.draworder.value),r.attributes?.opacity&&(t.objectgroup.opacity=r.attributes.opacity.value),r.attributes?.x&&r.attributes?.y&&(t.objectgroup.x=r.attributes.x.value,t.objectgroup.y=r.attributes.y.value),t.objectgroup.objects=[];const e=r.childNodes;for(let i=0;i<e.length;i++){const r=e[i];if("object"===r.nodeName){const e={id:Number(r.attributes?.id?.value),visible:!r.attributes.visible||"0"!==r.attributes.visible.value,x:Number(r.attributes?.x?.value),y:Number(r.attributes?.y?.value),rotation:r.attributes?.rotation?Number(r.attributes.rotation.value):0};r.attributes?.width&&(e.width=Number(r.attributes.width.value)),r.attributes?.height&&(e.height=Number(r.attributes.height.value));const i=r.childNodes;if(i&&i.length>0)for(let t=0;t<i.length;t++){const r=i[t];if("ellipse"===r.nodeName)e.ellipse=!0;else if("point"===r.nodeName)e.point=!0;else if("polygon"===r.nodeName){const t=r.attributes?.points?.value;if(t&&t.length>0){const i=t.split(" ").map((e=>{const[t,i]=e.split(",");return{x:Number(t),y:Number(i)}}));e.polygon=i}}}t.objectgroup.objects.push(e)}}}else if("animation"===s){t.animation=[];const e=r.childNodes;for(let i=0;i<e.length;i++){const r=e[i];if("frame"===r.nodeName){const e={tileid:Number(r.attributes?.tileid?.value),duration:Number(r.attributes?.duration?.value)};t.animation.push(e)}}}}e.tiles.push(t)}}}_processTilesetData=(e,t,i,r)=>{const{name:s,image:a}=e;return s&&a&&!this.isFileInQueue("Image",s)&&this.addImage(s,t?t+a:a),i&&(e.firstgid=i),r&&(e.source=r),Promise.resolve(e)};_defaultUploadMethod=(e,t)=>fetch(t);_loadTileMap=(e,t,i=!0)=>{let r;if(this.#L(t)===FILE_FORMAT.JSON)r=fetch(t).then((e=>e.json())).then((e=>this._processTileMapData(e,t,i))).catch((e=>(e.message.includes("JSON.parse:")&&(e=new Error(ERROR_MESSAGES.TILEMAP_LOADING_FAILED+t)),this.#g(e),Promise.resolve(null))));else{if(!FILE_FORMAT.XML)return Promise.reject(t+ERROR_MESSAGES.TILEMAP_FILE_EXTENSION_INCORRECT);r=fetch(t).then((e=>e.text())).then((e=>this._processTileMapXML(e))).then((e=>this._processTileMapData(e,t,i))).catch((e=>(this.#g(e),Promise.resolve(null))))}return r};_processTileMapXML=e=>{const t=(new DOMParser).parseFromString(e,"text/xml"),i=t.documentElement,r={type:i.tagName,width:Number(i.attributes?.width?.value),height:Number(i.attributes?.height?.value),infinite:!(!i.attributes.infinite||"1"!==i.attributes.infinite.value),nextlayerid:Number(i.attributes?.nextlayerid?.value),nextobjectid:Number(i.attributes?.nextobjectid?.value),orientation:i.attributes?.orientation?.value,renderorder:i.attributes?.renderorder?.value,tiledversion:i.attributes?.tiledversion?.value,tileheight:Number(i.attributes?.tileheight?.value),tilewidth:Number(i.attributes?.tilewidth?.value),version:i.attributes?.version?.value,tilesets:[],layers:[]},s=t.documentElement.childNodes;for(let e=0;e<s.length;e++){const t=s[e],i=t.nodeName;if("tileset"===i){const e={firstgid:Number(t.attributes?.firstgid?.value)};t.attributes?.source?e.source=t.attributes?.source?.value:(e.columns=Number(t.attributes?.columns?.value),t.attributes?.margin&&(e.margin=Number(t.attributes?.margin?.value)),t.attributes?.spacing&&(e.spacing=t.attributes?.spacing?.value),e.name=t.attributes?.name?.value,e.tilecount=Number(t.attributes?.tilecount?.value),e.tilewidth=Number(t.attributes?.tilewidth?.value),e.tileheight=Number(t.attributes?.tileheight?.value),this._processTilesetXmlChildData(e,t.childNodes)),r.tilesets.push(e)}else if("layer"===i){const e={height:Number(t.attributes?.height?.value),id:Number(t.attributes?.id?.value),name:t.attributes?.name?.value,width:Number(t.attributes?.width?.value),data:t.textContent?t.textContent.trim().split(",").map((e=>Number(e))):null};r.layers.push(e)}}return r};_processTileMapData=(e,t,i)=>{const r=this.#R(t);if(!0===i&&e.tilesets&&e.tilesets.length>0){const t=[];return e.tilesets.forEach(((e,i)=>{const{firstgid:s,source:a}=e;if(a){const e=this._loadTileSet("default-"+s,a,s,r).then((e=>(this.#N(),Promise.resolve(e))));t.push(e)}else{const i=this._processTilesetData(e,r).then((e=>(this.#N(),Promise.resolve(e))));t.push(i)}})),Promise.all(t).then((t=>{for(let i=0;i<t.length;i++){const r=t[i];e.tilesets[i]=r,e.tilesets[i].data=Object.assign({},r)}return Promise.resolve(e)}))}return Promise.resolve(e)};_loadAudio=(e,t)=>new Promise((e=>{const i=new Audio(t);i.addEventListener("loadeddata",(()=>{this.#N(),e(i)})),i.addEventListener("error",(()=>{const i=new Error(ERROR_MESSAGES.AUDIO_LOADING_FAILED+t);this.#g(i),e(null)}))}));_loadImage=(e,t,i,r="anonymous")=>new Promise(((e,s)=>{if(i)e(i);else{const i=new Image;i.crossOrigin=r,i.onload=()=>{createImageBitmap(i,{premultiplyAlpha:"premultiply"}).then((t=>{this.#N(),e(t)}))},i.onerror=()=>{const i=new Error(ERROR_MESSAGES.IMAGE_LOADING_FAILED+t);this.#g(i),e(null)},i.src=t}}));#_(e){e.includes(".xml")||Exception(e+ERROR_MESSAGES.XML_FILE_EXTENSION_INCORRECT)}#b(e){return e.includes(".tsj")||e.includes(".json")?FILE_FORMAT.JSON:e.includes(".tsx")||e.includes(".xml")?FILE_FORMAT.XML:FILE_FORMAT.UNKNOWN}#L(e){return e.includes(".tmj")||e.includes(".json")?FILE_FORMAT.JSON:e.includes(".tmx")||e.includes(".xml")?FILE_FORMAT.XML:FILE_FORMAT.UNKNOWN}#p(e){return e.message.includes(ERROR_MESSAGES.NOT_CORRECT_METHOD_TYPE)||e.message.includes(ERROR_MESSAGES.XML_FILE_EXTENSION_INCORRECT)||e.message.includes(ERROR_MESSAGES.TILESET_FILE_EXTENSION_INCORRECT)||e.message.includes(ERROR_MESSAGES.TILEMAP_FILE_EXTENSION_INCORRECT)||e.message.includes(ERROR_MESSAGES.INPUT_PARAMS_ARE_INCORRECT)||e.message.includes(ERROR_MESSAGES.LOADER_NOT_REGISTERED)}#R(e){let t=e.split("/"),i=t[t.length-1],r="/";return(i.includes(".tmj")||i.includes(".tmx")||i.includes(".xml")||i.includes(".json"))&&(t.pop(),r=t.join("/")+"/"),r}addFile(e,t,i,...r){const s=this.#u.get(e);s?(this.#I(t,i,e),s._addFile(t,[i,...r])):Exception(e+ERROR_MESSAGES.LOADER_NOT_REGISTERED)}isFileInQueue(e,t){const i=this.#u.get(e);if(i)return i._isFileInQueue(t);Exception("Loader for "+e+" is not registered!")}getFile(e,t){const i=this.#u.get(e);if(i)return i._getFile(t);Exception("Loader for "+e+" is not registered!")}#I(e,t,i){const r=ERROR_MESSAGES.INPUT_PARAMS_ARE_INCORRECT;e&&0!==e.trim().length||Exception("add"+i+"()"+r),t&&0!==t.trim().length||Exception("add"+i+"()"+r)}#h(){let e=this.filesWaitingForUpload;this.#n.dispatchEvent(new ProgressEvent(PROGRESS_EVENT_TYPE.loadstart,{total:e}))}#c(){this.#n.dispatchEvent(new ProgressEvent(PROGRESS_EVENT_TYPE.load))}#N(){const e=this.filesWaitingForUpload;this.#d+=1,this.#n.dispatchEvent(new ProgressEvent(PROGRESS_EVENT_TYPE.progress,{lengthComputable:!0,loaded:this.#d,total:e}))}#g(e){Warning("AssetsManger: "+e.message),this.#n.dispatchEvent(new ErrorEvent(PROGRESS_EVENT_TYPE.error,{error:e}))}}function Exception(e){throw new Error(e)}function Warning(e){console.warn(e)}