2023-12-24 00:32:43 +01:00
|
|
|
/** 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 {
|
2023-12-24 03:05:20 +01:00
|
|
|
#rootElement = document.querySelector("html");
|
2023-12-24 00:32:43 +01:00
|
|
|
#cfg = {
|
|
|
|
moduleSource: "./x0/modules/",
|
|
|
|
modules: [],
|
|
|
|
init: true
|
|
|
|
}
|
|
|
|
#hooks = {
|
2023-12-24 03:05:20 +01:00
|
|
|
moduleRemove: {},
|
|
|
|
moduleUpdate: {}
|
2023-12-24 00:32:43 +01:00
|
|
|
}
|
|
|
|
#moduleStorage = {};
|
|
|
|
|
2023-12-24 03:05:20 +01:00
|
|
|
constructor() {
|
2023-12-24 00:32:43 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-12-24 03:05:20 +01:00
|
|
|
registerModuleUpdateHook(module, callback) {
|
|
|
|
if (this.#hooks.moduleUpdate[module]) return null;
|
|
|
|
this.#hooks.moduleUpdate[module] = callback;
|
|
|
|
}
|
|
|
|
|
2023-12-24 00:32:43 +01:00
|
|
|
/** registerStorage
|
|
|
|
* Register a storage.
|
2023-12-24 20:40:28 +01:00
|
|
|
* This storage's lifetime is one page load.
|
|
|
|
* This can only happen once per module per page load.
|
2023-12-24 00:32:43 +01:00
|
|
|
*/
|
|
|
|
registerStorage(module) {
|
|
|
|
if (this.#moduleStorage[module]) return null;
|
|
|
|
this.#moduleStorage[module] = {};
|
|
|
|
return this.#moduleStorage[module];
|
|
|
|
}
|
|
|
|
|
|
|
|
update() {
|
|
|
|
this.#updateConfig();
|
|
|
|
this.#updateScriptElements();
|
2023-12-24 03:05:20 +01:00
|
|
|
Object.values(this.#hooks.moduleUpdate).forEach(f => f());
|
2023-12-24 00:32:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window.x0 = new x0();
|