/** x0 * Lazy web pages. * * [x0-mods] - modules to load * [x0-module-root] - set module src root */ if (window['x0']) { throw new Error("window.x0 already declared.", window.x0); } class x0 { #rootElement; #cfg = { moduleSource: "./x0/modules/", modules: [], init: true } #hooks = { moduleRemove: {} } #moduleStorage = {}; constructor(rootElement = document.querySelector("html")) { this.#rootElement = rootElement; this.#updateConfig(); if (this.#cfg.init) this.#updateScriptElements(); } #getAttribute(name, level) { return this.#rootElement.getAttribute(`x${level ?? ''}-${name}`) } #updateConfig() { this.#cfg.moduleSource = this.#getAttribute("module-root", 0) ?? this.#cfg.moduleSource; this.#cfg.modules = this.#getAttribute("mods", 0)?.split(",") ?? this.#cfg.modules; this.#cfg.init = (this.#getAttribute("init", 0) ?? "true") == "true" } #updateScriptElements() { this.#cfg.modules .forEach(m => { if (this.#rootElement.querySelector(`[x0-m="${m}"]`)) return; let script = document.createElement("script"); script.src = this.#cfg.moduleSource + m + ".js"; script.setAttribute("x0-m", m); this.#rootElement.appendChild(script); }) this.#rootElement .querySelectorAll("[x0-m]") .forEach(e => { if (!this.#cfg.modules.includes(e.getAttribute("x0-m"))) { this.#removeModule(e.getAttribute("x0-m")); e.remove(); } }) } #removeModule(module) { if (this.#hooks.moduleRemove[module]) this.#hooks.moduleRemove[module](this.#moduleStorage[module] ?? {}); // run module remove hook if (this.#moduleStorage[module]) this.#moduleStorage[module] = undefined; } registerModuleRemoveHook(module, callback) { if (this.#hooks.moduleRemove[module]) return null; this.#hooks.moduleRemove[module] = callback; } /** registerStorage * Register a storage. * This can only happen once per module. */ registerStorage(module) { if (this.#moduleStorage[module]) return null; this.#moduleStorage[module] = {}; return this.#moduleStorage[module]; } update() { this.#updateConfig(); this.#updateScriptElements(); } } window.x0 = new x0();