From 75bf105e980ac03848839cb34137d7242db764c4 Mon Sep 17 00:00:00 2001 From: Raine Date: Sun, 24 Dec 2023 00:32:43 +0100 Subject: [PATCH] init: init --- .gitignore | 1 + README.md | 30 +++++++++++++++++ modules/copy.js | 10 ++++++ modules/http.js | 24 +++++++++++++ test.html | 24 +++++++++++++ x0.js | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 modules/copy.js create mode 100644 modules/http.js create mode 100644 test.html create mode 100644 x0.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dbe9c82 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f655c1 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# x0 + +A web framework for *lazy* developers. + +## What can it do? + +It has some attributes that simplify stuff with js. + +## Attributes + +### Top level attributes + +These attributes only work on, and modify the root element (query selector: `html`). + +| attrib | description | default | +| ---------------- | ------------------------------- | --------------- | +| `x0-mods` | what modules to include | nothing | +| `x0-module-root` | where to fetch the modules from | `./x0/modules/` | +| `x0-init` | init x0? | `true` | + +### Element Attributes + +Attributes you can use + +| module | attrib | description | example | +| --------- | ---------------- | ----------------------------------------------------------- | ------------------------- | +| http | `x-get` | set the element's contents to the result of the get request | `x-get="./content"` | +| http | `x-get-interval` | use with `x-get` to poll the endpoint (millis) | `x-get-interval=1000` | +| http,copy | `x-inner-html` | instead of using innerHTML use innerText | `x-inner-html` | +| copy | `x-copy-query` | copy contents of element matching query selector | `x-copy-query="#content"` | \ No newline at end of file diff --git a/modules/copy.js b/modules/copy.js new file mode 100644 index 0000000..da2fea3 --- /dev/null +++ b/modules/copy.js @@ -0,0 +1,10 @@ +// [x-copy-query=""] +// [x-inner-text] - will use innerText +document.querySelectorAll("[x-copy-query]") + .forEach(e => { + if (e.hasAttribute("x-inner-text")) { + e.innerText = document.querySelector(e.getAttribute("x-copy-query")).innerHTML + } else { + e.innerHTML = document.querySelector(e.getAttribute("x-copy-query")).innerHTML + } + }) \ No newline at end of file diff --git a/modules/http.js b/modules/http.js new file mode 100644 index 0000000..d3e186b --- /dev/null +++ b/modules/http.js @@ -0,0 +1,24 @@ +// [x-get=""] +// [x-inner-text] - will use inner text instead of html +(() => { + let storage = window.x0?.registerStorage("http") || {}; + storage.intervals = []; + window.x0?.registerModuleRemoveHook("http", ({ intervals }) => intervals.forEach(p => clearInterval(p))); + + document.querySelectorAll("[x-get]") + .forEach(async e => { + e.innerHTML += "*"; + let update = async () => { + let result = await (await fetch(e.getAttribute("x-get"))).text(); + if (e.hasAttribute("x-inner-text")) { + e.innerText = result; + } else { + e.innerHTML = result; + } + } + await update() + if (e.hasAttribute("x-get-refresh")) { + storage.intervals.push(setInterval(update, e.getAttribute("x-get-refresh"))); + } + }) +})() \ No newline at end of file diff --git a/test.html b/test.html new file mode 100644 index 0000000..8fb1f29 --- /dev/null +++ b/test.html @@ -0,0 +1,24 @@ + + + +
+

Copy

+ +
+ Original content +
+
+
+
+

Get

+
+    
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/x0.js b/x0.js new file mode 100644 index 0000000..b477b13 --- /dev/null +++ b/x0.js @@ -0,0 +1,90 @@ +/** 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(); \ No newline at end of file