export default class Spot { constructor(asGlobals) { this.consts = asGlobals.consts; this.consts.hash_sep = '-'; this.consts.title = 'Spotty'; this.consts.default_page = 'project'; this.consts.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || this.consts.default_timezone; this.pages = {}; //Variables & constants from php this.vars('tmp', 'object'); this.vars('page', 'string'); $.each(asGlobals.vars, (sKey, oValue) => {this.vars(sKey, oValue)}); //page elem this.elem = {}; } /* Initialization */ init() { this.elem.container = $('#container'); this.elem.main = $('#main'); //On Key down $('html').on('keydown', (oEvent) => {this.onKeydown(oEvent);}); //on window resize $(window).on('resize', () => {this.onResize();}); //Hash management $(window) .on('hashchange', () => {this.onHashChange();}) .trigger('hashchange'); } /* Variable Management */ vars(oVarName, oValue) { var asVarName = (typeof oVarName == 'object')?oVarName:[oVarName]; //Set, name & type / default value (init) if(typeof oValue !== 'undefined') setElem(this.vars, copyArray(asVarName), oValue); //Get, only name parameter return getElem(this.vars, asVarName); } tmp(sVarName, oValue) { var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName]; asVarName.unshift('tmp'); return this.vars(asVarName, oValue); } /* Interface with server */ get(sAction, fOnSuccess, oVars, fOnError, fonProgress) { oVars = oVars || {}; fOnError = fOnError || function(sError) {console.log(sError);}; fonProgress = fonProgress || function(sState){}; fonProgress('start'); oVars['a'] = sAction; oVars['t'] = this.consts.timezone; return $.ajax( { url: this.consts.process_page, data: oVars, dataType: 'json' }) .done((oData) => { fonProgress('done'); if(oData.desc.substr(0, this.consts.lang_prefix.length)==this.consts.lang_prefix) oData.desc = this.lang(oData.desc.substr(5)); if(oData.result==this.consts.error) fOnError(oData.desc); else if(fOnSuccess) fOnSuccess(oData.data, oData.desc); }) .fail((jqXHR, textStatus, errorThrown) => { fonProgress('fail'); fOnError(textStatus+' '+errorThrown); }); } async get2(sAction, oVars, bLoading) { oVars = oVars || {}; oVars['a'] = sAction; oVars['t'] = this.consts.timezone; bLoading = true; let oUrl = new URL(this.consts.server+this.consts.process_page); oUrl.search = new URLSearchParams(oVars).toString(); try { let oUrl = new URL(this.consts.server+this.consts.process_page); oUrl.search = new URLSearchParams(oVars).toString(); const oRequest = await fetch(oUrl, {method: 'GET', /*body: JSON.stringify(oVars),*/ headers: {"Content-Type": "application/json"}}); if(!oRequest.ok) { bLoading = false; throw new Error('Error HTTP '+oRequest.status+': '+oRequest.statusText); } else { let oResponse = await oRequest.json(); bLoading = false; if(oResponse.desc.substr(0, this.consts.lang_prefix.length)==this.consts.lang_prefix) oResponse.desc = this.lang(oData.desc.substr(this.consts.lang_prefix.length)); if(oResponse.result == this.consts.error) return Promise.reject(oResponse.desc); else return Promise.resolve(oResponse.data, oResponse.desc); } } catch(oError) { bLoading = false; throw oError; } } lang(sKey, asParams) { asParams = asParams || []; if(typeof asParams == 'string') asParams = [asParams]; var sLang = ''; if(sKey in this.consts.lang) { sLang = this.consts.lang[sKey]; for(let i in asParams) sLang = sLang.replace('$'+i, asParams[i]); } else { console.log('missing translation: '+sKey); sLang = sKey; } return sLang; } isMobile() { return $('#mobile').is(':visible'); } /* Page Switch - Trigger & Event catching */ onHashChange() { var asHash = this.getHash(); if(asHash.hash !='' && asHash.page != '') this.switchPage(asHash); //page switching else if(this.vars('page')=='') this.setHash(this.consts.default_page); //first page } getHash() { var sHash = this.hash(); var asHash = sHash.split(this.consts.hash_sep); var sPage = asHash.shift() || ''; return {hash:sHash, page:sPage, items:asHash}; } setHash(sPage, asItems, bReboot) { bReboot = bReboot || false; sPage = sPage || ''; asItems = asItems || []; if(typeof asItems == 'string') asItems = [asItems]; if(sPage != '') { var sItems = (asItems.length > 0)?this.consts.hash_sep+asItems.join(this.consts.hash_sep):''; this.hash(sPage+sItems, bReboot); } } hash(hash, bReboot) { bReboot = bReboot || false; if(!hash) return window.location.hash.slice(1); else window.location.hash = '#'+hash; if(bReboot) location.reload(); } updateHash(sType, iId) { sType = sType || ''; iId = iId || 0; var asHash = this.getHash(); if(iId) this.setHash(asHash.page, [asHash.items[0], sType, iId]); } flushHash(asTypes) { asTypes = asTypes || []; var asHash = this.getHash(); if(asHash.items.length > 1 && (asTypes.length == 0 || asTypes.indexOf(asHash.items[1]) != -1)) this.setHash(asHash.page, [asHash.items[0]]); } /* Page Events */ pageInit(asHash) { let sPage = this.vars('page'); if(this.pages[sPage].pageInit) this.pages[sPage].pageInit(asHash); else console.log('no init for the page: '+asHash.page); } onSamePageMove(asHash) { let sPage = this.vars('page'); return (this.pages[sPage] && this.pages[sPage].onSamePageMove)?this.pages[sPage].onSamePageMove(asHash):false; } onQuitPage() { let sPage = this.vars('page'); return (this.pages[sPage] && this.pages[sPage].onQuitPage)?this.pages[sPage].onQuitPage():true; } onResize() { let sPage = this.vars('page'); if(this.pages[sPage].onResize) this.pages[sPage].onResize(); } onFeedback(sType, sMsg, asContext) { asContext = asContext || {}; let sPage = this.vars('page'); if(this.pages[sPage].onFeedback) this.pages[sPage].onFeedback(sType, sMsg, asContext); else console.log({type:sType, msg:sMsg, context:asContext}); } onKeydown(oEvent) { let sPage = this.vars('page'); if(this.pages[sPage].onKeydown) this.pages[sPage].onKeydown(oEvent); } /* Page Switch - DOM Replacement */ getActionLink(sAction, oVars) { if(!oVars) oVars = {}; let sVars = ''; for(i in oVars) sVars += '&'+i+'='+oVars[i]; return this.consts.process_page+'?a='+sAction+sVars; } addPage(sPage, oPage) { this.pages[sPage] = oPage; } switchPage(asHash) { var sPageName = asHash.page; var bSamePage = (this.vars('page') == sPageName); var bFirstPage = (this.vars('page') == ''); if(!this.consts.pages[sPageName]) { //Page does not exist if(bFirstPage) this.setHash(this.consts.default_page); else this.setHash(this.vars('page'), this.vars(['hash', 'items'])); } else if(this.onQuitPage(bSamePage) && !bSamePage || this.onSamePageMove(asHash)) { //Delete tmp variables this.vars('tmp', {}); //Officially a new page this.vars('page', sPageName); this.vars('hash', asHash); //Update Page Title this.setPageTitle(sPageName+' '+(asHash.items[0] || '')); //Replacing DOM var $Dom = $(this.consts.pages[sPageName]); if(bFirstPage) this.splash($Dom, asHash, bFirstPage); //first page else this.elem.main.stop().fadeTo('fast', 0, () => {this.splash($Dom, asHash, bFirstPage);}); //Switching page } else if(bSamePage) this.vars('hash', asHash); } splash($Dom, asHash, bFirstPage) { //Switch main content this.elem.main.empty().html($Dom); //Page Bootstrap this.pageInit(asHash, bFirstPage); //Show main var $FadeInElem = bFirstPage?this.elem.container:this.elem.main; $FadeInElem.hide().fadeTo('slow', 1); } setPageTitle(sTitle) { document.title = this.consts.title+' - '+sTitle; } getNaturalDuration(iHours) { var iTimeMinutes = 0, iTimeHours = 0, iTimeDays = Math.floor(iHours/8); //8 hours a day if(iTimeDays > 1) iTimeDays = Math.round(iTimeDays * 2) / 2; //Round down to the closest half day else { iTimeDays = 0; iTimeHours = Math.floor(iHours); iHours -= iTimeHours; iTimeMinutes = Math.floor(iHours * 4) * 15; //Round down to the closest 15 minutes } return '~ ' +(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+this.lang(iTimeDays>1?'unit_days':'unit_day')):'') //Days +((iTimeHours>0 || iTimeDays==0)?iTimeHours+this.lang('unit_hour'):'') //Hours +((iTimeDays>0 || iTimeMinutes==0)?'':iTimeMinutes) //Minutes } checkClearance(sClearance) { return (this.vars(['user', 'clearance']) >= sClearance); } }