@armniko/assets
Advanced tools
+314
-1
@@ -1,1 +0,314 @@ | ||
| var t=class{},s=class extends t{load(t,s,e){const o=new Image;o.crossOrigin="Anonymous",o.onload=()=>{s(o)},o.onerror=t=>{e(new Error("string"==typeof t?t:"Failed to load image!"))},o.src=t.url()}},e=class{_total;_loaded;_failed;constructor(t,s,e){this._total=t,this._loaded=s,this._failed=e}total(){return this._total}loaded(){return this._loaded}failed(){return this._failed}isCompleted(){return this.loaded()+this.failed()>=this.total()}percents(){return Math.floor(100/this.total()*(this.loaded()+this.failed()))}},o=/* @__PURE__ */function(t){return t[t.Image=0]="Image",t[t.Font=1]="Font",t[t.Audio=2]="Audio",t[t.Json=3]="Json",t}({}),r=class{_batchAsset;constructor(t){this._batchAsset=t}url(){return"string"==typeof this._batchAsset?this._batchAsset:Object.values(this._batchAsset)[0]}name(){return"string"==typeof this._batchAsset?this.urlLastSegment().split(".")[0]:Object.keys(this._batchAsset)[0]}type(){let t;switch(this.urlLastSegment().split(".").pop()){case"woff2":case"woff":case"ttf":case"otf":t=o.Font;break;case"png":case"webp":case"jpg":case"jpeg":case"svg":case"gif":t=o.Image;break;case"webm":case"mp3":case"ogg":case"m4a":case"aac":case"wav":t=o.Audio;break;case"json":t=o.Json;break;default:t=void 0}return t}urlLastSegment(){return this.url().split("?")[0].split("/").filter(t=>""!==t).pop()||""}},a=class extends t{load(t,s,e){const o=new FontFace(t.name(),"url("+t.url()+")");document.fonts.add(o),o.load().then(()=>{s(t.name())}).catch(t=>{e(t instanceof Error?t:/* @__PURE__ */new Error("Failed to load font!"))})}},n=class extends Error{constructor(t){super(`MediaError ${t.code}`),this.mediaError=t}},i=class extends t{load(t,s,e){const o=new Audio(t.url());o.oncanplaythrough=()=>{s(o)},o.onerror=()=>{e(o.error?new n(o.error):/* @__PURE__ */new Error("Failed to load audio!"))},o.load()}},l=class extends Error{constructor(t){super(`HTTP ${t.status}`),this.response=t}},c=class extends Error{constructor(t,s){super("Response is not valid JSON"),this.response=t,this.body=s}},h=class extends t{load(t,s,e){fetch(t.url()).then(t=>{if(!t.ok)throw new l(t);return t.text().then(s=>{try{return JSON.parse(s)}catch{throw new c(t,s)}})}).then(t=>{s(t)}).catch(t=>{e(t)})}},d=class{_total=0;_loaded=0;_failed=0;_batchAssets=[];_onLoad;_onComplete;_onError;constructor(t,s,e,o){this._batchAssets=t,this._total=this._batchAssets.length,this._onLoad=s,this._onComplete=e,this._onError=o}loadAsset(t,s,e){const o=new r(t),a=this.loader(o);a?a.load(o,t=>{s({name:o.name(),url:o.url(),asset:t,type:o.type()})},t=>e(o.url(),t)):e(o.url(),/* @__PURE__ */new Error("Cant find loader for `"+o.url()+"`"))}progress(){return new e(this._total,this._loaded,this._failed)}loader(t){let e;switch(t.type()){case o.Font:e=new a;break;case o.Image:e=new s;break;case o.Audio:e=new i;break;case o.Json:e=new h}return e}},u=class extends d{fetch(){this._batchAssets.forEach(t=>{this.loadAsset(t,this.onLoadAssetSuccessCallback.bind(this),this.onLoadAssetErrorCallback.bind(this))})}onLoadAssetSuccessCallback(t){this._loaded++;const s=this.progress();this._onLoad(t,s),this.checkAndCompleteBatch(s)}onLoadAssetErrorCallback(t,s){this._failed++;const e=this.progress();this._onError(t,s),this.checkAndCompleteBatch(e)}checkAndCompleteBatch(t){t.isCompleted()&&this._onComplete(t)}},p=class{_store={[o.Font]:{},[o.Image]:{},[o.Audio]:{},[o.Json]:{}};font(t){return this._store[o.Font][t]?.asset}image(t){return this._store[o.Image][t]?.asset}audio(t){return this._store[o.Audio][t]?.asset}json(t){return this._store[o.Json][t]?.asset}asset(t){return this.image(t)??this.audio(t)??this.json(t)??this.font(t)}fetch(t){new u(t.assets(),(s,e)=>{this._store[s.type][s.name]=s,t.loaded(s.name,e)},s=>{t.completed(s)},(s,e)=>{t.error(s,e)}).fetch()}},_=class{constructor(t){this.options=t,t.assets=this.uniqueAssets(t.assets)}assets(){return this.options.assets}loaded(t,s){this.options.onLoad&&this.options.onLoad(t,s)}completed(t){this.options.onComplete&&this.options.onComplete(t)}error(t,s){this.options.onError&&this.options.onError(t,s)}uniqueAssets(t){return t.filter((t,s,e)=>s===e.indexOf(t))}};export{p as Assets,_ as AssetsBatch,e as Progress}; | ||
| //#region src/loaders/loader.ts | ||
| var Loader = class {}; | ||
| //#endregion | ||
| //#region src/loaders/image-loader.ts | ||
| var ImageLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| const image = new Image(); | ||
| image.crossOrigin = "Anonymous"; | ||
| image.onload = () => { | ||
| onSuccess(image); | ||
| }; | ||
| image.onerror = (error) => { | ||
| onError(new Error(typeof error === "string" ? error : "Failed to load image!")); | ||
| }; | ||
| image.src = loadableAsset.url(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/core/progress.ts | ||
| var Progress = class { | ||
| _total; | ||
| _loaded; | ||
| _failed; | ||
| constructor(total, loaded, failed) { | ||
| this._total = total; | ||
| this._loaded = loaded; | ||
| this._failed = failed; | ||
| } | ||
| total() { | ||
| return this._total; | ||
| } | ||
| loaded() { | ||
| return this._loaded; | ||
| } | ||
| failed() { | ||
| return this._failed; | ||
| } | ||
| isCompleted() { | ||
| return this.loaded() + this.failed() >= this.total(); | ||
| } | ||
| percents() { | ||
| return Math.floor(100 / this.total() * (this.loaded() + this.failed())); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/enums.ts | ||
| var AssetType = /* @__PURE__ */ function(AssetType) { | ||
| AssetType[AssetType["Image"] = 0] = "Image"; | ||
| AssetType[AssetType["Font"] = 1] = "Font"; | ||
| AssetType[AssetType["Audio"] = 2] = "Audio"; | ||
| AssetType[AssetType["Json"] = 3] = "Json"; | ||
| return AssetType; | ||
| }({}); | ||
| //#endregion | ||
| //#region src/core/loadable-asset.ts | ||
| var LoadableAsset = class { | ||
| _batchAsset; | ||
| constructor(batchAsset) { | ||
| this._batchAsset = batchAsset; | ||
| } | ||
| url() { | ||
| return typeof this._batchAsset === "string" ? this._batchAsset : Object.values(this._batchAsset)[0]; | ||
| } | ||
| name() { | ||
| return typeof this._batchAsset === "string" ? this.urlLastSegment().split(".")[0] : Object.keys(this._batchAsset)[0]; | ||
| } | ||
| type() { | ||
| const extension = this.urlLastSegment().split(".").pop(); | ||
| let result; | ||
| switch (extension) { | ||
| case "woff2": | ||
| case "woff": | ||
| case "ttf": | ||
| case "otf": | ||
| result = AssetType.Font; | ||
| break; | ||
| case "png": | ||
| case "webp": | ||
| case "jpg": | ||
| case "jpeg": | ||
| case "svg": | ||
| case "gif": | ||
| result = AssetType.Image; | ||
| break; | ||
| case "webm": | ||
| case "mp3": | ||
| case "ogg": | ||
| case "m4a": | ||
| case "aac": | ||
| case "wav": | ||
| result = AssetType.Audio; | ||
| break; | ||
| case "json": | ||
| result = AssetType.Json; | ||
| break; | ||
| default: | ||
| result = void 0; | ||
| break; | ||
| } | ||
| return result; | ||
| } | ||
| urlLastSegment() { | ||
| return this.url().split("?")[0].split("/").filter((segment) => segment !== "").pop() || ""; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/loaders/font-loader.ts | ||
| var FontLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| const fontFace = new FontFace(loadableAsset.name(), "url(" + loadableAsset.url() + ")"); | ||
| document.fonts.add(fontFace); | ||
| fontFace.load().then(() => { | ||
| onSuccess(loadableAsset.name()); | ||
| }).catch((error) => { | ||
| onError(error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to load font!")); | ||
| }); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/libraries/media-load-error.ts | ||
| var MediaLoadError = class extends Error { | ||
| constructor(mediaError) { | ||
| super(`MediaError ${mediaError.code}`); | ||
| this.mediaError = mediaError; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/loaders/audio-loader.ts | ||
| var AudioLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| const audio = new Audio(loadableAsset.url()); | ||
| audio.oncanplaythrough = () => { | ||
| onSuccess(audio); | ||
| }; | ||
| audio.onerror = () => { | ||
| onError(audio.error ? new MediaLoadError(audio.error) : /* @__PURE__ */ new Error("Failed to load audio!")); | ||
| }; | ||
| audio.load(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/libraries/http-error.ts | ||
| var HttpError = class extends Error { | ||
| constructor(response) { | ||
| super(`HTTP ${response.status}`); | ||
| this.response = response; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/libraries/invalid-json-error.ts | ||
| var InvalidJsonError = class extends Error { | ||
| constructor(response, body) { | ||
| super("Response is not valid JSON"); | ||
| this.response = response; | ||
| this.body = body; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/loaders/json-loader.ts | ||
| var JsonLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| fetch(loadableAsset.url()).then((response) => { | ||
| if (!response.ok) throw new HttpError(response); | ||
| return response.text().then((text) => { | ||
| try { | ||
| return JSON.parse(text); | ||
| } catch { | ||
| throw new InvalidJsonError(response, text); | ||
| } | ||
| }); | ||
| }).then((json) => { | ||
| onSuccess(json); | ||
| }).catch((error) => { | ||
| onError(error); | ||
| }); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/fetchers/fetcher.ts | ||
| var Fetcher = class { | ||
| _total = 0; | ||
| _loaded = 0; | ||
| _failed = 0; | ||
| _batchAssets = []; | ||
| _onLoad; | ||
| _onComplete; | ||
| _onError; | ||
| constructor(batchAssets, onLoad, onComplete, onError) { | ||
| this._batchAssets = batchAssets; | ||
| this._total = this._batchAssets.length; | ||
| this._onLoad = onLoad; | ||
| this._onComplete = onComplete; | ||
| this._onError = onError; | ||
| } | ||
| loadAsset(batchAsset, onSuccess, onError) { | ||
| const loadableAsset = new LoadableAsset(batchAsset); | ||
| const loader = this.loader(loadableAsset); | ||
| if (loader) loader.load(loadableAsset, (asset) => { | ||
| onSuccess({ | ||
| name: loadableAsset.name(), | ||
| url: loadableAsset.url(), | ||
| asset, | ||
| type: loadableAsset.type() | ||
| }); | ||
| }, (error) => onError(loadableAsset.url(), error)); | ||
| else onError(loadableAsset.url(), /* @__PURE__ */ new Error("Cant find loader for `" + loadableAsset.url() + "`")); | ||
| } | ||
| progress() { | ||
| return new Progress(this._total, this._loaded, this._failed); | ||
| } | ||
| loader(loadableAsset) { | ||
| let loader; | ||
| switch (loadableAsset.type()) { | ||
| case AssetType.Font: | ||
| loader = new FontLoader(); | ||
| break; | ||
| case AssetType.Image: | ||
| loader = new ImageLoader(); | ||
| break; | ||
| case AssetType.Audio: | ||
| loader = new AudioLoader(); | ||
| break; | ||
| case AssetType.Json: | ||
| loader = new JsonLoader(); | ||
| break; | ||
| } | ||
| return loader; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/fetchers/concurrent-fetcher.ts | ||
| var ConcurrentFetcher = class extends Fetcher { | ||
| fetch() { | ||
| this._batchAssets.forEach((batchAsset) => { | ||
| this.loadAsset(batchAsset, this.onLoadAssetSuccessCallback.bind(this), this.onLoadAssetErrorCallback.bind(this)); | ||
| }); | ||
| } | ||
| onLoadAssetSuccessCallback(assetRecord) { | ||
| this._loaded++; | ||
| const progress = this.progress(); | ||
| this._onLoad(assetRecord, progress); | ||
| this.checkAndCompleteBatch(progress); | ||
| } | ||
| onLoadAssetErrorCallback(url, error) { | ||
| this._failed++; | ||
| const progress = this.progress(); | ||
| this._onError(url, error); | ||
| this.checkAndCompleteBatch(progress); | ||
| } | ||
| checkAndCompleteBatch(progress) { | ||
| if (progress.isCompleted()) this._onComplete(progress); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/core/assets.ts | ||
| var Assets = class { | ||
| _store = { | ||
| [AssetType.Font]: {}, | ||
| [AssetType.Image]: {}, | ||
| [AssetType.Audio]: {}, | ||
| [AssetType.Json]: {} | ||
| }; | ||
| font(name) { | ||
| return this._store[AssetType.Font][name]?.asset; | ||
| } | ||
| image(name) { | ||
| return this._store[AssetType.Image][name]?.asset; | ||
| } | ||
| audio(name) { | ||
| return this._store[AssetType.Audio][name]?.asset; | ||
| } | ||
| json(name) { | ||
| return this._store[AssetType.Json][name]?.asset; | ||
| } | ||
| asset(name) { | ||
| return this.image(name) ?? this.audio(name) ?? this.json(name) ?? this.font(name); | ||
| } | ||
| fetch(batch) { | ||
| new ConcurrentFetcher(batch.assets(), (assetRecord, progress) => { | ||
| const store = this._store[assetRecord.type]; | ||
| store[assetRecord.name] = assetRecord; | ||
| batch.loaded(assetRecord.name, progress); | ||
| }, (progress) => { | ||
| batch.completed(progress); | ||
| }, (url, error) => { | ||
| batch.error(url, error); | ||
| }).fetch(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/core/assets-batch.ts | ||
| var AssetsBatch = class { | ||
| constructor(options) { | ||
| this.options = options; | ||
| options.assets = this.uniqueAssets(options.assets); | ||
| } | ||
| assets() { | ||
| return this.options.assets; | ||
| } | ||
| loaded(name, progress) { | ||
| if (this.options.onLoad) this.options.onLoad(name, progress); | ||
| } | ||
| completed(progress) { | ||
| if (this.options.onComplete) this.options.onComplete(progress); | ||
| } | ||
| error(url, error) { | ||
| if (this.options.onError) this.options.onError(url, error); | ||
| } | ||
| uniqueAssets(array) { | ||
| return array.filter((item, index, self) => index === self.indexOf(item)); | ||
| } | ||
| }; | ||
| //#endregion | ||
| export { Assets, AssetsBatch, Progress }; |
+321
-1
@@ -1,1 +0,321 @@ | ||
| !function(t,s){"object"==typeof exports&&"undefined"!=typeof module?s(exports):"function"==typeof define&&define.amd?define(["exports"],s):s((t="undefined"!=typeof globalThis?globalThis:t||self).Assets={})}(this,function(t){Object.defineProperty(t,Symbol.toStringTag,{value:"Module"});var s=class{},e=class extends s{load(t,s,e){const o=new Image;o.crossOrigin="Anonymous",o.onload=()=>{s(o)},o.onerror=t=>{e(new Error("string"==typeof t?t:"Failed to load image!"))},o.src=t.url()}},o=class{_total;_loaded;_failed;constructor(t,s,e){this._total=t,this._loaded=s,this._failed=e}total(){return this._total}loaded(){return this._loaded}failed(){return this._failed}isCompleted(){return this.loaded()+this.failed()>=this.total()}percents(){return Math.floor(100/this.total()*(this.loaded()+this.failed()))}},r=function(t){return t[t.Image=0]="Image",t[t.Font=1]="Font",t[t.Audio=2]="Audio",t[t.Json=3]="Json",t}({}),a=class{_batchAsset;constructor(t){this._batchAsset=t}url(){return"string"==typeof this._batchAsset?this._batchAsset:Object.values(this._batchAsset)[0]}name(){return"string"==typeof this._batchAsset?this.urlLastSegment().split(".")[0]:Object.keys(this._batchAsset)[0]}type(){let t;switch(this.urlLastSegment().split(".").pop()){case"woff2":case"woff":case"ttf":case"otf":t=r.Font;break;case"png":case"webp":case"jpg":case"jpeg":case"svg":case"gif":t=r.Image;break;case"webm":case"mp3":case"ogg":case"m4a":case"aac":case"wav":t=r.Audio;break;case"json":t=r.Json;break;default:t=void 0}return t}urlLastSegment(){return this.url().split("?")[0].split("/").filter(t=>""!==t).pop()||""}},n=class extends s{load(t,s,e){const o=new FontFace(t.name(),"url("+t.url()+")");document.fonts.add(o),o.load().then(()=>{s(t.name())}).catch(t=>{e(t instanceof Error?t:new Error("Failed to load font!"))})}},i=class extends Error{constructor(t){super(`MediaError ${t.code}`),this.mediaError=t}},l=class extends s{load(t,s,e){const o=new Audio(t.url());o.oncanplaythrough=()=>{s(o)},o.onerror=()=>{e(o.error?new i(o.error):new Error("Failed to load audio!"))},o.load()}},c=class extends Error{constructor(t){super(`HTTP ${t.status}`),this.response=t}},d=class extends Error{constructor(t,s){super("Response is not valid JSON"),this.response=t,this.body=s}},h=class extends s{load(t,s,e){fetch(t.url()).then(t=>{if(!t.ok)throw new c(t);return t.text().then(s=>{try{return JSON.parse(s)}catch{throw new d(t,s)}})}).then(t=>{s(t)}).catch(t=>{e(t)})}},u=class{_total=0;_loaded=0;_failed=0;_batchAssets=[];_onLoad;_onComplete;_onError;constructor(t,s,e,o){this._batchAssets=t,this._total=this._batchAssets.length,this._onLoad=s,this._onComplete=e,this._onError=o}loadAsset(t,s,e){const o=new a(t),r=this.loader(o);r?r.load(o,t=>{s({name:o.name(),url:o.url(),asset:t,type:o.type()})},t=>e(o.url(),t)):e(o.url(),new Error("Cant find loader for `"+o.url()+"`"))}progress(){return new o(this._total,this._loaded,this._failed)}loader(t){let s;switch(t.type()){case r.Font:s=new n;break;case r.Image:s=new e;break;case r.Audio:s=new l;break;case r.Json:s=new h}return s}},p=class extends u{fetch(){this._batchAssets.forEach(t=>{this.loadAsset(t,this.onLoadAssetSuccessCallback.bind(this),this.onLoadAssetErrorCallback.bind(this))})}onLoadAssetSuccessCallback(t){this._loaded++;const s=this.progress();this._onLoad(t,s),this.checkAndCompleteBatch(s)}onLoadAssetErrorCallback(t,s){this._failed++;const e=this.progress();this._onError(t,s),this.checkAndCompleteBatch(e)}checkAndCompleteBatch(t){t.isCompleted()&&this._onComplete(t)}};t.Assets=class{_store={[r.Font]:{},[r.Image]:{},[r.Audio]:{},[r.Json]:{}};font(t){return this._store[r.Font][t]?.asset}image(t){return this._store[r.Image][t]?.asset}audio(t){return this._store[r.Audio][t]?.asset}json(t){return this._store[r.Json][t]?.asset}asset(t){return this.image(t)??this.audio(t)??this.json(t)??this.font(t)}fetch(t){new p(t.assets(),(s,e)=>{this._store[s.type][s.name]=s,t.loaded(s.name,e)},s=>{t.completed(s)},(s,e)=>{t.error(s,e)}).fetch()}},t.AssetsBatch=class{constructor(t){this.options=t,t.assets=this.uniqueAssets(t.assets)}assets(){return this.options.assets}loaded(t,s){this.options.onLoad&&this.options.onLoad(t,s)}completed(t){this.options.onComplete&&this.options.onComplete(t)}error(t,s){this.options.onError&&this.options.onError(t,s)}uniqueAssets(t){return t.filter((t,s,e)=>s===e.indexOf(t))}},t.Progress=o}); | ||
| (function(global, factory) { | ||
| typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.Assets = {})); | ||
| })(this, function(exports) { | ||
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | ||
| //#region src/loaders/loader.ts | ||
| var Loader = class {}; | ||
| //#endregion | ||
| //#region src/loaders/image-loader.ts | ||
| var ImageLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| const image = new Image(); | ||
| image.crossOrigin = "Anonymous"; | ||
| image.onload = () => { | ||
| onSuccess(image); | ||
| }; | ||
| image.onerror = (error) => { | ||
| onError(new Error(typeof error === "string" ? error : "Failed to load image!")); | ||
| }; | ||
| image.src = loadableAsset.url(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/core/progress.ts | ||
| var Progress = class { | ||
| _total; | ||
| _loaded; | ||
| _failed; | ||
| constructor(total, loaded, failed) { | ||
| this._total = total; | ||
| this._loaded = loaded; | ||
| this._failed = failed; | ||
| } | ||
| total() { | ||
| return this._total; | ||
| } | ||
| loaded() { | ||
| return this._loaded; | ||
| } | ||
| failed() { | ||
| return this._failed; | ||
| } | ||
| isCompleted() { | ||
| return this.loaded() + this.failed() >= this.total(); | ||
| } | ||
| percents() { | ||
| return Math.floor(100 / this.total() * (this.loaded() + this.failed())); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/enums.ts | ||
| var AssetType = /* @__PURE__ */ function(AssetType) { | ||
| AssetType[AssetType["Image"] = 0] = "Image"; | ||
| AssetType[AssetType["Font"] = 1] = "Font"; | ||
| AssetType[AssetType["Audio"] = 2] = "Audio"; | ||
| AssetType[AssetType["Json"] = 3] = "Json"; | ||
| return AssetType; | ||
| }({}); | ||
| //#endregion | ||
| //#region src/core/loadable-asset.ts | ||
| var LoadableAsset = class { | ||
| _batchAsset; | ||
| constructor(batchAsset) { | ||
| this._batchAsset = batchAsset; | ||
| } | ||
| url() { | ||
| return typeof this._batchAsset === "string" ? this._batchAsset : Object.values(this._batchAsset)[0]; | ||
| } | ||
| name() { | ||
| return typeof this._batchAsset === "string" ? this.urlLastSegment().split(".")[0] : Object.keys(this._batchAsset)[0]; | ||
| } | ||
| type() { | ||
| const extension = this.urlLastSegment().split(".").pop(); | ||
| let result; | ||
| switch (extension) { | ||
| case "woff2": | ||
| case "woff": | ||
| case "ttf": | ||
| case "otf": | ||
| result = AssetType.Font; | ||
| break; | ||
| case "png": | ||
| case "webp": | ||
| case "jpg": | ||
| case "jpeg": | ||
| case "svg": | ||
| case "gif": | ||
| result = AssetType.Image; | ||
| break; | ||
| case "webm": | ||
| case "mp3": | ||
| case "ogg": | ||
| case "m4a": | ||
| case "aac": | ||
| case "wav": | ||
| result = AssetType.Audio; | ||
| break; | ||
| case "json": | ||
| result = AssetType.Json; | ||
| break; | ||
| default: | ||
| result = void 0; | ||
| break; | ||
| } | ||
| return result; | ||
| } | ||
| urlLastSegment() { | ||
| return this.url().split("?")[0].split("/").filter((segment) => segment !== "").pop() || ""; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/loaders/font-loader.ts | ||
| var FontLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| const fontFace = new FontFace(loadableAsset.name(), "url(" + loadableAsset.url() + ")"); | ||
| document.fonts.add(fontFace); | ||
| fontFace.load().then(() => { | ||
| onSuccess(loadableAsset.name()); | ||
| }).catch((error) => { | ||
| onError(error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to load font!")); | ||
| }); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/libraries/media-load-error.ts | ||
| var MediaLoadError = class extends Error { | ||
| constructor(mediaError) { | ||
| super(`MediaError ${mediaError.code}`); | ||
| this.mediaError = mediaError; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/loaders/audio-loader.ts | ||
| var AudioLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| const audio = new Audio(loadableAsset.url()); | ||
| audio.oncanplaythrough = () => { | ||
| onSuccess(audio); | ||
| }; | ||
| audio.onerror = () => { | ||
| onError(audio.error ? new MediaLoadError(audio.error) : /* @__PURE__ */ new Error("Failed to load audio!")); | ||
| }; | ||
| audio.load(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/libraries/http-error.ts | ||
| var HttpError = class extends Error { | ||
| constructor(response) { | ||
| super(`HTTP ${response.status}`); | ||
| this.response = response; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/libraries/invalid-json-error.ts | ||
| var InvalidJsonError = class extends Error { | ||
| constructor(response, body) { | ||
| super("Response is not valid JSON"); | ||
| this.response = response; | ||
| this.body = body; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/loaders/json-loader.ts | ||
| var JsonLoader = class extends Loader { | ||
| load(loadableAsset, onSuccess, onError) { | ||
| fetch(loadableAsset.url()).then((response) => { | ||
| if (!response.ok) throw new HttpError(response); | ||
| return response.text().then((text) => { | ||
| try { | ||
| return JSON.parse(text); | ||
| } catch { | ||
| throw new InvalidJsonError(response, text); | ||
| } | ||
| }); | ||
| }).then((json) => { | ||
| onSuccess(json); | ||
| }).catch((error) => { | ||
| onError(error); | ||
| }); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/fetchers/fetcher.ts | ||
| var Fetcher = class { | ||
| _total = 0; | ||
| _loaded = 0; | ||
| _failed = 0; | ||
| _batchAssets = []; | ||
| _onLoad; | ||
| _onComplete; | ||
| _onError; | ||
| constructor(batchAssets, onLoad, onComplete, onError) { | ||
| this._batchAssets = batchAssets; | ||
| this._total = this._batchAssets.length; | ||
| this._onLoad = onLoad; | ||
| this._onComplete = onComplete; | ||
| this._onError = onError; | ||
| } | ||
| loadAsset(batchAsset, onSuccess, onError) { | ||
| const loadableAsset = new LoadableAsset(batchAsset); | ||
| const loader = this.loader(loadableAsset); | ||
| if (loader) loader.load(loadableAsset, (asset) => { | ||
| onSuccess({ | ||
| name: loadableAsset.name(), | ||
| url: loadableAsset.url(), | ||
| asset, | ||
| type: loadableAsset.type() | ||
| }); | ||
| }, (error) => onError(loadableAsset.url(), error)); | ||
| else onError(loadableAsset.url(), /* @__PURE__ */ new Error("Cant find loader for `" + loadableAsset.url() + "`")); | ||
| } | ||
| progress() { | ||
| return new Progress(this._total, this._loaded, this._failed); | ||
| } | ||
| loader(loadableAsset) { | ||
| let loader; | ||
| switch (loadableAsset.type()) { | ||
| case AssetType.Font: | ||
| loader = new FontLoader(); | ||
| break; | ||
| case AssetType.Image: | ||
| loader = new ImageLoader(); | ||
| break; | ||
| case AssetType.Audio: | ||
| loader = new AudioLoader(); | ||
| break; | ||
| case AssetType.Json: | ||
| loader = new JsonLoader(); | ||
| break; | ||
| } | ||
| return loader; | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/fetchers/concurrent-fetcher.ts | ||
| var ConcurrentFetcher = class extends Fetcher { | ||
| fetch() { | ||
| this._batchAssets.forEach((batchAsset) => { | ||
| this.loadAsset(batchAsset, this.onLoadAssetSuccessCallback.bind(this), this.onLoadAssetErrorCallback.bind(this)); | ||
| }); | ||
| } | ||
| onLoadAssetSuccessCallback(assetRecord) { | ||
| this._loaded++; | ||
| const progress = this.progress(); | ||
| this._onLoad(assetRecord, progress); | ||
| this.checkAndCompleteBatch(progress); | ||
| } | ||
| onLoadAssetErrorCallback(url, error) { | ||
| this._failed++; | ||
| const progress = this.progress(); | ||
| this._onError(url, error); | ||
| this.checkAndCompleteBatch(progress); | ||
| } | ||
| checkAndCompleteBatch(progress) { | ||
| if (progress.isCompleted()) this._onComplete(progress); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/core/assets.ts | ||
| var Assets = class { | ||
| _store = { | ||
| [AssetType.Font]: {}, | ||
| [AssetType.Image]: {}, | ||
| [AssetType.Audio]: {}, | ||
| [AssetType.Json]: {} | ||
| }; | ||
| font(name) { | ||
| return this._store[AssetType.Font][name]?.asset; | ||
| } | ||
| image(name) { | ||
| return this._store[AssetType.Image][name]?.asset; | ||
| } | ||
| audio(name) { | ||
| return this._store[AssetType.Audio][name]?.asset; | ||
| } | ||
| json(name) { | ||
| return this._store[AssetType.Json][name]?.asset; | ||
| } | ||
| asset(name) { | ||
| return this.image(name) ?? this.audio(name) ?? this.json(name) ?? this.font(name); | ||
| } | ||
| fetch(batch) { | ||
| new ConcurrentFetcher(batch.assets(), (assetRecord, progress) => { | ||
| const store = this._store[assetRecord.type]; | ||
| store[assetRecord.name] = assetRecord; | ||
| batch.loaded(assetRecord.name, progress); | ||
| }, (progress) => { | ||
| batch.completed(progress); | ||
| }, (url, error) => { | ||
| batch.error(url, error); | ||
| }).fetch(); | ||
| } | ||
| }; | ||
| //#endregion | ||
| //#region src/core/assets-batch.ts | ||
| var AssetsBatch = class { | ||
| constructor(options) { | ||
| this.options = options; | ||
| options.assets = this.uniqueAssets(options.assets); | ||
| } | ||
| assets() { | ||
| return this.options.assets; | ||
| } | ||
| loaded(name, progress) { | ||
| if (this.options.onLoad) this.options.onLoad(name, progress); | ||
| } | ||
| completed(progress) { | ||
| if (this.options.onComplete) this.options.onComplete(progress); | ||
| } | ||
| error(url, error) { | ||
| if (this.options.onError) this.options.onError(url, error); | ||
| } | ||
| uniqueAssets(array) { | ||
| return array.filter((item, index, self) => index === self.indexOf(item)); | ||
| } | ||
| }; | ||
| //#endregion | ||
| exports.Assets = Assets; | ||
| exports.AssetsBatch = AssetsBatch; | ||
| exports.Progress = Progress; | ||
| }); |
+17
-8
| { | ||
| "name": "@armniko/assets", | ||
| "version": "1.4.1", | ||
| "version": "1.5.0", | ||
| "description": "A tiny, zero-dependency JavaScript/TypeScript library for fetching and managing assets with in-memory caching and automatic font registration for canvas and dynamic text rendering.", | ||
@@ -27,6 +27,16 @@ "author": "Armīns Nikolajevs <armins.nikolajevs@gmail.com>", | ||
| ], | ||
| "type": "module", | ||
| "sideEffects": false, | ||
| "main": "./dist/index.umd.js", | ||
| "module": "./dist/index.esm.js", | ||
| "typings": "./dist/index.d.ts", | ||
| "type": "module", | ||
| "types": "./dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/index.d.ts", | ||
| "import": "./dist/index.esm.js", | ||
| "require": "./dist/index.umd.js", | ||
| "default": "./dist/index.umd.js" | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "files": [ | ||
@@ -45,3 +55,3 @@ "dist" | ||
| "@eslint/js": "^10.0.1", | ||
| "@vitest/coverage-v8": "^4.1.4", | ||
| "@vitest/coverage-v8": "^4.1.5", | ||
| "eslint": "^10.2.1", | ||
@@ -52,9 +62,8 @@ "eslint-config-prettier": "^10.1.8", | ||
| "prettier": "^3.8.3", | ||
| "terser": "^5.46.1", | ||
| "typescript": "^6.0.3", | ||
| "typescript-eslint": "^8.58.2", | ||
| "vite": "^8.0.8", | ||
| "typescript-eslint": "^8.59.0", | ||
| "vite": "^8.0.10", | ||
| "vite-plugin-dts": "^4.5.4", | ||
| "vitest": "^4.1.4" | ||
| "vitest": "^4.1.5" | ||
| } | ||
| } |
+14
-5
@@ -8,3 +8,4 @@ # Assets | ||
| A tiny, zero-dependency JavaScript/TypeScript library for fetching and managing assets with in-memory caching and automatic font registration for canvas and dynamic text rendering. | ||
| A tiny, zero-dependency JavaScript/TypeScript library for fetching and managing assets with in-memory caching and | ||
| automatic font registration for canvas and dynamic text rendering. | ||
@@ -23,3 +24,3 @@ **Supported formats:** | ||
| ``` | ||
| ```bash | ||
| npm install @armniko/assets | ||
@@ -39,3 +40,3 @@ ``` | ||
| onLoad: (name: string, progress: Progress): void => { | ||
| console.log('Loaded: ' + name, progress.percents()); | ||
| console.log(`Loaded ${name}: ${progress.percents()}%`); | ||
| }, | ||
@@ -58,5 +59,13 @@ onComplete: (): void => { | ||
| <tr> | ||
| <td>v1.5.0</td> | ||
| <td> | ||
| Removed minification of build.<br> | ||
| Added <code>exports</code> field for proper module resolution and types.<br> | ||
| Marked package as side-effect free. | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <td>v1.4.1</td> | ||
| <td> | ||
| Update readme. | ||
| Updated readme. | ||
| </td> | ||
@@ -89,3 +98,3 @@ </tr> | ||
| <td> | ||
| Precompile UMD and ESM. | ||
| Precompiled UMD and ESM. | ||
| </td> | ||
@@ -92,0 +101,0 @@ </tr> |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
28044
44.88%12
-7.69%779
440.97%1
-66.67%104
9.47%12
9.09%