ts: format finally

formatted with biome, a config file is provided.
This commit is contained in:
Harvey Tindall
2025-12-08 20:38:30 +00:00
parent ca7c553147
commit 817107622a
29 changed files with 3956 additions and 2610 deletions
+162 -73
View File
@@ -1,6 +1,6 @@
declare var window: GlobalWindow;
import dateParser from "any-date-parser";
import { Temporal } from 'temporal-polyfill';
import { Temporal } from "temporal-polyfill";
export function toDateString(date: Date): string {
const locale = window.language || (window as any).navigator.userLanguage || window.navigator.language;
@@ -9,7 +9,7 @@ export function toDateString(date: Date): string {
let args1 = {};
let args2: Intl.DateTimeFormatOptions = {
hour: "2-digit",
minute: "2-digit"
minute: "2-digit",
};
if (t12 && t24) {
if (t12.checked) {
@@ -29,9 +29,9 @@ export const parseDateString = (value: string): ParsedDate => {
// Used just to tell use what fields the user passed.
attempt: dateParser.attempt(value),
// note Date.fromString is also provided by dateParser.
date: (Date as any).fromString(value) as Date
date: (Date as any).fromString(value) as Date,
};
if (("invalid" in (out.date as any))) {
if ("invalid" in (out.date as any)) {
out.invalid = true;
} else {
// getTimezoneOffset returns UTC - Timezone, so invert it to get distance from UTC -to- timezone.
@@ -40,7 +40,7 @@ export const parseDateString = (value: string): ParsedDate => {
// Month in Date objects is 0-based, so make our parsed date that way too
if ("month" in out.attempt) out.attempt.month -= 1;
return out;
}
};
// DateCountdown sets the given el's textContent to the time till the given date (unixSeconds), updating
// every minute. It returns the timeout, so it can be later removed with clearTimeout if desired.
@@ -53,14 +53,14 @@ export function DateCountdown(el: HTMLElement, unixSeconds: number): ReturnType<
let diff = now.until(then).round({
largestUnit: "years",
smallestUnit: "minutes",
relativeTo: nowPlain
relativeTo: nowPlain,
});
// FIXME: I'd really like this to be localized, but don't know of any nice solutions.
const fields = [diff.years, diff.months, diff.days, diff.hours, diff.minutes];
const abbrevs = ["y", "mo", "d", "h", "m"];
for (let i = 0; i < fields.length; i++) {
if (fields[i]) {
out += ""+fields[i] + abbrevs[i] + " ";
out += "" + fields[i] + abbrevs[i] + " ";
}
}
return out.slice(0, -1);
@@ -72,13 +72,20 @@ export function DateCountdown(el: HTMLElement, unixSeconds: number): ReturnType<
return setTimeout(update, 60000);
}
export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => {
export const _get = (
url: string,
data: Object,
onreadystatechange: (req: XMLHttpRequest) => void,
noConnectionError: boolean = false,
): void => {
let req = new XMLHttpRequest();
if (window.pages) { url = window.pages.Base + url; }
if (window.pages) {
url = window.pages.Base + url;
}
req.open("GET", url, true);
req.responseType = 'json';
req.responseType = "json";
req.setRequestHeader("Authorization", "Bearer " + window.token);
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
req.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
req.onreadystatechange = () => {
if (req.status == 0) {
if (!noConnectionError) window.notifications.connectionError();
@@ -93,11 +100,13 @@ export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHtt
export const _download = (url: string, fname: string): void => {
let req = new XMLHttpRequest();
if (window.pages) { url = window.pages.Base + url; }
if (window.pages) {
url = window.pages.Base + url;
}
req.open("GET", url, true);
req.responseType = 'blob';
req.responseType = "blob";
req.setRequestHeader("Authorization", "Bearer " + window.token);
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
req.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
req.onload = (e: Event) => {
let link = document.createElement("a") as HTMLAnchorElement;
link.href = URL.createObjectURL(req.response);
@@ -109,25 +118,38 @@ export const _download = (url: string, fname: string): void => {
export const _upload = (url: string, formData: FormData): void => {
let req = new XMLHttpRequest();
if (window.pages) { url = window.pages.Base + url; }
if (window.pages) {
url = window.pages.Base + url;
}
req.open("POST", url, true);
req.setRequestHeader("Authorization", "Bearer " + window.token);
// req.setRequestHeader('Content-Type', 'multipart/form-data');
req.send(formData);
};
export const _req = (method: string, url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => {
export const _req = (
method: string,
url: string,
data: Object,
onreadystatechange: (req: XMLHttpRequest) => void,
response?: boolean,
statusHandler?: (req: XMLHttpRequest) => void,
noConnectionError: boolean = false,
): void => {
let req = new XMLHttpRequest();
if (window.pages) { url = window.pages.Base + url; }
if (window.pages) {
url = window.pages.Base + url;
}
req.open(method, url, true);
if (response) {
req.responseType = 'json';
req.responseType = "json";
}
req.setRequestHeader("Authorization", "Bearer " + window.token);
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
req.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
req.onreadystatechange = () => {
if (statusHandler) { statusHandler(req); }
else if (req.status == 0) {
if (statusHandler) {
statusHandler(req);
} else if (req.status == 0) {
if (!noConnectionError) window.notifications.connectionError();
return;
} else if (req.status == 401) {
@@ -138,18 +160,46 @@ export const _req = (method: string, url: string, data: Object, onreadystatechan
req.send(JSON.stringify(data));
};
export const _post = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => _req("POST", url, data, onreadystatechange, response, statusHandler, noConnectionError);
export const _post = (
url: string,
data: Object,
onreadystatechange: (req: XMLHttpRequest) => void,
response?: boolean,
statusHandler?: (req: XMLHttpRequest) => void,
noConnectionError: boolean = false,
): void => _req("POST", url, data, onreadystatechange, response, statusHandler, noConnectionError);
export const _put = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => _req("PUT", url, data, onreadystatechange, response, statusHandler, noConnectionError);
export const _put = (
url: string,
data: Object,
onreadystatechange: (req: XMLHttpRequest) => void,
response?: boolean,
statusHandler?: (req: XMLHttpRequest) => void,
noConnectionError: boolean = false,
): void => _req("PUT", url, data, onreadystatechange, response, statusHandler, noConnectionError);
export const _patch = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, response?: boolean, statusHandler?: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => _req("PATCH", url, data, onreadystatechange, response, statusHandler, noConnectionError);
export const _patch = (
url: string,
data: Object,
onreadystatechange: (req: XMLHttpRequest) => void,
response?: boolean,
statusHandler?: (req: XMLHttpRequest) => void,
noConnectionError: boolean = false,
): void => _req("PATCH", url, data, onreadystatechange, response, statusHandler, noConnectionError);
export function _delete(url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void {
export function _delete(
url: string,
data: Object,
onreadystatechange: (req: XMLHttpRequest) => void,
noConnectionError: boolean = false,
): void {
let req = new XMLHttpRequest();
if (window.pages) { url = window.pages.Base + url; }
if (window.pages) {
url = window.pages.Base + url;
}
req.open("DELETE", url, true);
req.setRequestHeader("Authorization", "Bearer " + window.token);
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
req.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
req.onreadystatechange = () => {
if (req.status == 0) {
if (!noConnectionError) window.notifications.connectionError();
@@ -162,8 +212,8 @@ export function _delete(url: string, data: Object, onreadystatechange: (req: XML
req.send(JSON.stringify(data));
}
export function toClipboard (str: string) {
const el = document.createElement('textarea') as HTMLTextAreaElement;
export function toClipboard(str: string) {
const el = document.createElement("textarea") as HTMLTextAreaElement;
el.value = str;
el.readOnly = true;
el.style.position = "absolute";
@@ -193,45 +243,50 @@ export class notificationBox implements NotificationBox {
static baseClasses = ["aside", "flex", "flex-row", "justify-between", "gap-4"];
private _error = (message: string): HTMLElement => {
const noti = document.createElement('aside');
const noti = document.createElement("aside");
noti.classList.add(...notificationBox.baseClasses, "~critical", "@low", "notification-error");
let error = "";
if (window.lang) {
error = window.lang.strings("error") + ":"
error = window.lang.strings("error") + ":";
}
noti.innerHTML = `<div><strong>${error}</strong> ${message}</div>`;
const closeButton = document.createElement('span') as HTMLSpanElement;
const closeButton = document.createElement("span") as HTMLSpanElement;
closeButton.classList.add("button", "~critical", "@low");
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
closeButton.onclick = () => this._close(noti);
noti.classList.add("animate-slide-in");
noti.appendChild(closeButton);
return noti;
}
};
private _positive = (bold: string, message: string): HTMLElement => {
const noti = document.createElement('aside');
const noti = document.createElement("aside");
noti.classList.add(...notificationBox.baseClasses, "~positive", "@low", "notification-positive");
noti.innerHTML = `<div><strong>${bold}</strong> ${message}</div>`;
const closeButton = document.createElement('span') as HTMLSpanElement;
const closeButton = document.createElement("span") as HTMLSpanElement;
closeButton.classList.add("button", "~positive", "@low");
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
closeButton.onclick = () => this._close(noti);
closeButton.onclick = () => this._close(noti);
noti.classList.add("animate-slide-in");
noti.appendChild(closeButton);
return noti;
}
};
private _close = (noti: HTMLElement) => {
noti.classList.remove("animate-slide-in");
noti.classList.add("animate-slide-out");
noti.addEventListener(window.animationEvent, () => {
this._box.removeChild(noti);
}, false);
}
noti.addEventListener(
window.animationEvent,
() => {
this._box.removeChild(noti);
},
false,
);
};
connectionError = () => { this.customError("connectionError", window.lang.notif("errorConnection")); }
connectionError = () => {
this.customError("connectionError", window.lang.notif("errorConnection"));
};
customError = (type: string, message: string) => {
this._errorTypes[type] = this._errorTypes[type] || false;
@@ -245,9 +300,14 @@ export class notificationBox implements NotificationBox {
}
this._box.appendChild(noti);
this._errorTypes[type] = true;
setTimeout(() => { if (this._box.contains(noti)) { this._close(noti); this._errorTypes[type] = false; } }, this.timeout*1000);
}
setTimeout(() => {
if (this._box.contains(noti)) {
this._close(noti);
this._errorTypes[type] = false;
}
}, this.timeout * 1000);
};
customPositive = (type: string, bold: string, message: string) => {
this._positiveTypes[type] = this._positiveTypes[type] || false;
const noti = this._positive(bold, message);
@@ -260,10 +320,16 @@ export class notificationBox implements NotificationBox {
}
this._box.appendChild(noti);
this._positiveTypes[type] = true;
setTimeout(() => { if (this._box.contains(noti)) { this._close(noti); this._positiveTypes[type] = false; } }, this.timeout*1000);
}
setTimeout(() => {
if (this._box.contains(noti)) {
this._close(noti);
this._positiveTypes[type] = false;
}
}, this.timeout * 1000);
};
customSuccess = (type: string, message: string) => this.customPositive(type, window.lang.strings("success") + ":", message)
customSuccess = (type: string, message: string) =>
this.customPositive(type, window.lang.strings("success") + ":", message);
}
export const whichAnimationEvent = () => {
@@ -272,19 +338,23 @@ export const whichAnimationEvent = () => {
return "animationend";
}
return "webkitAnimationEnd";
}
};
export function toggleLoader(el: HTMLElement, small: boolean = true) {
if (el.classList.contains("loader")) {
el.classList.remove("loader");
el.classList.remove("loader-sm");
const dot = el.querySelector("span.dot");
if (dot) { dot.remove(); }
if (dot) {
dot.remove();
}
} else {
el.classList.add("loader");
if (small) { el.classList.add("loader-sm"); }
if (small) {
el.classList.add("loader-sm");
}
const dot = document.createElement("span") as HTMLSpanElement;
dot.classList.add("dot")
dot.classList.add("dot");
el.appendChild(dot);
}
}
@@ -293,9 +363,11 @@ export function addLoader(el: HTMLElement, small: boolean = true, relative: bool
if (el.classList.contains("loader")) return;
el.classList.add("loader");
if (relative) el.classList.add("rel");
if (small) { el.classList.add("loader-sm"); }
if (small) {
el.classList.add("loader-sm");
}
const dot = document.createElement("span") as HTMLSpanElement;
dot.classList.add("dot")
dot.classList.add("dot");
el.appendChild(dot);
}
@@ -305,7 +377,9 @@ export function removeLoader(el: HTMLElement, small: boolean = true) {
el.classList.remove("loader-sm");
el.classList.remove("rel");
const dot = el.querySelector("span.dot");
if (dot) { dot.remove(); }
if (dot) {
dot.remove();
}
}
}
@@ -329,7 +403,9 @@ export function insertText(textarea: HTMLTextAreaElement, text: string) {
}
export function bindManualDropdowns() {
const buttons = Array.from(document.getElementsByClassName("dropdown-manual-toggle") as HTMLCollectionOf<HTMLSpanElement>);
const buttons = Array.from(
document.getElementsByClassName("dropdown-manual-toggle") as HTMLCollectionOf<HTMLSpanElement>,
);
for (let button of buttons) {
const parent = button.closest(".dropdown.manual");
const display = parent.querySelector(".dropdown-display");
@@ -337,7 +413,7 @@ export function bindManualDropdowns() {
const mouseout = () => parent.classList.remove("selected");
button.addEventListener("mouseover", mousein);
button.addEventListener("mouseout", mouseout);
display.addEventListener("mouseover", mousein);
display.addEventListener("mouseover", mousein);
display.addEventListener("mouseout", mouseout);
button.onclick = () => {
parent.classList.add("selected");
@@ -346,7 +422,12 @@ export function bindManualDropdowns() {
display.removeEventListener("mouseout", mouseout);
};
const outerClickListener = (event: Event) => {
if (!(event.target instanceof HTMLElement && (display.contains(event.target) || button.contains(event.target)))) {
if (
!(
event.target instanceof HTMLElement &&
(display.contains(event.target) || button.contains(event.target))
)
) {
parent.classList.remove("selected");
document.removeEventListener("click", outerClickListener);
button.addEventListener("mouseout", mouseout);
@@ -372,20 +453,28 @@ export function unicodeB64Encode(s: string): string {
// Only allow running a function every n milliseconds.
// Source: Clément Prévost at https://stackoverflow.com/questions/27078285/simple-throttle-in-javascript
// function foo<T>(bar: T): T {
export function throttle (callback: () => void, limitMilliseconds: number): () => void {
var waiting = false; // Initially, we're not waiting
return function () { // We return a throttled function
if (!waiting) { // If we're not waiting
callback.apply(this, arguments); // Execute users function
waiting = true; // Prevent future invocations
setTimeout(function () { // After a period of time
waiting = false; // And allow future invocations
export function throttle(callback: () => void, limitMilliseconds: number): () => void {
var waiting = false; // Initially, we're not waiting
return function () {
// We return a throttled function
if (!waiting) {
// If we're not waiting
callback.apply(this, arguments); // Execute users function
waiting = true; // Prevent future invocations
setTimeout(function () {
// After a period of time
waiting = false; // And allow future invocations
}, limitMilliseconds);
}
}
};
}
export function SetupCopyButton(button: HTMLButtonElement, text: string | (() => string), baseClass?: string, notif?: string) {
export function SetupCopyButton(
button: HTMLButtonElement,
text: string | (() => string),
baseClass?: string,
notif?: string,
) {
if (!notif) notif = window.lang.strings("copied");
if (!baseClass) baseClass = "~info";
// script will probably turn this into multiple
@@ -395,8 +484,8 @@ export function SetupCopyButton(button: HTMLButtonElement, text: string | (() =>
button.title = window.lang.strings("copy");
const icon = document.createElement("i");
icon.classList.add("icon", "ri-file-copy-line");
button.appendChild(icon)
button.onclick = () => {
button.appendChild(icon);
button.onclick = () => {
if (typeof text === "string") {
toClipboard(text);
} else {