Skip to content

Instantly share code, notes, and snippets.

@bitsmanent
Last active October 20, 2020 13:05
Show Gist options
  • Save bitsmanent/e1db98deaf94bb1ccf8556d993245afb to your computer and use it in GitHub Desktop.
Save bitsmanent/e1db98deaf94bb1ccf8556d993245afb to your computer and use it in GitHub Desktop.
Modular collection of JS functions
/* 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