forked from AbleOS/holey-bytes
96 lines
3 KiB
JavaScript
96 lines
3 KiB
JavaScript
|
//// @ts-check
|
||
|
|
||
|
if (window.location.hostname === 'localhost') {
|
||
|
let id; setInterval(async () => {
|
||
|
let new_id = await fetch('/hot-reload').then(reps => reps.text());
|
||
|
id ??= new_id;
|
||
|
if (id !== new_id) window.location.reload();
|
||
|
}, 300);
|
||
|
}
|
||
|
|
||
|
document.body.addEventListener('htmx:afterSwap', (ev) => {
|
||
|
wireUp(ev.target);
|
||
|
});
|
||
|
|
||
|
wireUp(document.body);
|
||
|
|
||
|
/** @param {HTMLElement} target */
|
||
|
function wireUp(target) {
|
||
|
execApply(target);
|
||
|
cacheInputs(target);
|
||
|
bindTextareaAutoResize(target);
|
||
|
}
|
||
|
|
||
|
/** @param {string} content @return {string} */
|
||
|
function fmtTimestamp(content) {
|
||
|
new Date(parseInt(content) * 1000).toLocaleString()
|
||
|
}
|
||
|
|
||
|
/** @param {HTMLElement} target */
|
||
|
function execApply(target) {
|
||
|
/**@type {HTMLElement}*/ let elem;
|
||
|
for (elem of target.querySelectorAll('[apply]')) {
|
||
|
const funcname = elem.getAttribute('apply');
|
||
|
elem.textContent = window[funcname](elem.textContent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @param {HTMLElement} target */
|
||
|
function bindTextareaAutoResize(target) {
|
||
|
/**@type {HTMLTextAreaElement}*/ let textarea;
|
||
|
for (textarea of target.querySelectorAll("textarea")) {
|
||
|
textarea.style.height = textarea.scrollHeight + "px";
|
||
|
textarea.style.overflowY = "hidden";
|
||
|
textarea.addEventListener("input", function() {
|
||
|
textarea.style.height = "auto";
|
||
|
textarea.style.height = textarea.scrollHeight + "px";
|
||
|
});
|
||
|
|
||
|
textarea.onkeydown = (ev) => {
|
||
|
const selecting = textarea.selectionStart !== textarea.selectionEnd;
|
||
|
|
||
|
if (ev.key === "Tab") {
|
||
|
ev.preventDefault();
|
||
|
const prevPos = textarea.selectionStart;
|
||
|
textarea.value = textarea.value.slice(0, textarea.selectionStart) +
|
||
|
' ' + textarea.value.slice(textarea.selectionEnd);
|
||
|
textarea.selectionStart = textarea.selectionEnd = prevPos + 4;
|
||
|
}
|
||
|
|
||
|
if (ev.key === "Backspace" && textarea.selectionStart != 0 && !selecting) {
|
||
|
let i = textarea.selectionStart, looped = false;
|
||
|
while (textarea.value.charCodeAt(--i) === ' '.charCodeAt(0)) looped = true;
|
||
|
if (textarea.value.charCodeAt(i) === '\n'.charCodeAt(0) && looped) {
|
||
|
ev.preventDefault();
|
||
|
let toDelete = (textarea.selectionStart - (i + 1)) % 4;
|
||
|
if (toDelete === 0) toDelete = 4;
|
||
|
const prevPos = textarea.selectionStart;
|
||
|
textarea.value = textarea.value.slice(0, textarea.selectionStart - toDelete) +
|
||
|
textarea.value.slice(textarea.selectionEnd);
|
||
|
textarea.selectionStart = textarea.selectionEnd = prevPos - toDelete;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @param {HTMLElement} target */
|
||
|
function cacheInputs(target) {
|
||
|
/**@type {HTMLFormElement}*/ let form;
|
||
|
for (form of target.querySelectorAll('form')) {
|
||
|
const path = form.getAttribute('hx-post') || form.getAttribute('hx-delete');
|
||
|
if (!path) {
|
||
|
console.warn('form does not have a hx-post or hx-delete attribute', form);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/**@type {HTMLInputElement}*/ let input;
|
||
|
for (input of form.elements) {
|
||
|
if ('password submit button'.includes(input.type)) continue;
|
||
|
const key = path + input.name;
|
||
|
input.value = localStorage.getItem(key) ?? '';
|
||
|
input.addEventListener("input", (ev) => localStorage.setItem(key, ev.target.value));
|
||
|
}
|
||
|
}
|
||
|
}
|