Skip to content

Instantly share code, notes, and snippets.

@SaulDoesCode
Last active June 3, 2018 17:35
Show Gist options
  • Save SaulDoesCode/1327daa63187ba14ac9163efdd3e9cea to your computer and use it in GitHub Desktop.
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!
<!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>
{ /* 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})
}
{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