Last active
October 20, 2020 13:05
-
-
Save bitsmanent/e1db98deaf94bb1ccf8556d993245afb to your computer and use it in GitHub Desktop.
Modular collection of JS functions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Do not use code you have not read first. | |
* Original file: https://gist.github.com/clamiax/e1db98deaf94bb1ccf8556d993245afb */ | |
(() => { | |
"use strict"; | |
var eventsmap = []; /* on(), onevent(), off() */ | |
function $(sel, ctx = document) { | |
return ctx.querySelector(sel); | |
} | |
function $$(sel, ctx = document) { | |
return ctx.querySelectorAll(sel); | |
} | |
function append(where, what) { | |
var i, len, scripts, script, p, att; | |
where.appendChild(what); | |
scripts = $$("script", what); | |
len = scripts.length; | |
for(i = 0; i < len; ++i) { | |
script = document.createElement("script"); | |
script.innerHTML = scripts[i].innerHTML; | |
for(att in scripts[i].atts) | |
script[att] = scripts[i][att]; | |
p = scripts[i].parentNode; | |
p.insertBefore(script, scripts[i]); | |
p.removeChild(scripts[i]); | |
} | |
} | |
function collects(elems) { | |
var i, len, v, keys = null, ret = {}; | |
len = elems ? elems.length : 0; | |
for(i = 0; i < len; ++i) { | |
if(!elems[i].name) | |
continue; | |
keys = elems[i].name.replace(/\[([^\]]*)\]/g, ",$1").split(','); | |
v = (elems[i].type == "checkbox" ? (elems[i].checked ? elems[i].value : "") : elems[i].value); | |
deepset(ret, keys, v); | |
} | |
if(!keys) | |
return null; | |
return ret; | |
} | |
/* required by collects() */ | |
function deepset(obj, hier, val, _i) { | |
var k; | |
if(!_i) | |
_i = 0; | |
k = hier[_i]; | |
if(!hier[_i + 1]) { | |
if(obj[k] !== undefined) { | |
if(typeof obj[k] == "string") | |
obj[k] = [obj[k]]; | |
obj[k].push(val); | |
} | |
else | |
obj[k] = val; | |
return; | |
} | |
if(!obj[k]) | |
obj[k] = {}; | |
deepset(obj[k], hier, val, _i + 1); | |
} | |
function extend(dst, src) { | |
var k; | |
for(k in src) | |
dst[k] = src[k]; | |
} | |
/* Deprecated: use template literals instead. For example: | |
* var tpl = (d) => `Hello ${d.name}`; tpl({name:"Foo"}); */ | |
function mkview(tpl, d) { | |
var ret = tpl, re, k; | |
for(k in d) { | |
re = new RegExp("%{"+k+"}", "g"); | |
ret = ret.replace(re , d[k]); | |
} | |
return ret; | |
} | |
function on(elems, evnm, callback) { | |
var i, len, uuid; | |
if(window || elems.length == undefined) | |
elems = [elems]; | |
len = elems.length; | |
if(!len) | |
return; | |
for(i = 0; i < len; ++i) { | |
if(elems[i].__id == undefined) { | |
uuid = eventsmap.length; | |
elems[i].__id = uuid; | |
} | |
else | |
uuid = elems[i].__id; | |
if(eventsmap[uuid] == undefined) | |
eventsmap[uuid] = {}; | |
if(eventsmap[uuid][evnm] == undefined) { | |
eventsmap[uuid][evnm] = []; | |
elems[i].addEventListener(evnm, onevent, true); | |
} | |
eventsmap[uuid][evnm].push(callback); | |
} | |
} | |
function onevent(ev) { | |
var uuid = this.__id, evnm = ev.type, cbs = eventsmap[uuid][evnm]; | |
var i, len, r, cbs, rv = null; | |
for(i = 0, len = cbs.length; i < len; ++i) { | |
r = cbs[i].apply(this, [ev]); | |
if(rv == null && r != undefined && !r) { | |
rv = 0; | |
continue; | |
} | |
} | |
if(rv == 0) { | |
ev.stopPropagation(); | |
ev.preventDefault(); | |
} | |
} | |
function off(elems, evnm, callback) { | |
var uuid, i, ilen, j, jlen; | |
if(elems.length == undefined) | |
elems = [elems]; | |
ilen = elems.length; | |
if(!ilen) | |
return; | |
for(i = 0, ilen = elems.length; i < ilen; ++i) { | |
uuid = elems[i].__id; | |
for(j = 0, jlen = eventsmap[uuid][evnm].length; j < jlen; ++j) { | |
if(!callback || eventsmap[uuid][evnm][j] == callback) { | |
eventsmap[uuid][evnm].splice(j, 1); | |
if(eventsmap[uuid][evnm].length == 0) { | |
elems[i].removeEventListener(evnm, onevent, false); | |
delete eventsmap[uuid][evnm]; | |
} | |
} | |
} | |
if(eventsmap[uuid].length == 0) | |
eventsmap.splice(uuid, 1); | |
} | |
} | |
function pad(s, len, c, pr) { | |
var rest; | |
/* prevent arithmetic issues */ | |
s = s.toString(); | |
c = c.toString(); | |
rest = len - s.length; | |
if(rest <= 0) | |
return s; | |
[...Array(rest)].forEach(() => s=pr ? s+c : c+s); | |
return s; | |
} | |
function parents(el, sel, _arr) { | |
if(!_arr) | |
_arr = []; | |
if(!el) | |
return _arr; | |
if(el.matches(sel)) | |
_arr.push(el); | |
return parents(el.parentElement, sel, _arr); | |
} | |
function parsexml(root) { | |
var ret = {}, node, v; | |
if(!root) | |
return null; | |
for(node of (root.attributes||[])) | |
ret[node.name] = node.value; | |
if(root.children) { | |
for(node of root.children) { | |
if(!node.children.length && node.textContent) { | |
ret[node.nodeName] = node.textContent; | |
continue; | |
} | |
if(!ret[node.nodeName]) | |
ret[node.nodeName] = []; | |
ret[node.nodeName].push(parsexml(node)); | |
} | |
} | |
else | |
ret[node.nodeName] = node.nodeValue; | |
return ret; | |
} | |
function qs() { | |
var qs = {}; | |
if(!location.search.length) | |
return qs; | |
window.location.search.split('?')[1].split('&').forEach((kv) => { | |
var sp = kv.split('='); | |
qs[sp[0]] = sp[1]; | |
}); | |
return qs; | |
} | |
/* required by xhr() */ | |
function serialize(obj, _name) { | |
var i, pfx, tp, str = []; | |
if(!obj) | |
return _name; | |
tp = typeof obj; | |
if(obj.length && tp != 'object' && tp != 'string') { /* pure arrays */ | |
for(i = 0; i < obj.length; ++i) { | |
pfx = (_name ? _name+'['+i+']' : i); | |
str.push(pfx+'='+obj[i]); | |
} | |
} | |
else { /* objects */ | |
for(i in obj) { | |
pfx = (_name ? _name+'['+i+']' : i); | |
if(typeof obj[i] == 'string' || typeof obj[i] == 'number') { | |
if(obj[i].replace) | |
str.push(pfx+'='+obj[i].replace(/&/g, '%26')); | |
else | |
str.push(pfx+'='+obj[i]); | |
} | |
else | |
str.push(serialize(obj[i], (_name ? _name+'['+i+']' : i))); | |
} | |
} | |
return str.join('&'); | |
} | |
/* An attempt to have a consistent strftime(). | |
* Not everything is implemented, only what makes sense. | |
* Reference: http://php.net/manual/en/function.strftime.php */ | |
function strftime(fmt, d) { | |
var ret = "", i, len, c; | |
var txt = (d, o) => { | |
d = d.toLocaleString(navigator.language, o).toString(); | |
/* not always capitalized (see it-IT) */ | |
d = d[0].toUpperCase() + d.substring(1); | |
return d; | |
}; | |
if(!d) d = new Date(); | |
for(i = 0, len = fmt.length; i < len; ++i) { | |
c = fmt[i]; | |
if(c != '%' || fmt[++i] == '%') { | |
ret += c; | |
continue; | |
} | |
c = fmt[i]; | |
switch(c) { | |
case 'a': ret += txt(d, {weekday:"short"}); break; | |
case 'A': ret += txt(d, {weekday:"long"}); break; | |
case 'b': ret += txt(d, {month:"short"}); break; | |
case 'B': ret += txt(d, {month:"long"}); break; | |
case 'd': ret += pad(d.getDate(), 2, 0); break; | |
case 'e': ret += pad(d.getDate(), 2, ' '); break; | |
case 'm': ret += pad(d.getMonth()+1, 2, 0); break; | |
case 'H': ret += pad(d.getHours(), 2, 0); break; | |
case 'M': ret += pad(d.getMinutes(), 2, 0); break; | |
case 'S': ret += pad(d.getSeconds(), 2, 0); break; | |
case 'Y': ret += d.getFullYear(); break; | |
case 'y': | |
/* recycle c */ | |
c = d.getFullYear().toString(); | |
ret += c.substring(c.length - 2); | |
break; | |
default: console.warn(c+": invalid format operator"); break; | |
} | |
} | |
return ret; | |
} | |
function trigger(elems, evnm, data) { | |
var ev, i, len; | |
if(typeof elems == 'object' && elems.length == undefined) | |
elems = [elems]; | |
len = elems.length; | |
if(!len) | |
return; | |
ev = new CustomEvent(evnm, {bubbles:true,detail:data}); | |
for(i = 0; i < len; ++i) | |
elems[i].dispatchEvent(ev); | |
} | |
function xhr(method, hdrs, action, data, callback) { | |
var r = new XMLHttpRequest(); | |
hdrs = hdrs || {}; | |
method = method.toUpperCase(); | |
r.onreadystatechange = () => { | |
if(r.readyState == 4 && r.status == 200) | |
callback(JSON.parse(r.responseText)); /* always expect JSON */ | |
}; | |
switch(method) { | |
case "POST": | |
r.open(method, action, true); | |
if(!hdrs["Content-Type"]) | |
hdrs["Content-Type"] = "application/x-www-form-urlencoded"; | |
break; | |
case "GET": | |
if(data) { | |
data = serialize(data); | |
action += ((action.indexOf('?') == -1) ? '?' : '&')+data; | |
data = null; | |
} | |
r.open(method, action, true); | |
break; | |
default: | |
console.error(method+": method not supported"); | |
return; | |
} | |
if(hdrs) | |
for(var h in hdrs) | |
r.setRequestHeader(h, hdrs[h]); | |
if(data && !(data instanceof FormData) && method == "POST" | |
&& hdrs["Content-Type"] == "application/x-www-form-urlencoded") | |
data = serialize(data); | |
r.send(data); | |
} | |
/* public */ | |
extend(window, { | |
$: window.$ ? window.$ : $, | |
$$: window.$$ ? window.$$ : $$, | |
append: append, | |
collects: collects, | |
extend: extend, | |
mkview: mkview, | |
on: on, | |
off: off, | |
parents: parents, | |
parsexml: parsexml, | |
qs: qs, | |
serialize: serialize, | |
strftime: strftime, | |
trigger: trigger, | |
xhr: xhr, | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment