soselo/js/base.php

473 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|br)\/?>/g, function (m){ return m+'&nbsp;' });
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('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;');
text = html2text(text);
text = text.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;');
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>