474 lines
14 KiB
PHP
474 lines
14 KiB
PHP
<script>
|
|
|
|
var E = {
|
|
elemid: function(id, cback) { const e = document.getElementById(id); return E.do(e, cback) },
|
|
element: function(q, cback) { const e = document.querySelector(q); return E.do(e, cback) },
|
|
elements: function(q, cback) { const e = document.querySelectorAll(q); return E.do(e, cback) },
|
|
do: function(e, cback) {
|
|
if (typeof cback === 'function' && e !== null && e !== undefined)
|
|
return cback(e);
|
|
return e;
|
|
},
|
|
class: {
|
|
removeAll: function(elems, cls) {
|
|
if (typeof elems === 'string') elems = E.elements(elems);
|
|
elems.forEach(function(it) {
|
|
it.classList !== undefined &&
|
|
it.classList.remove(cls) });
|
|
},
|
|
addAll: function(elems, cls) {
|
|
if (typeof elems === 'string') elems = E.elements(elems);
|
|
elems.forEach(function(it) {
|
|
it.classList !== undefined &&
|
|
it.classList.add(cls) });
|
|
},
|
|
setAll: function(elems, cls) {
|
|
if (typeof elems === 'string') elems = E.elements(elems);
|
|
elems.forEach(function(it) { it.className = cls });
|
|
},
|
|
},
|
|
removeAll: function(query) {
|
|
return E.remove(E.elements(query));
|
|
},
|
|
remove: function(query) {
|
|
if (typeof query === 'string') {
|
|
const _ = E.element(query);
|
|
if (_ !== null) _.remove();
|
|
} else if (query instanceof NodeList) {
|
|
for (var i = 0; i < query.length; i++)
|
|
query[i].remove();
|
|
} else if (query instanceof Object) {
|
|
query.remove();
|
|
}
|
|
},
|
|
template: function(_id, cback) {
|
|
var elem = null;
|
|
elem = E.elemid(_id);
|
|
if (elem === null)
|
|
elem = E.element(_id);
|
|
if (elem === null)
|
|
return;
|
|
if (typeof cback === 'string') {
|
|
elem.innerText = cback;
|
|
return;
|
|
}
|
|
_id = elem.id;
|
|
var tpl = E.elemid(_id+'-item');
|
|
if (tpl !== null) {
|
|
tpl = tpl.outerHTML;
|
|
tpl = tpl.replace('id="'+_id+'-item"','class="item"');
|
|
tpl = tpl.replace('data-src=', 'src=');
|
|
elem.innerHTML = cback(tpl);
|
|
}
|
|
},
|
|
http_template: function(name, cback) {
|
|
const slug = 'html_cache__'+name.replaceAll('/','--');
|
|
if (window.cache.http === undefined)
|
|
window.cache.http = {};
|
|
if (window.cache.http[slug] !== undefined)
|
|
return cback(window.cache.http[slug]);
|
|
|
|
http.get('js/templates/'+name+'.html',
|
|
{}, function(data) {
|
|
data = data.trim();
|
|
window.cache.http[slug] = data;
|
|
cback(data);
|
|
});
|
|
},
|
|
custom: {
|
|
loader: {
|
|
show: function(text, cancelable) {
|
|
text = text || 'Loading...';
|
|
E.custom.overlay(`
|
|
<div class="flex" style="max-width: 30em">
|
|
<div class="lds-ring" style="transform: scale(70%);
|
|
width: 10em">
|
|
<div></div><div></div>
|
|
<div></div><div></div>
|
|
</div>
|
|
<div class="flex">
|
|
<span class="center">${text}</span>
|
|
</div>
|
|
</div>
|
|
`, cancelable);
|
|
},
|
|
hide: function() {
|
|
E.elemid('overlay-loader').innerHTML = '';
|
|
},
|
|
},
|
|
overlay: function(html, cancelable) {
|
|
cancelable = [undefined, true].includes(cancelable) ? 'true' : 'false';
|
|
E.template('overlay-loader', function(tpl) {
|
|
tpl = tpl.replaceAll('{cancelable}', cancelable);
|
|
tpl = tpl.replaceAll('{content}', html);
|
|
return tpl;
|
|
});
|
|
},
|
|
btncollapse_render: function() {
|
|
E.elements('button[id*="btncollapse-"]').forEach(function(it) {
|
|
it.setAttribute('onclick', 'E.custom.btncollapse_click(this)');
|
|
});
|
|
},
|
|
btncollapse_click: function(elem) {
|
|
const stat = elem.innerText.trim().toLowerCase();
|
|
const refelem = E.elemid(elem.id.replace('btncollapse-',''));
|
|
if (stat === 'hide') {
|
|
elem.innerText = 'show';
|
|
refelem.style.visibility = 'collapse';
|
|
refelem.style.height = '1px';
|
|
refelem.style.padding = 0;
|
|
refelem.style.margin = 0;
|
|
} else {
|
|
elem.innerText = 'hide';
|
|
refelem.removeAttribute('style');
|
|
}
|
|
},
|
|
},
|
|
};
|
|
|
|
function isVisible(element) {
|
|
const rect = element.getBoundingClientRect();
|
|
return (
|
|
rect.top >= 0 &&
|
|
rect.left >= 0 &&
|
|
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
|
);
|
|
}
|
|
|
|
function html2text(html) {
|
|
html = html.replace(/<\/(p|div)>/g, function (m){ return m+' ' });
|
|
html = html.replace(/<br\s*\/?>/g, function (m){ return m+' ' });
|
|
html = html.replace(/\s+/g, ' ');
|
|
var e = document.createElement('div');
|
|
e.innerHTML = html;
|
|
var result = e.innerText;
|
|
result = result.replace(/\s+/g, ' ');
|
|
e.remove();
|
|
return result.trim();
|
|
}
|
|
|
|
function JSON_to_URLEncoded(element,key,list) {
|
|
var list = list || [];
|
|
if (typeof(element) == 'object') {
|
|
for (var idx in element)
|
|
JSON_to_URLEncoded(element[idx],key?key+'['+idx+']':idx,list);
|
|
} else {
|
|
list.push(key+'='+encodeURIComponent(element));
|
|
}
|
|
return list.join('&');
|
|
}
|
|
|
|
function remove_accents(str) {
|
|
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
}
|
|
|
|
function insert_string(main_string, ins_string, pos) {
|
|
if (typeof(pos) == undefined)
|
|
pos = 0;
|
|
if (typeof(ins_string) == undefined)
|
|
ins_string = '';
|
|
return main_string.slice(0, pos) + ins_string + main_string.slice(pos);
|
|
}
|
|
|
|
function normalize_word_sound(word, cback) {
|
|
const hashes = [
|
|
'4:a', '3:e', '1:i', '0:o',
|
|
'5:s', 'b:v', '8:b', 'k:c',
|
|
'y:i', 'que:kee', 'q:k',
|
|
];
|
|
for (var i = 0; i < hashes.length; i++) {
|
|
const hash = hashes[i].split(':');
|
|
if (cback !== undefined)
|
|
word = cback(word, hash);
|
|
else word = word.replaceAll(hash[0], hash[1]);
|
|
}
|
|
return word;
|
|
}
|
|
|
|
function normalize_for_search(str) {
|
|
if (str.trim() === '') return '';
|
|
str = str.trim().replaceAll(/\s\s+/g, ' ');
|
|
var words = str.split(' ');
|
|
var newwords = [];
|
|
for (var i = 0; i < words.length; i++) {
|
|
var word = words[i];
|
|
word = remove_accents(word);
|
|
word = word.toLowerCase();
|
|
word = word.replaceAll(/[^a-z0-9]+/g, ' ');
|
|
if (word.trim() === '')
|
|
continue;
|
|
word = normalize_word_sound(word);
|
|
var nword = '';
|
|
for (var j = 0; j < word.length; j++) {
|
|
if (j === 0) {
|
|
nword += word[0];
|
|
continue;
|
|
}
|
|
if (word[j] !== word[j-1]) {
|
|
nword += word[j];
|
|
}
|
|
}
|
|
newwords.push(nword);
|
|
}
|
|
return newwords.join(' ');
|
|
}
|
|
|
|
// source: https://www.geeksforgeeks.org/how-to-get-the-javascript-function-parameter-names-values-dynamically/
|
|
function _get_func_params(func) {
|
|
// String representaation of the function code
|
|
var str = func.toString();
|
|
// Remove comments of the form /* ... */
|
|
// Removing comments of the form //
|
|
// Remove body of the function { ... }
|
|
// removing '=>' if func is arrow function
|
|
str = str.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
.replace(/\/\/(.)*/g, '')
|
|
.replace(/{[\s\S]*}/, '')
|
|
.replace(/=>/g, '')
|
|
.trim();
|
|
// Start parameter names after first '('
|
|
var start = str.indexOf("(") + 1;
|
|
// End parameter names is just before last ')'
|
|
var end = str.length - 1;
|
|
var result = str.substring(start, end).split(", ");
|
|
var params = [];
|
|
result.forEach(element => {
|
|
// Removing any default value
|
|
element = element.replace(/=[\s\S]*/g, '').trim();
|
|
if(element.length > 0)
|
|
params.push(element);
|
|
});
|
|
return params;
|
|
}
|
|
|
|
function del_hash_argument(key, fireevent) {
|
|
document.activeElement.blur();
|
|
|
|
const hargs = get_hash_arguments();
|
|
if (hargs[key] === undefined) return;
|
|
|
|
fireevent = fireevent === undefined ? true : fireevent;
|
|
console.log(`DEL hash-argument: ${key} (fireevent: ${fireevent})`);
|
|
if (fireevent) delete window.hash_argument_nofirechange;
|
|
else window.hash_argument_nofirechange = true;
|
|
|
|
var hash = window.location.hash;
|
|
hash = hash.replace(new RegExp(';?'+key+'=[^;]+'),'');
|
|
hash = hash.replace('/;','/');
|
|
window.location.hash = hash;
|
|
}
|
|
|
|
function set_hash_argument(key, val, fireevent) {
|
|
document.activeElement.blur();
|
|
|
|
fireevent = fireevent === undefined ? true : fireevent;
|
|
console.log(`SET hash-argument "${key}": ${val} (fireevent: ${fireevent})`);
|
|
if (fireevent) delete window.hash_argument_nofirechange;
|
|
else window.hash_argument_nofirechange = true;
|
|
|
|
var hargs = get_hash_arguments();
|
|
if (hargs[key] === undefined) {
|
|
window.location.hash += ';'+key+'='+val;
|
|
return;
|
|
}
|
|
var hash = window.location.hash;
|
|
hash = hash.replace(new RegExp(key+'=[^;]+'),`${key}=${val}`);
|
|
window.location.hash = hash;
|
|
}
|
|
|
|
function get_hash_arguments() {
|
|
var args = window.location.hash.substring(1).trim().split('/');
|
|
if (args.length > 0 && args[0].trim() === '')
|
|
args = [];
|
|
return parse_hash_arguments(args[args.length-1]);
|
|
}
|
|
|
|
function parse_ini_config(str) { return parse_hash_arguments(str, '\n') }
|
|
function parse_hash_arguments(str,by) {
|
|
by = by || ';';
|
|
var args = {};
|
|
const ps = str.trim().split(by);
|
|
for (var i = 0; i < ps.length; i++) {
|
|
if (!ps[i].includes('='))
|
|
continue;
|
|
const k = ps[i].substr(0,ps[i].indexOf('='));
|
|
const v = ps[i].substr(ps[i].indexOf('=')+1);
|
|
if (k.trim() === '')
|
|
continue;
|
|
args[k] = v;
|
|
}
|
|
return args;
|
|
}
|
|
|
|
function human_field_name(str) {
|
|
str = ' '+str+' ';
|
|
return capitalize(
|
|
str.replaceAll('_', ' ')
|
|
.replaceAll(' api ',' API ')
|
|
.replaceAll(' db ',' DB ')
|
|
.replaceAll(' url ',' URL ')
|
|
.replaceAll(' psql ',' Postgres ')
|
|
.trim()
|
|
);
|
|
}
|
|
function sleep(ms){ return new Promise(resolve => setTimeout(resolve, ms)) }
|
|
function capitalize(str) { return str[0].toUpperCase() + str.substring(1) }
|
|
function printstack() { console.error(new Error().stack) }
|
|
function uuidv4() {
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
return v.toString(16);
|
|
});
|
|
}
|
|
|
|
window.http_requests = {};
|
|
const http = {
|
|
abort: function() {
|
|
const ks = Object.keys(window.http_requests);
|
|
for (var i = 0; i < ks.length; i++) {
|
|
window.http_requests[ks[i]].abort();
|
|
delete window.http_requests[ks[i]];
|
|
}
|
|
E.custom.loader.hide();
|
|
},
|
|
cache: {
|
|
buster_process: function(path) {
|
|
if (window.http_cachebuster === undefined)
|
|
return false;
|
|
const ks = Object.keys(window.http_cachebuster);
|
|
var keys = null;
|
|
for (var i = 0; i < ks.length; i++) {
|
|
if (path.match(new RegExp(ks[i])) || path.startsWith(ks[i])) {
|
|
keys = window.http_cachebuster[ks[i]];
|
|
keys = keys.trim().split('|');
|
|
for (var j = 0; j < keys.length; j++)
|
|
keys[j] = keys[j].trim();
|
|
}
|
|
}
|
|
if (keys === null)
|
|
return false;
|
|
for (var i = 0; i < keys.length; i++)
|
|
http.cache.bust(keys[i]);
|
|
},
|
|
bust_all: function() {
|
|
const ks = Object.keys(localStorage);
|
|
for (var i = 0; i < ks.length; i++)
|
|
if (ks[i].startsWith('http_cachekey__'))
|
|
delete localStorage[ks[i]];
|
|
},
|
|
bust: function(key) {
|
|
localStorage['http_cachekey__'+key] = (new Date()).getTime();
|
|
},
|
|
bust_if_new: function(key) {
|
|
if (localStorage['http_cachekey__'+key] === undefined)
|
|
http.cache.bust(key);
|
|
},
|
|
get: function(key) {
|
|
return localStorage['http_cachekey__'+key] || (new Date()).getTime();
|
|
},
|
|
},
|
|
request: function(method, path, payload, callbk, cache_key) {
|
|
cache_key = cache_key || null;
|
|
payload = payload || null;
|
|
callbk = callbk || null;
|
|
|
|
// Apache servers work like shit on redirects
|
|
// this code is needed to avoid 301 on API,
|
|
/// which makes POST and other request fail.
|
|
path = path.trim();
|
|
if (path.indexOf('?') !== -1)
|
|
path = path.replace(/(?<=[^\/])\?/, '/?');
|
|
else if (!path.endsWith('/') &&
|
|
!(path.endsWith('.php') || path.endsWith('.html')))
|
|
path += '/';
|
|
|
|
//console.log(path); printstack();
|
|
const httpid = uuidv4();
|
|
const httpdiv = document.getElementById('http');
|
|
const httpts = new Date().getTime();
|
|
if (httpdiv !== null)
|
|
httpdiv.innerHTML += '<div id="http-'+httpts+'">'+method+' '+path+'</div>';
|
|
|
|
document.activeElement.blur();
|
|
http.cache.buster_process(path);
|
|
var cachestr = '';
|
|
if (cache_key !== null) {
|
|
http.cache.bust_if_new(cache_key);
|
|
var cacheval = http.cache.get(cache_key);
|
|
if (!path.includes('?'))
|
|
cachestr = '?_c='+cacheval;
|
|
else cachestr = '&_c='+cacheval;
|
|
}
|
|
path += cachestr;
|
|
|
|
const oReq = new XMLHttpRequest();
|
|
oReq.addEventListener("load", function() {
|
|
delete window.http_requests[httpid];
|
|
E.custom.loader.hide();
|
|
|
|
if (httpdiv !== null) {
|
|
const hit = document.getElementById('http-'+httpts);
|
|
if (hit !== null) hit.remove();
|
|
}
|
|
if (callbk) {
|
|
if (this.status === 403) {
|
|
toast.error('Unautorized: session might have been closed');
|
|
setTimeout(function(){ window.location.href = '/' }, 4000);
|
|
return false;
|
|
}
|
|
const ps = _get_func_params(callbk);
|
|
if (ps.includes('data') || ps.includes('text') ||
|
|
ps.includes('html') || ps.includes('plain'))
|
|
callbk(this.responseText);
|
|
else if (ps.includes('json') || ps.includes('js')) {
|
|
try { callbk(JSON.parse(this.responseText)) }
|
|
catch (SyntaxError) { callbk(undefined) }
|
|
}
|
|
}
|
|
});
|
|
oReq.open(method, path);
|
|
oReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
oReq.send(JSON_to_URLEncoded(payload));
|
|
window.http_requests[httpid] = oReq;
|
|
},
|
|
get: function(path, payload, callbk, cache_key) {
|
|
return http.request('GET', path, payload, callbk, cache_key);
|
|
},
|
|
post: function(path, payload, callbk, cache_key) {
|
|
return http.request('POST', path, payload, callbk, cache_key);
|
|
},
|
|
API: {
|
|
}
|
|
};
|
|
const toast = {
|
|
error: function(text, timeo) {
|
|
timeo = timeo || 7000;
|
|
return toast._show(text, 'error', 'exclamation-triangle', timeo);
|
|
},
|
|
info: function(text, timeo) {
|
|
timeo = timeo || 5000;
|
|
return toast._show(text, 'info', 'info-circle', timeo)
|
|
},
|
|
_show: function(text, type, fa, timeo) {
|
|
const id = 'toast-'+(new Date()).getTime();
|
|
var div = document.createElement('div');
|
|
div.id = id;
|
|
div.className = 'toast-container';
|
|
div.setAttribute('onclick', 'this.remove()');
|
|
text = text.replaceAll('&','&').replaceAll('<','<').replaceAll('>','>');
|
|
text = html2text(text);
|
|
text = text.replaceAll('&','&').replaceAll('<','<').replaceAll('>','>');
|
|
div.innerHTML = '<div class="toast '+type+'"><p style="margin: auto 0 !important;font-size: 1.2em;line-height: 1.8em;">'+
|
|
'<i style="margin-right: 1em;font-size: 1.2em;position:relative;top:.1em" class="fa fa-'+fa+'"></i>'+capitalize(text)+'</p></div>';
|
|
if (document.getElementById('toast-container') !== null)
|
|
document.getElementById('toast-container').appendChild(div);
|
|
else document.body.appendChild(div);
|
|
setTimeout(function() {
|
|
const el = document.getElementById(id)
|
|
if (el !== undefined && el !== null) el.remove();
|
|
}, timeo);
|
|
},
|
|
};
|
|
</script>
|