Last active
June 3, 2018 17:35
-
-
Save SaulDoesCode/1327daa63187ba14ac9163efdd3e9cea to your computer and use it in GitHub Desktop.
tired of frameworks? here's a framework! you'll need the latest browsers to run it and so-on. have fun? Don't worry it only weighs 5352 bytes and it doesn't bite. It's post-modern art I tell you!
This file contains 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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="author" content="Saul van der Walt"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>festoon.js</title> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css"> | |
<style> | |
body { | |
font-family: Nunito, Verdunda, Helvetica, Arial, sans-serif; | |
} | |
</style> | |
<script src="./festoon.js"></script> | |
<script defer> | |
const {div, h1, footer, p, render} = dom | |
render([ | |
h1.festoon(` | |
This is not a joke I promise! | |
it's just a little ehm... uh... postmodern you know. | |
y'all best know the difference now jk! | |
`), | |
p({ | |
on: { | |
mouseover: e => console.log(`How dare you mouseover me!?!?!?`) | |
} | |
}, ` | |
festoon.js - does many things, perhaps too many yet still not enough. | |
it is not huge but for even for it's reasonable size it offers luxuries | |
your other KBs can't afford to provide. | |
docs? oh no best you read the source now. | |
freedom oh yes many so many too many. | |
Do you really need festoon? Not really but hey that's not important. | |
`) | |
]) | |
// let's not forget the footer, most important part of the site | |
try { | |
footer.footer.is_footer.bigFooter.theFooter({$: 'body'}, | |
but => that => nesting => though => `The Imposible footer` | |
) | |
// ^-- but does this work? well download festoon and find out... * evil laugh * | |
// throw {message: 'boo hoo'} | |
} catch (e) { | |
window.cheek = div({ | |
render: 'body', | |
class: {apologize: true}, | |
attr: { | |
'listen-to-reason': 'no? I thought we were past that and those pesky positivists', | |
nihilism: 'yasss gimme gimme gimmee!!!!' | |
}, | |
props: { | |
accessors: { | |
butWhatWouldChompskySay: { | |
get: () => 'were almost 2 minutes to midnight!' | |
}, | |
what_Would_Dead_Derida_Say_Kinda: () => ` | |
Everything is a text! Let's deconstruct the phalo-logical myth of the corrupt western modernitè as | |
an integrated response combining the lacanian objet-a to the Sartrian notion of unbearable freedom. | |
` | |
} | |
}, | |
methods: { | |
spewNonsenseDangerously (el, ...extras) { | |
if (extras.length) extras.push(' and so on, etc...\n') | |
this.innerHTML = ` | |
<h1>${el.what_Would_Dead_Derida_Say_Kinda}</h1> | |
${ | |
extras.length | |
? '<p><b>' + extras.join(' and then ') + '</b></p>' | |
: '' | |
} | |
` | |
} | |
}, | |
onceclick (e, {spewNonsenseDangerously}) { | |
spewNonsenseDangerously() | |
this.taintSpread = true | |
} | |
}, | |
` | |
I'll let you know I'm very sorry for wasting your time, | |
in the meantime here's a savory error message to tickle your tastebuds:\t | |
`, | |
e.message | |
) | |
} | |
</script> | |
</head> | |
</html> |
This file contains 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
{ /* global Node NodeList Text requestIdleCallback */ | |
const {assign, defineProperty: define} = Object | |
const isArr = Array.isArray | |
const isObj = o => o != null && o.constructor === Object | |
const isFn = o => o instanceof Function | |
const isStr = o => o != null && typeof o === 'string' | |
const isStrOrNum = o => (o = typeof o, o === 'string' || o === 'number') | |
const isMounted = (child, parent = document) => parent === child || !!(parent.compareDocumentPosition(child) & 16) | |
const find = (selector, parent = document) => isStr(selector) ? find(parent).querySelector(selector) : selector | |
find.all = (selector, parent = document) => isStr(selector) ? Array.from(find(parent).querySelectorAll(selector)) : selector != null && selector.length ? selector : [] | |
find.each = (selector, parent, fn) => { | |
if (isFn(parent)) [fn, parent] = [parent, document] | |
const nodes = find.all(selector, parent) | |
if (nodes.length) for (let i = 0; i < nodes.length; i++) fn.call(nodes[i], nodes[i], i, nodes) | |
return nodes | |
} | |
const listen = (once, target, type, fn, options = false) => { | |
if (isStr(target) && (target = find.all(target)).length === 1) target = target[0] | |
if (!(target instanceof Node) || (isArr(target) && !target.length)) throw new Error('nil/empty event target(s)') | |
let typeobj = isObj(type) | |
if (type == null || !(typeobj || isStr(type))) { | |
throw new TypeError('cannot listen to nil or invalid event type') | |
} | |
if (isArr(target)) { | |
for (let i = 0; i < target.length; i++) | |
target[i] = listen(once, target, typeobj ? assign({}, type) : type, fn, options) | |
return target | |
} | |
if (typeobj) { | |
for (const name in type) type[name] = listen(once, target, name, type[name], options) | |
return type | |
} | |
function wrapper () { | |
fn.call(this, ...arguments, target) | |
if (stop.once) stop() | |
} | |
const on = mode => { | |
if (mode != null && mode !== stop.once) stop.once = !!mode | |
target.addEventListener(type, wrapper, options) | |
stop.ison = true | |
return stop | |
} | |
const stop = assign(() => { | |
target.removeEventListener(type, wrapper) | |
stop.ison = false | |
}, {target, listen: on, once}) | |
stop.stop = stop | |
return on() | |
} | |
const infinifyListen = { get: (ln, type) => (tgt, fn, opts) => ln(tgt, type, fn, opts) } | |
const on = new Proxy(listen.bind(null, false), infinifyListen) | |
const once = new Proxy(listen.bind(null, true), infinifyListen) | |
const run = (fn, ...args) => { | |
fn = fn.bind(null, ...args) | |
document.body ? requestIdleCallback(fn) : window.addEventListener('DOMContentLoaded', fn, {once: true}) | |
} | |
const html = (input, host) => { | |
if (isFn(input)) input = input(host) | |
if (isStr(input)) return Array.from(document.createRange().createContextualFragment(input).childNodes) | |
if (input instanceof Node) return input | |
if (isArr(input)) return input.map(i => html(i)) | |
} | |
const frag = inner => inner != null ? html(inner) : document.createDocumentFragment() | |
const vpend = (children, host, connector = 'appendChild', dfrag = frag(), noAppend) => { | |
for (let i = 0; i < children.length; i++) { | |
let child = children[i] | |
if (isFn(child)) { | |
if ((child = child(host)) === host) continue | |
else if (isFn(child)) { | |
let lvl = 0 | |
let ishost = false | |
while (isFn(child) && lvl < 25) { | |
child = child() | |
if ((ishost = child === host)) break | |
lvl++ | |
} | |
if (ishost) continue | |
} | |
} | |
if (isStr(child)) { | |
if (!child.length) continue | |
child = new Text(child) | |
} else if (isArr(child)) child = vpend(child, host, connector, dfrag, true) | |
if (child instanceof Node) { | |
dfrag.appendChild(child) | |
children[i] = child | |
} | |
} | |
if (host && !noAppend) run(() => host[connector](dfrag)) | |
return children | |
} | |
const prime = (...nodes) => { | |
for (let i = 0; i < nodes.length; i++) { | |
let n = nodes[i] | |
if (n instanceof Node || isFn(n)) continue | |
if (n == null || typeof n === 'boolean') nodes.splice(i, 1) | |
else if (isStrOrNum(n)) { | |
const nextI = i + 1 | |
if (nextI < nodes.length) { | |
const next = nodes[nextI] | |
if (isStrOrNum(next)) { | |
nodes[i] = new Text(String(n) + String(next)) | |
nodes.splice(nextI, 1) | |
i-- | |
} else nodes[i] = new Text(String(n)) | |
} | |
} else { | |
const isnl = n instanceof NodeList | |
if (isnl) { | |
if (n.length === 1) { | |
nodes[i] = n[0] | |
i-- | |
continue | |
} | |
n = Array.from(n) | |
} else if (isObj(n)) n = Object.values(n) | |
if (isArr(n)) { | |
if (!isnl) { | |
n = prime.apply(undefined, n) | |
if (n.length === 1) { | |
nodes[i] = n[0] | |
i-- | |
continue | |
} | |
} | |
nodes.splice(i, 1, ...n) | |
i-- | |
} else if (n != null) throw new Error(`illegal renderable: ${n}`) | |
} | |
} | |
return nodes | |
} | |
const attach = (host, connector, ...renderables) => { | |
if (host == null) return | |
if (isFn(host)) host = host() | |
renderables = prime(renderables) | |
if (isStr(host)) return run(() => attach(find(host), connector, ...renderables)) | |
if (host instanceof Node) vpend(renderables, host, connector) | |
else if (isArr(host)) host.push(...renderables) | |
return renderables.length < 2 ? renderables[0] : renderables | |
} | |
const render = (node, host = document.body || 'body', connector = 'appendChild') => attach(host, connector, node) | |
const domfn = { | |
css(node, styles, prop) { | |
if (isObj(styles)) for (const key in styles) domfn.css(node, key, styles[key]) | |
else if (isStr(styles)) styles[0] === '-' ? node.style.setProperty(styles, prop) : node.style[styles] = prop | |
return node | |
}, | |
class (node, c, state) { | |
if (!node || c == null || !node.classList) return node | |
if (isArr(node)) for (let i = 0; i < node.length; i++) domfn.class(node[i], c, state) | |
else if (isObj(c)) for (const name in c) domfn.class(node, name, c[name]) | |
else { | |
if (isStr(c)) c = c.split(' ') | |
if (isArr(c)) { | |
const bs = typeof state === 'boolean' | |
for (let i = 0; i < c.length; i++) node.classList[bs ? state ? 'add' : 'remove' : 'toggle'](c[i]) | |
} | |
} | |
return node | |
}, | |
hasClass: (node, name) => node.classList.contains(name), | |
attr (node, attr, val) { | |
if (isObj(attr)) for (const a in attr) node[attr[a] == null ? 'removeAttribute' : 'setAttribute'](a, attr[a]) | |
else { | |
if (val == null) return node.getAttribute(attr) | |
node.setAttribute(attr, val) | |
} | |
return node | |
}, | |
attrToggle: (node, name, state = !node.hasAttribute(name), val = node.getAttribute(name) || '') => (node[state ? 'setAttribute' : 'removeAttribute'](name, val), node), | |
append: (node, ...children) => (attach(node, 'appendChild', ...children), node), | |
prepend: (node, ...children) => (attach(node, 'prepend', ...children), node), | |
appendTo: (node, host) => (attach(host, 'appendChild', node), node), | |
prependTo: (node, host) => (attach(host, 'prepend', node), node), | |
clear: node => (node['value' in node ? 'value' : 'textContent'] = '', node), | |
remove (node, after) { | |
if (isFn(node)) node = node() | |
if (isArr(node)) for (let i = 0; i < node.length; i++) domfn.remove(node[i], after) | |
else if (typeof after === 'number') setTimeout(() => domfn.remove(node), after) | |
else if (isMounted(node)) run(() => node.remove()) | |
else if (node instanceof NodeList || (isArr(node) && node.every(n => n instanceof NodeList))) for (let i = 0; i < node.length; i++) domfn.remove(node[i]) | |
return node | |
}, | |
replace: (node, newnode) => (run(() => node.replaceWith(newnode)), newnode = isFn(newnode) ? newnode() : newnode) | |
} | |
const assimilateProps = (el, props) => { | |
for (const prop in props) { | |
let val = props[prop] | |
if (prop in el) el[prop] = val | |
else if (prop === 'accessors') { | |
for (const key in val) { | |
const {set = val[key], get = val[key]} = val[key] | |
const accessors = {} | |
if (isFn(set)) accessors.set = set.bind(el, el) | |
if (isFn(get)) accessors.get = get.bind(el, el) | |
define(el, key, accessors) | |
} | |
} else if (isFn(val)) el[prop] = val.call(el, el) | |
else define(el, prop, Object.getOwnPropertyDescriptor(props, prop)) | |
} | |
} | |
const assimilateMethods = (el, methods) => { | |
for (const name in methods) define(el, name, {value: methods[name].bind(el, el)}) | |
} | |
const infinifyDOM = (dom, tag) => { | |
if (Reflect.has(dom, tag)) return Reflect.get(dom, tag) | |
const el = dom.bind(undefined, tag) | |
el.$classes = [] | |
return (dom[tag] = new Proxy(el, { | |
apply (el, _, args) { | |
el = el(...args) | |
if (dom[tag].$classes.length) { | |
for (let i = 0; i < dom[tag].$classes.length; i++) el.classList.add(dom[tag].$classes[i]) | |
dom[tag].$classes.length = 0 | |
} | |
return el | |
}, | |
get (el, className, proxy) { | |
if (className === '$classes') return el.$classes | |
el.$classes.push(...className.replace(/_/g, '-').split('.')) | |
return proxy | |
} | |
})) | |
} | |
var dom = new Proxy(assign((tag, opts, ...children) => { | |
const el = isStr(tag) ? document.createElement(tag) : tag | |
if (isObj(opts)) { | |
if (opts.props) assimilateProps(el, opts.props) | |
if (opts.methods) assimilateMethods(el, opts.methods) | |
for (const key in opts) { | |
let val = opts[key] | |
if (val == null) continue | |
if (key[0] === 'o' && key[1] === 'n') { | |
const isOnce = key[2] === 'c' && key[3] === 'e' | |
const evtfn = listen.bind(null, isOnce) | |
const args = isArr(val) ? val : [val] | |
const i = isOnce ? 4 : 2 | |
const mode = key.substr(0, i) | |
let type = key.substr(i) | |
if (!opts[mode]) opts[mode] = {} | |
opts[mode][type] = type.length ? evtfn(el, type, ...args) : evtfn(el, ...args) | |
} else if (key in el) isFn(el[key]) ? isArr(val) ? el[key].apply(el, val) : el[key](val) : el[key] = opts[key] | |
else if (key in domfn) { | |
val = isArr(opts[key]) ? domfn[key](el, ...val) : domfn[key](el, val) | |
if (val !== el) opts[key] = val | |
} | |
} | |
attach(opts.$ || opts.render, 'appendChild', el) | |
} | |
if (el.nodeType !== 3 /* el != TextNode */) { | |
if (isFn(opts)) { | |
const result = opts.call(el, el) | |
opts = result !== el ? result : null | |
} | |
if (opts instanceof Node || isStrOrNum(opts)) children.unshift(opts) | |
if (children.length) attach(el, 'appendChild', ...children) | |
} | |
return el | |
}, {find, listen, on, once, render, fn: domfn}), {get: infinifyDOM}) | |
} |
This file contains 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
{const{assign:a,defineProperty:b}=Object,d=Array.isArray,e=a=>null!=a&&a.constructor===Object,c=a=>a instanceof Function,f=a=>null!=a&&"string"==typeof a,g=a=>(a=typeof a,"string"===a||"number"===a),h=(a,b=document)=>b===a||!!(16&b.compareDocumentPosition(a)),i=(a,b=document)=>f(a)?i(b).querySelector(a):a;i.all=(a,b=document)=>f(a)?Array.from(i(b).querySelectorAll(a)):null!=a&&a.length?a:[],i.each=(a,b,d)=>{c(b)&&([d,b]=[b,document]);const e=i.all(a,b);if(e.length)for(let a=0;a<e.length;a++)d.call(e[a],e[a],a,e);return e};const j=(b,c,g,h,k=!1)=>{function l(){h.call(this,...arguments,c),o.once&&o()}if(f(c)&&1===(c=i.all(c)).length&&(c=c[0]),!(c instanceof Node)||d(c)&&!c.length)throw new Error("nil/empty event target(s)");let m=e(g);if(null==g||!(m||f(g)))throw new TypeError("cannot listen to nil or invalid event type");if(d(c)){for(let d=0;d<c.length;d++)c[d]=j(b,c,m?a({},g):g,h,k);return c}if(m){for(const a in g)g[a]=j(b,c,a,g[a],k);return g}const n=a=>(null!=a&&a!==o.once&&(o.once=!!a),c.addEventListener(g,l,k),o.ison=!0,o),o=a(()=>{c.removeEventListener(g,l),o.ison=!1},{target:c,listen:n,once:b});return o.stop=o,n()},k={get:(a,b)=>(c,d,e)=>a(c,b,d,e)},l=new Proxy(j.bind(null,!1),k),m=new Proxy(j.bind(null,!0),k),n=(a,...b)=>{a=a.bind(null,...b),document.body?requestIdleCallback(a):window.addEventListener("DOMContentLoaded",a,{once:!0})},o=(a,b)=>(c(a)&&(a=a(b)),f(a)?Array.from(document.createRange().createContextualFragment(a).childNodes):a instanceof Node?a:d(a)?a.map(a=>o(a)):void 0),p=a=>null==a?document.createDocumentFragment():o(a),q=(a,b,e="appendChild",g=p(),h)=>{for(let j,k=0;k<a.length;k++){if(j=a[k],c(j))if((j=j(b))===b)continue;else if(c(j)){let a=0,d=!1;for(;c(j)&&25>a&&(j=j(),!(d=j===b));)a++;if(d)continue}if(f(j)){if(!j.length)continue;j=new Text(j)}else d(j)&&(j=q(j,b,e,g,!0));j instanceof Node&&(g.appendChild(j),a[k]=j)}return b&&!h&&n(()=>b[e](g)),a},r=(...a)=>{for(let b,f=0;f<a.length;f++)if(b=a[f],!(b instanceof Node||c(b)))if(null==b||"boolean"==typeof b)a.splice(f,1);else if(g(b)){const c=f+1;if(c<a.length){const d=a[c];g(d)?(a[f]=new Text(b+""+(d+"")),a.splice(c,1),f--):a[f]=new Text(b+"")}}else{const c=b instanceof NodeList;if(c){if(1===b.length){a[f]=b[0],f--;continue}b=Array.from(b)}else e(b)&&(b=Object.values(b));if(d(b)){if(!c&&(b=r.apply(void 0,b),1===b.length)){a[f]=b[0],f--;continue}a.splice(f,1,...b),f--}else if(null!=b)throw new Error(`illegal renderable: ${b}`)}return a},s=(a,b,...e)=>{if(null!=a)return(c(a)&&(a=a()),e=r(e),f(a))?n(()=>s(i(a),b,...e)):(a instanceof Node?q(e,a,b):d(a)&&a.push(...e),2>e.length?e[0]:e)},t={css(a,b,c){if(e(b))for(const c in b)t.css(a,c,b[c]);else f(b)&&("-"===b[0]?a.style.setProperty(b,c):a.style[b]=c);return a},class(a,b,g){if(!a||null==b||!a.classList)return a;if(d(a))for(let c=0;c<a.length;c++)t.class(a[c],b,g);else if(e(b))for(const c in b)t.class(a,c,b[c]);else if(f(b)&&(b=b.split(" ")),d(b)){for(let c=0;c<b.length;c++)a.classList["boolean"==typeof g?g?"add":"remove":"toggle"](b[c])}return a},hasClass:(a,b)=>a.classList.contains(b),attr(b,c,a){if(e(c))for(const d in c)b[null==c[d]?"removeAttribute":"setAttribute"](d,c[d]);else{if(null==a)return b.getAttribute(c);b.setAttribute(c,a)}return b},attrToggle:(a,b,c=!a.hasAttribute(b),d=a.getAttribute(b)||"")=>(a[c?"setAttribute":"removeAttribute"](b,d),a),append:(a,...b)=>(s(a,"appendChild",...b),a),prepend:(a,...b)=>(s(a,"prepend",...b),a),appendTo:(a,b)=>(s(b,"appendChild",a),a),prependTo:(a,b)=>(s(b,"prepend",a),a),clear:a=>(a["value"in a?"value":"textContent"]="",a),remove(a,b){if(c(a)&&(a=a()),d(a))for(let c=0;c<a.length;c++)t.remove(a[c],b);else if("number"==typeof b)setTimeout(()=>t.remove(a),b);else if(h(a))n(()=>a.remove());else if(a instanceof NodeList||d(a)&&a.every(a=>a instanceof NodeList))for(let b=0;b<a.length;b++)t.remove(a[b]);return a},replace:(a,b)=>(n(()=>a.replaceWith(b)),b=c(b)?b():b)},u=(a,d)=>{for(const e in d){let f=d[e];if(e in a)a[e]=f;else if("accessors"==e)for(const d in f){const{set:e=f[d],get:g=f[d]}=f[d],h={};c(e)&&(h.set=e.bind(a,a)),c(g)&&(h.get=g.bind(a,a)),b(a,d,h)}else c(f)?a[e]=f.call(a,a):b(a,e,Object.getOwnPropertyDescriptor(d,e))}},v=(a,c)=>{for(const d in c)b(a,d,{value:c[d].bind(a,a)})};var dom=new Proxy(a((a,b,...h)=>{const k=f(a)?document.createElement(a):a;if(e(b)){for(const a in b.props&&u(k,b.props),b.methods&&v(k,b.methods),b){let e=b[a];if(null!=e)if("o"===a[0]&&"n"===a[1]){const c="c"===a[2]&&"e"===a[3],f=j.bind(null,c),g=d(e)?e:[e],h=c?4:2,i=a.substr(0,h);let l=a.substr(h);b[i]||(b[i]={}),b[i][l]=l.length?f(k,l,...g):f(k,...g)}else a in k?c(k[a])?d(e)?k[a].apply(k,e):k[a](e):k[a]=b[a]:a in t&&(e=d(b[a])?t[a](k,...e):t[a](k,e),e!==k&&(b[a]=e))}s(b.$||b.render,"appendChild",k)}if(3!==k.nodeType){if(c(b)){const a=b.call(k,k);b=a===k?null:a}(b instanceof Node||g(b))&&h.unshift(b),h.length&&s(k,"appendChild",...h)}return k},{find:i,listen:j,on:l,once:m,render:(a,b=document.body||"body",c="appendChild")=>s(b,c,a),fn:t}),{get:(a,b)=>{if(Reflect.has(a,b))return Reflect.get(a,b);const c=a.bind(void 0,b);return c.$classes=[],a[b]=new Proxy(c,{apply(c,d,e){if(c=c(...e),a[b].$classes.length){for(let d=0;d<a[b].$classes.length;d++)c.classList.add(a[b].$classes[d]);a[b].$classes.length=0}return c},get(a,b,c){return"$classes"===b?a.$classes:(a.$classes.push(...b.replace(/_/g,"-").split(".")),c)}})}})} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment