Files
spot/src/scripts/spot.js

304 lines
8.4 KiB
JavaScript
Executable File

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);
}
}