/** __ ___ ___ ____ ____ ___ /__\ / __)/ __)( ___)(_ _)/ __) /(__)\ \__ \\__ \ )__) )( \__ \ (__)(__)(___/(___/(____) (__) (___/ for SPARC By Mike & Nike This file is part of Sparc by Mike & Nike. Sparc is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License, as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Sparc is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Get your copy of the GNU General Public License at . * @category Core * @subcategory Libraries * @hideconstructor */ class Assets { static defaults = {}; /** * @type {object} * @property {Array} images * @property {Array} css * @property {Array} html * @property {Array} json * @property {Array} fonts * @property {Array} sfx */ static Store = { 'images':{}, 'css':{}, 'html':{}, 'json':{}, 'fonts':{}, 'sfx':{} }; /** * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {Promise} */ static getImage(args){ if( (!args.hasOwnProperty('path')) || (args.path=='') ) var fpath = this.defaults.basePath+'images/'+args.name; else var fpath = (args.path+'/'+args.name).replace(/\/+/g,'/').replace(/http(s)?:\//g, 'http$1://'); let aid = args.id || fpath; if(this.Store.images.hasOwnProperty(aid) && (!args.refresh)) { return(new Promise((resolve) => { resolve(this.Store.images[aid]); }) ); } let node = document.createElement('img'); return(new Promise( (resolve, fail) => { node.onload = (e) => { this.Store.images[aid] = node; resolve(node); } node.onerror= (e) => { console.warn('Could not load IMAGE asset ',aid, fpath); } node.src = fpath+'?'+crypto.randomUUID(); // for img nodes, setting the src is sufficient to start loading, then trigger the onload }) ); } /** * * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {Promise} */ static loadJson(args){ if( (!args.hasOwnProperty('path')) || (args.path=='') ) var fpath = this.defaults.basePath+'json/'+args.name; else var fpath = (args.path+args.name).replace(/\/+/g,'/').replace(/http(s)?:\//g, 'http$1://'); let aid = args.id || fpath; if( (this.Store.json.hasOwnProperty(aid)) && (!args.refresh) ){ return(new Promise((resolve) => { resolve(this.Store.json[aid]); }) ); } return( fetch(fpath+'?'+crypto.randomUUID()) .then(response => response.json()) .then(data => { this.Store.json[aid] = data; return(data); }) .catch(error => { console.warn('Could not load json asset:', aid, fpath, error); }) ); } /** * * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {Promise} */ static loadCss(args){ if( (!args.hasOwnProperty('path')) || (args.path=='') ) var fpath = this.defaults.basePath+'styles/'+args.name; else var fpath = (args.path+args.name).replace(/\/+/g,'/').replace(/http(s)?:\//g, 'http$1://'); let aid = args.id || fpath; let _notInIndex = (aid) => { if( (!app.config.squeeze) || (!app.config.squeeze.packages) ) return(true) for(let dragee of app.config.squeeze.packages) { let ext = dragee.target.toLowerCase().substring(dragee.target.lastIndexOf('.')) if((ext!='.css') || (!dragee.setIndexPage)) continue for(let file of dragee.sources) { if(file == aid) return(false) } } return(true) } if( this.Store.css.hasOwnProperty(aid) && (!args.refresh) ) { return(new Promise((resolve) => { resolve(this.Store.css[aid]); }) ); } let node; if( (this.Store.css.hasOwnProperty(aid)) && (args.refresh) ) node = this.Store.css[aid]; else { node = document.createElement('link'); node.setAttribute('rel','stylesheet'); node.setAttribute('type','text/css'); } return( new Promise( (resolve, fail) => { node.onload = (e) => { this.Store.css[aid] = node; resolve(node); } node.onerror= (e) => { console.warn('Could not load CSS asset ', aid, fpath); fail(node); } node.setAttribute('href',fpath+'?'+crypto.randomUUID()); // for link nodes, setting the href is NOT sufficient to start loading // , then trigger the onload. Must add it to the dom ! document.head.appendChild(node) }) ); } /** * Loads an audio file. * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {Promise} */ static loadSound(args){ if( (!args.hasOwnProperty('path')) || (args.path=='') ) var fpath = this.defaults.basePath+'sfx/'+args.name; else var fpath = (args.path+args.name).replace(/\/+/g,'/').replace(/http(s)?:\//g, 'http$1://'); let aid = args.id || fpath; if( (this.Store.sfx.hasOwnProperty(aid)) && (!args.refresh) ) { return(new Promise((resolve) => { resolve(this.Store.sfx[aid]); }) ); } return(new Promise((resolve, fail) => { this.Store.sfx[aid] = new Audio(); this.Store.sfx[aid].setAttribute('aid', aid); this.Store.sfx[aid].addEventListener('canplaythrough', (e) => resolve(e.target), { 'once':true }); this.Store.sfx[aid].addEventListener('error', (e) => fail(e.target), { 'once':true }); this.Store.sfx[aid].src = fpath+'?'+crypto.randomUUID(); return(this.Store.sfx[aid]); }) ); } /** * Plays a loaded audio, loads it first if needed. * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {Promise} */ static playSound(args){ // Nike: Promise when fulfilled when sfx loaded. // If we want when sfx finished to play then // stick a 'ended' event listener as resolver in a new promise return( this.loadSound(args).then( (snd) => { snd.play(); }) ); } /** * * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {Promise} * @todo check issue with undeclared aid */ static loadFont(args){ // something fishy here => aid not declared let acronym = aid || args.name.substr(0,args.name.lastIndexOf('.')); if( (!args.hasOwnProperty('path')) || (args.path=='') ) var fpath = this.defaults.basePath+'fonts/'+args.name; else var fpath = (args.path+args.name).replace(/\/+/g,'/').replace(/http(s)?:\//g, 'http$1://'); let aid = args.id || fpath; if( (this.Store.fonts.hasOwnProperty(aid)) && (!args.refresh) ) { return(new Promise((resolve) => { resolve([loaded_face, acronym]); }) ); } let ff = new FontFace(acronym,'url('+fpath+'?'+crypto.randomUUID()+')'); return(ff.load() .then(loaded_face => { document.fonts.add(loaded_face); this.Store.fonts[aid] = loaded_face; return([loaded_face, acronym]); }) .catch(error => { console.warn('Could not load FONT asset:' + fpath); [null, acronym] }) ); } /** * Loads an HTML file * @async * @param {*} args * @param {string} args.path * @param {string} args.name * @param {string} [args.id] * @param {boolean} [args.refresh] * @returns {string} */ static loadHtml(args) { if( (!args.hasOwnProperty('path')) || (args.path=='') ) var fpath = this.defaults.basePath+'html/'+args.name; else var fpath = (args.path+args.name).replace(/\/+/g,'/').replace(/http(s)?:\//g, 'http$1://'); let aid = args.id || fpath; if( (this.Store.html.hasOwnProperty(aid)) && (!args.refresh) ) { let html = this.Store.html[aid]; return(new Promise((resolve) => { resolve(html); }) ); } return(fetch(fpath + '?' + crypto.randomUUID()) .then(response => response.text()) .then(html => { this.Store.html[aid] = html; return html; }) .catch(error => { console.warn('Could not load HTML asset:', aid, fpath); return(null); }) ); } } app.LoadedClasses.Assets = Assets;