/** * Automatically appends an `s` to the passed `word`, if `num` is not equal to 1 * @param word A word in singular form, to auto-convert to plural * @param num If this is an array, the amount of items is used */ export function autoPlural(word: string, num: number | unknown[] | NodeList) { if(Array.isArray(num) || num instanceof NodeList) num = num.length; return `${word}${num === 1 ? "" : "s"}`; } /** Ensures the passed `value` always stays between `min` and `max` */ export function clamp(value: number, min: number, max: number) { return Math.max(Math.min(value, max), min); } /** Pauses async execution for the specified time in ms */ export function pauseFor(time: number) { return new Promise((res) => { setTimeout(res, time); }); } /** * Calls the passed `func` after the specified `timeout` in ms. * Any subsequent calls to this function will reset the timer and discard previous calls. */ export function debounce void, TArgs = any>(func: TFunc, timeout = 300) { // eslint-disable-line @typescript-eslint/no-explicit-any let timer: number | undefined; return function(...args: TArgs[]) { clearTimeout(timer); timer = setTimeout(() => func.apply(this, args), timeout) as unknown as number; }; } /** * Returns `unsafeWindow` if it is available (if `@grant unsafeWindow` is set), otherwise falls back to the regular `window` */ export function getUnsafeWindow() { try { // throws ReferenceError if the "@grant unsafeWindow" isn't present return unsafeWindow; } catch(e) { return window; } } /** * Inserts `afterNode` as a sibling just after the provided `beforeNode` * @returns Returns the `afterNode` */ export function insertAfter(beforeNode: HTMLElement, afterNode: HTMLElement) { beforeNode.parentNode?.insertBefore(afterNode, beforeNode.nextSibling); return afterNode; } /** Adds a parent container around the provided element - returns the new parent node */ export function addParent(element: HTMLElement, newParent: HTMLElement) { const oldParent = element.parentNode; if(!oldParent) throw new Error("Element doesn't have a parent node"); oldParent.replaceChild(newParent, element); newParent.appendChild(element); return newParent; } /** * Adds global CSS style through a `