init: init
This commit is contained in:
commit
75bf105e98
6 changed files with 179 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.vscode/
|
30
README.md
Normal file
30
README.md
Normal file
|
@ -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"` |
|
10
modules/copy.js
Normal file
10
modules/copy.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// [x-copy-query="<query selector>"]
|
||||||
|
// [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
|
||||||
|
}
|
||||||
|
})
|
24
modules/http.js
Normal file
24
modules/http.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// [x-get="<url>"]
|
||||||
|
// [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")));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})()
|
24
test.html
Normal file
24
test.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<html x0-mods="copy,http" x0-module-root="./modules/">
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<h2>Copy</h2>
|
||||||
|
|
||||||
|
<div id="ex">
|
||||||
|
<span>Original content</span>
|
||||||
|
</div>
|
||||||
|
<div x-inner-text x-copy-query="#ex"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Get</h2>
|
||||||
|
<pre x-inner-text x-get="./test.html" x-get-refresh=5000></>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button onclick="window.x0.update()">Update x0</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script src="./x0.js"></script>
|
||||||
|
|
||||||
|
</html>
|
90
x0.js
Normal file
90
x0.js
Normal file
|
@ -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();
|
Loading…
Reference in a new issue