const A = (...args) => { const classname = args[0].constructor.name; if (classname === 'String') { const c = document.querySelectorAll(args[0]); return new A._Collection(c); } if (classname === 'NodeList') { return new A._Collection(args[0]); } // TODO: lol this is such a hack if (classname.startsWith('HTML')) { return new A._Collection([args[0]]); } if (classname === 'Function') { return A.on('DOMContentLoaded', args[0]); } throw new Error(`Function "A" does not accept first argument of type ${typeof args[0]}`); }; A.on = (event, handler) => { document.addEventListener(event, handler); }; // --- Collection class --- A._Collection = class { constructor(list) { this._list = list; this.reactors = []; } }; A._Collection.prototype._forCollection = function (f) { for (let i = 0; i < this._list.length; i++) { f(this._list[i], i); } return this; }; A._Collection.prototype.html = function (html) { return this._forCollection((el, _) => { el.innerHTML = html; }); }; A._Collection.prototype.load = async function (url) { const res = await fetch(url); if (!res.ok) { console.error(`load(${url}) failed with code ${res.status}`); return; } const html = await res.text(); return this._forCollection((el, _) => { el.innerHTML = html; }); }; A._Collection.prototype.on = function (event, handler) { return this._forCollection((el, _) => { el.addEventListener(event, handler); }); }; A._Collection.prototype.each = function (handler) { return this._forCollection((el, i) => { handler(el, i); }); }; A._Collection.prototype.hide = function () { return this._forCollection((el, _) => { el.style.display = 'none'; }); }; A._Collection.prototype.show = function () { return this._forCollection((el, _) => { el.style.display = 'inline'; }); }; A._Collection.prototype.remove = function () { return this._forCollection((el, _) => { el.parentNode.removeChild(el); }); }; A._Collection.prototype.css = function (...args) { if (typeof args[0] === 'string' && typeof args[1] === 'string') { return this._forCollection((el, _) => { el.style[args[0]] = args[1]; }); } else if (typeof args[0] === 'object') { return this._forCollection((el, _) => { for (const [prop, value] of Object.entries(args[0])) { el.style[prop] = value; } }); } else { throw new Error(`Method "css" does not accept first argument of type ${typeof handler}`); } }; A._Collection.prototype.createReactor = function (values, watchData=undefined, elementIndex=undefined) { const el = this._list[elementIndex]; const reactor = new A._Reactor(values, el, watchData); this.reactors.push(reactor); return reactor; }; // --- Reactor class --- A._Reactor = class { constructor(values, element=undefined, watchData=undefined) { this.values = values; this._data = {}; this.element = element; if (watchData) { this.watch(watchData); } } }; A._Reactor.prototype.watch = function (value) { if (typeof value === 'object') { // Format of config object { valueThatChanged: 'propertytoupdate' } if (!this.element) { throw new Error(`Method "watch" requires that "element" argument must exist in its class when being passed an object as an argument`); } for (const [i, v] of Object.entries(value)) { Object.defineProperty(this.values, i, { set: (x) => { this._data[value] = x; if (typeof v === 'function') { v(x); } else if (typeof v === 'string') { if (v.startsWith('!')) { // Property this.element[v.substring(1)] = x; return; } if (v.startsWith('.')) { // Attribute this.element.setAttribute(v.substring(1), x); return; } } // TODO: Don't blindly trust that the property exists. this.element.setAttribute(v, x); } }); } } else { throw new Error(`Method "watch" does not accept value argument of type ${typeof value}`); } };