const hex = (function() { const q = (el, sel) => !sel ? document.querySelectorAll(el) : el.querySelectorAll(sel); const id = (sel) => document.getElementById(sel); const q1 = (el, sel) => !sel ? document.querySelector(el) : el.querySelector(sel); const isObj = (obj) => { var type = typeof obj; return type === 'object' && !!obj; }; const isSimple = (v) => {var type = typeof v; return type=== "number" || type === "string" || type === "boolean"; } const arise = (fn) => document.addEventListener('DOMContentLoaded', fn, false); const scrawl = (k, v) => localStorage.setItem(k, JSON.stringify(v)); const recall = (k) => JSON.parse(localStorage.getItem(k) || "null"); function setterFor(node) { if (["INPUT", "TEXTAREA"].indexOf(node.tagName) > -1) { return (val) => node.value = val; } else { return (val) => node.innerText = val; } } let templs = new Map(); function from(tempSel) { let toClone; if (templs.has(tempSel)) { toClone = templs.get(tempSel); } else { let n = q1(tempSel); if (n) { templs.set(tempSel, n); toClone = n; } else { throw tempSel + " failed"; } } return toClone.content.cloneNode(true); } function become(spell, el, config) { // TODO: Figure out a way for replaceChildren to work el.replaceChildren(); spell(el, config); } function spell(template, config) { return function(into, start) { const basis = from(template) const state = { vars: {}, dom: {}, setters: {} }; const interface = Object.create(spell); const recursionGuard = new Set(); for (let [k, v] of Object.entries(config)) { const makeProp = (el, initialValue) => { state.setters[k] = setterFor(el); Object.defineProperty(interface, k, { get() { return state.vars[k] }, set(v) { state.vars[k] = v; state.setters[k]((v || "") + ""); } }); interface[k] = initialValue; } if (typeof v === "string" && v) { const el = q1(basis, v); state.dom[k] = el; makeProp(el, start[k]); } else if (typeof v === "function") { interface[k] = v.bind(interface); } else if (Array.isArray(v)) { if (v.length <= 0) { throw new Error("Empty array not valid!"); } let el; if (typeof v[0] === "string") { el = q1(basis, v[0]); state.dom[k] = el; } else { throw new Error(`Selector for ${k} must be a string!`); } if (v.length == 1 && (typeof v[0]) == "string") { state.vars[k] = start[k]; makeProp(el, start[k]); } else if (v.length == 2 && (typeof v[0]) === "string" && isSimple(v[1])) { makeProp(el, start[k] || v[1]); } else if (v.length == 2 && (typeof v[0]) === "string" && isObj(v[1])) { makeProp(el, start[k]); const obj = v[1]; if (obj.t) { interface[k] = start[k] || obj.t; } if (obj.on) { for (const [evt, fn] of Object.entries(obj.on)) { let inner = fn.bind(interface); let handler = function() { if (recursionGuard.has(handler)) { return; } try { recursionGuard.add(handler); inner(...arguments); } finally { recursionGuard.delete(handler); } }; el.addEventListener(evt, handler); } } if (obj.init && typeof obj.init === "function") { obj.init.call(interface, start); } } else if (v.length === 3 && (typeof v[0]) === "string" && (typeof v[1]) === "function" && (typeof v[2]) === "function" && Array.isArray(start[k]) ) { // TODO: state.vars[k] = start[k].map((data) => v[1](el, v[2](data))); interface[k] = function(new_data) { el.replaceChildren(); state.vars[k] = new_data.map((data) => v[1](el, v[2](data))); } } else { throw new Error(`Invalid config: ${k}`); } } } interface._ = state; into.appendChild(basis); if (typeof interface.init === "function") { interface.init(start); } return interface; } } return {q, q1, id, from, arise, scrawl, recall, become, spell}; })(); window.hex = hex;