export function listen(...listenerSpecs) { return new Promise((resolve) => { let removeAllEventListeners; let listeners = listenerSpecs.map(([element, eventName]) => { let listener = (event) => { removeAllEventListeners(); resolve(event); }; element.addEventListener(eventName, listener); return { element, eventName, func: listener }; }); removeAllEventListeners = () => { for (let listener of listeners) { listener.element.removeEventListener(listener.eventName, listener.func); } }; }); } export function debounce(time, fn) { // This function is kind of tricky, but basically: // // - we want to guarantee `fn` is called at most once every `time` milliseconds // - at the same time, in case debounced `fn` is called during an ongoing timeout, we want to // queue up another run, and run it immediately after `time` passes // - at the same time, in case this catch-up condition occurs, we must also ensure there's a // delay after `fn` is called // // yielding the recursive solution below. let timeout = null; let queued = null; const callFn = (args) => { fn(...args); timeout = setTimeout(() => { timeout = null; if (queued != null) { callFn(queued); queued = null; } }, time); }; return (...args) => { if (timeout == null) { callFn(args); } else { queued = args; } }; } export class SaveData { constructor(prefix) { this.prefix = `rkgk.${prefix}`; this.elementId = ""; } #localStorageKey(key) { return `${this.prefix}.${this.elementId}.${key}`; } attachToElement(element) { this.elementId = element.id; } getRaw(key) { return localStorage.getItem(this.#localStorageKey(key)); } setRaw(key, value) { localStorage.setItem(this.#localStorageKey(key), value); } get(key, defaultValue) { let value = this.getRaw(key); if (value == null) { return defaultValue; } else { try { return JSON.parse(value); } catch (e) { throw new Error(`${this.#localStorageKey(key)}`, { cause: e }); } } } set(key, value) { this.setRaw(key, JSON.stringify(value)); } }