NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @namespace https://github.com/Sv443-Network/UserUtils // @exclude * // @author Sv443 // @supportURL https://github.com/Sv443-Network/UserUtils/issues // @homepageURL https://github.com/Sv443-Network/UserUtils // ==UserLibrary== // @name UserUtils // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more // @version 8.3.3 // @license MIT // @copyright Sv443 (https://github.com/Sv443) // ==/UserScript== // ==/UserLibrary== // ==OpenUserJS== // @author Sv443 // ==/OpenUserJS== var UserUtils = (function (exports) { var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __publicField = (obj, key, value) => { __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); return value; }; var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // lib/math.ts function clamp(value, min, max) { return Math.max(Math.min(value, max), min); } function mapRange(value, range1min, range1max, range2min, range2max) { if (typeof range2min === "undefined" || range2max === void 0) { range2max = range1max; range2min = 0; range1max = range1min; range1min = 0; } if (Number(range1min) === 0 && Number(range2min) === 0) return value * (range2max / range1max); return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min; } function randRange(...args) { let min, max, enhancedEntropy = false; if (typeof args[0] === "number" && typeof args[1] === "number") [min, max] = args; else if (typeof args[0] === "number" && typeof args[1] !== "number") { min = 0; [max] = args; } else throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`); if (typeof args[2] === "boolean") enhancedEntropy = args[2]; else if (typeof args[1] === "boolean") enhancedEntropy = args[1]; min = Number(min); max = Number(max); if (isNaN(min) || isNaN(max)) return NaN; if (min > max) throw new TypeError(`Parameter "min" can't be bigger than "max"`); if (enhancedEntropy) { const uintArr = new Uint8Array(1); crypto.getRandomValues(uintArr); return Number(Array.from( uintArr, (v) => Math.round(mapRange(v, 0, 255, min, max)).toString(10).substring(0, 1) ).join("")); } else return Math.floor(Math.random() * (max - min + 1)) + min; } // lib/array.ts function randomItem(array) { return randomItemIndex(array)[0]; } function randomItemIndex(array) { if (array.length === 0) return [void 0, void 0]; const idx = randRange(array.length - 1); return [array[idx], idx]; } function takeRandomItem(arr) { const [itm, idx] = randomItemIndex(arr); if (idx === void 0) return void 0; arr.splice(idx, 1); return itm; } function randomizeArray(array) { const retArray = [...array]; if (array.length === 0) return retArray; for (let i = retArray.length - 1; i > 0; i--) { const j = Math.floor(randRange(0, 1e4) / 1e4 * (i + 1)); [retArray[i], retArray[j]] = [retArray[j], retArray[i]]; } return retArray; } // lib/colors.ts function hexToRgb(hex) { hex = (hex.startsWith("#") ? hex.slice(1) : hex).trim(); const a = hex.length === 8 || hex.length === 4 ? parseInt(hex.slice(-(hex.length / 4)), 16) / (hex.length === 8 ? 255 : 15) : void 0; if (!isNaN(Number(a))) hex = hex.slice(0, -(hex.length / 4)); if (hex.length === 3 || hex.length === 4) hex = hex.split("").map((c) => c + c).join(""); const bigint = parseInt(hex, 16); const r = bigint >> 16 & 255; const g = bigint >> 8 & 255; const b = bigint & 255; return [clamp(r, 0, 255), clamp(g, 0, 255), clamp(b, 0, 255), typeof a === "number" ? clamp(a, 0, 1) : void 0]; } function rgbToHex(red, green, blue, alpha, withHash = true, upperCase = false) { const toHexVal = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0")[upperCase ? "toUpperCase" : "toLowerCase"](); return `${withHash ? "#" : ""}${toHexVal(red)}${toHexVal(green)}${toHexVal(blue)}${alpha ? toHexVal(alpha * 255) : ""}`; } function lightenColor(color, percent, upperCase = false) { return darkenColor(color, percent * -1, upperCase); } function darkenColor(color, percent, upperCase = false) { var _a; color = color.trim(); const darkenRgb = (r2, g2, b2, percent2) => { r2 = Math.max(0, Math.min(255, r2 - r2 * percent2 / 100)); g2 = Math.max(0, Math.min(255, g2 - g2 * percent2 / 100)); b2 = Math.max(0, Math.min(255, b2 - b2 * percent2 / 100)); return [r2, g2, b2]; }; let r, g, b, a; const isHexCol = color.match(/^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/); if (isHexCol) [r, g, b, a] = hexToRgb(color); else if (color.startsWith("rgb")) { const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? void 0 : _a.map(Number); if (!rgbValues) throw new Error("Invalid RGB/RGBA color format"); [r, g, b, a] = rgbValues; } else throw new Error("Unsupported color format"); [r, g, b] = darkenRgb(r, g, b, percent); if (isHexCol) return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase); else if (color.startsWith("rgba")) return `rgba(${r}, ${g}, ${b}, ${a != null ? a : NaN})`; else if (color.startsWith("rgb")) return `rgb(${r}, ${g}, ${b})`; else throw new Error("Unsupported color format"); } // lib/dom.ts function getUnsafeWindow() { try { return unsafeWindow; } catch (e) { return window; } } function addParent(element, newParent) { 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; } function addGlobalStyle(style) { const styleElem = document.createElement("style"); setInnerHtmlUnsafe(styleElem, style); document.head.appendChild(styleElem); return styleElem; } function preloadImages(srcUrls, rejects = false) { const promises = srcUrls.map((src) => new Promise((res, rej) => { const image = new Image(); image.src = src; image.addEventListener("load", () => res(image)); image.addEventListener("error", (evt) => rejects && rej(evt)); })); return Promise.allSettled(promises); } function openInNewTab(href, background) { try { GM.openInTab(href, background); } catch (e) { const openElem = document.createElement("a"); Object.assign(openElem, { className: "userutils-open-in-new-tab", target: "_blank", rel: "noopener noreferrer", href }); openElem.style.display = "none"; document.body.appendChild(openElem); openElem.click(); setTimeout(openElem.remove, 50); } } function interceptEvent(eventObject, eventName, predicate = () => true) { var _a; if ((eventObject === window || eventObject === getUnsafeWindow()) && ((_a = GM == null ? void 0 : GM.info) == null ? void 0 : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey") throw new Error("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in."); Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100); if (isNaN(Error.stackTraceLimit)) Error.stackTraceLimit = 100; (function(original) { eventObject.__proto__.addEventListener = function(...args) { var _a2, _b; const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a2 = args[1]) == null ? void 0 : _a2.handleEvent) != null ? _b : () => void 0; args[1] = function(...a) { if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a)) return; else return origListener.apply(this, a); }; original.apply(this, args); }; })(eventObject.__proto__.addEventListener); } function interceptWindowEvent(eventName, predicate = () => true) { return interceptEvent(getUnsafeWindow(), eventName, predicate); } function isScrollable(element) { const { overflowX, overflowY } = getComputedStyle(element); return { vertical: (overflowY === "scroll" || overflowY === "auto") && element.scrollHeight > element.clientHeight, horizontal: (overflowX === "scroll" || overflowX === "auto") && element.scrollWidth > element.clientWidth }; } function observeElementProp(element, property, callback) { const elementPrototype = Object.getPrototypeOf(element); if (elementPrototype.hasOwnProperty(property)) { const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property); Object.defineProperty(element, property, { get: function() { var _a; return (_a = descriptor == null ? void 0 : descriptor.get) == null ? void 0 : _a.apply(this, arguments); }, set: function() { var _a; const oldValue = this[property]; (_a = descriptor == null ? void 0 : descriptor.set) == null ? void 0 : _a.apply(this, arguments); const newValue = this[property]; if (typeof callback === "function") { callback.bind(this, oldValue, newValue); } return newValue; } }); } } function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "center-top", includeRef = true) { var _a, _b; const siblings = [...(_b = (_a = refElement.parentNode) == null ? void 0 : _a.childNodes) != null ? _b : []]; const elemSiblIdx = siblings.indexOf(refElement); if (elemSiblIdx === -1) throw new Error("Element doesn't have a parent node"); if (refElementAlignment === "top") return [...siblings.slice(elemSiblIdx + Number(!includeRef), elemSiblIdx + siblingAmount + Number(!includeRef))]; else if (refElementAlignment.startsWith("center-")) { const halfAmount = (refElementAlignment === "center-bottom" ? Math.ceil : Math.floor)(siblingAmount / 2); const startIdx = Math.max(0, elemSiblIdx - halfAmount); const topOffset = Number(refElementAlignment === "center-top" && siblingAmount % 2 === 0 && includeRef); const btmOffset = Number(refElementAlignment === "center-bottom" && siblingAmount % 2 !== 0 && includeRef); const startIdxWithOffset = startIdx + topOffset + btmOffset; return [ ...siblings.filter((_, idx) => includeRef || idx !== elemSiblIdx).slice(startIdxWithOffset, startIdxWithOffset + siblingAmount) ]; } else if (refElementAlignment === "bottom") return [...siblings.slice(elemSiblIdx - siblingAmount + Number(includeRef), elemSiblIdx + Number(includeRef))]; return []; } var ttPolicy; function setInnerHtmlUnsafe(element, html) { var _a, _b, _c; if (!ttPolicy && typeof ((_a = window == null ? void 0 : window.trustedTypes) == null ? void 0 : _a.createPolicy) === "function") { ttPolicy = window.trustedTypes.createPolicy("_uu_set_innerhtml_unsafe", { createHTML: (unsafeHtml) => unsafeHtml }); } element.innerHTML = (_c = (_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html)) != null ? _c : html; return element; } // lib/crypto.ts function compress(input, compressionFormat, outputType = "string") { return __async(this, null, function* () { const byteArray = typeof input === "string" ? new TextEncoder().encode(input) : input; const comp = new CompressionStream(compressionFormat); const writer = comp.writable.getWriter(); writer.write(byteArray); writer.close(); const buf = yield new Response(comp.readable).arrayBuffer(); return outputType === "arrayBuffer" ? buf : ab2str(buf); }); } function decompress(input, compressionFormat, outputType = "string") { return __async(this, null, function* () { const byteArray = typeof input === "string" ? str2ab(input) : input; const decomp = new DecompressionStream(compressionFormat); const writer = decomp.writable.getWriter(); writer.write(byteArray); writer.close(); const buf = yield new Response(decomp.readable).arrayBuffer(); return outputType === "arrayBuffer" ? buf : new TextDecoder().decode(buf); }); } function ab2str(buf) { return getUnsafeWindow().btoa( new Uint8Array(buf).reduce((data, byte) => data + String.fromCharCode(byte), "") ); } function str2ab(str) { return Uint8Array.from(getUnsafeWindow().atob(str), (c) => c.charCodeAt(0)); } function computeHash(input, algorithm = "SHA-256") { return __async(this, null, function* () { let data; if (typeof input === "string") { const encoder = new TextEncoder(); data = encoder.encode(input); } else data = input; const hashBuffer = yield crypto.subtle.digest(algorithm, data); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join(""); return hashHex; }); } function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) { let arr = []; const caseArr = randomCase ? [0, 1] : [0]; if (enhancedEntropy) { const uintArr = new Uint8Array(length); crypto.getRandomValues(uintArr); arr = Array.from( uintArr, (v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1) ); } else { arr = Array.from( { length }, () => Math.floor(Math.random() * radix).toString(radix) ); } if (!arr.some((v) => /[a-zA-Z]/.test(v))) return arr.join(""); return arr.map((v) => caseArr[randRange(0, caseArr.length - 1, enhancedEntropy)] === 1 ? v.toUpperCase() : v).join(""); } // lib/DataStore.ts var DataStore = class { /** * Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions. * Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found. * * ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"` * ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData` * * @template TData The type of the data that is saved in persistent storage for the currently set format version (will be automatically inferred from `defaultData` if not provided) - **This has to be a JSON-compatible object!** (no undefined, circular references, etc.) * @param options The options for this DataStore instance */ constructor(options) { __publicField(this, "id"); __publicField(this, "formatVersion"); __publicField(this, "defaultData"); __publicField(this, "encodeData"); __publicField(this, "decodeData"); __publicField(this, "storageMethod"); __publicField(this, "cachedData"); __publicField(this, "migrations"); __publicField(this, "migrateIds", []); var _a; this.id = options.id; this.formatVersion = options.formatVersion; this.defaultData = options.defaultData; this.cachedData = options.defaultData; this.migrations = options.migrations; if (options.migrateIds) this.migrateIds = Array.isArray(options.migrateIds) ? options.migrateIds : [options.migrateIds]; this.storageMethod = (_a = options.storageMethod) != null ? _a : "GM"; this.encodeData = options.encodeData; this.decodeData = options.decodeData; } //#region public /** * Loads the data saved in persistent storage into the in-memory cache and also returns it. * Automatically populates persistent storage with default data if it doesn't contain any data yet. * Also runs all necessary migration functions if the data format has changed since the last time the data was saved. */ loadData() { return __async(this, null, function* () { try { if (this.migrateIds.length > 0) { yield this.migrateId(this.migrateIds); this.migrateIds = []; } const gmData = yield this.getValue(`_uucfg-${this.id}`, JSON.stringify(this.defaultData)); let gmFmtVer = Number(yield this.getValue(`_uucfgver-${this.id}`, NaN)); if (typeof gmData !== "string") { yield this.saveDefaultData(); return __spreadValues({}, this.defaultData); } const isEncoded = Boolean(yield this.getValue(`_uucfgenc-${this.id}`, false)); let saveData = false; if (isNaN(gmFmtVer)) { yield this.setValue(`_uucfgver-${this.id}`, gmFmtVer = this.formatVersion); saveData = true; } let parsed = yield this.deserializeData(gmData, isEncoded); if (gmFmtVer < this.formatVersion && this.migrations) parsed = yield this.runMigrations(parsed, gmFmtVer); if (saveData) yield this.setData(parsed); this.cachedData = __spreadValues({}, parsed); return this.cachedData; } catch (err) { console.warn("Error while parsing JSON data, resetting it to the default value.", err); yield this.saveDefaultData(); return this.defaultData; } }); } /** * Returns a copy of the data from the in-memory cache. * Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage). * @param deepCopy Whether to return a deep copy of the data (default: `false`) - only necessary if your data object is nested and may have a bigger performance impact if enabled */ getData(deepCopy = false) { return deepCopy ? this.deepCopy(this.cachedData) : __spreadValues({}, this.cachedData); } /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */ setData(data) { this.cachedData = data; const useEncoding = this.encodingEnabled(); return new Promise((resolve) => __async(this, null, function* () { yield Promise.all([ this.setValue(`_uucfg-${this.id}`, yield this.serializeData(data, useEncoding)), this.setValue(`_uucfgver-${this.id}`, this.formatVersion), this.setValue(`_uucfgenc-${this.id}`, useEncoding) ]); resolve(); })); } /** Saves the default data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */ saveDefaultData() { return __async(this, null, function* () { this.cachedData = this.defaultData; const useEncoding = this.encodingEnabled(); return new Promise((resolve) => __async(this, null, function* () { yield Promise.all([ this.setValue(`_uucfg-${this.id}`, yield this.serializeData(this.defaultData, useEncoding)), this.setValue(`_uucfgver-${this.id}`, this.formatVersion), this.setValue(`_uucfgenc-${this.id}`, useEncoding) ]); resolve(); })); }); } /** * Call this method to clear all persistently stored data associated with this DataStore instance. * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()} * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data. * * ⚠️ This requires the additional directive `@grant GM.deleteValue` if the storageMethod is left as the default of `"GM"` */ deleteData() { return __async(this, null, function* () { yield Promise.all([ this.deleteValue(`_uucfg-${this.id}`), this.deleteValue(`_uucfgver-${this.id}`), this.deleteValue(`_uucfgenc-${this.id}`) ]); }); } /** Returns whether encoding and decoding are enabled for this DataStore instance */ encodingEnabled() { return Boolean(this.encodeData && this.decodeData); } //#region migrations /** * Runs all necessary migration functions consecutively and saves the result to the in-memory cache and persistent storage and also returns it. * This method is automatically called by {@linkcode loadData()} if the data format has changed since the last time the data was saved. * Though calling this method manually is not necessary, it can be useful if you want to run migrations for special occasions like a user importing potentially outdated data that has been previously exported. * * If one of the migrations fails, the data will be reset to the default value if `resetOnError` is set to `true` (default). Otherwise, an error will be thrown and no data will be saved. */ runMigrations(oldData, oldFmtVer, resetOnError = true) { return __async(this, null, function* () { if (!this.migrations) return oldData; let newData = oldData; const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b)); let lastFmtVer = oldFmtVer; for (const [fmtVer, migrationFunc] of sortedMigrations) { const ver = Number(fmtVer); if (oldFmtVer < this.formatVersion && oldFmtVer < ver) { try { const migRes = migrationFunc(newData); newData = migRes instanceof Promise ? yield migRes : migRes; lastFmtVer = oldFmtVer = ver; } catch (err) { if (!resetOnError) throw new Error(`Error while running migration function for format version '${fmtVer}'`); console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err); yield this.saveDefaultData(); return this.getData(); } } } yield Promise.all([ this.setValue(`_uucfg-${this.id}`, yield this.serializeData(newData)), this.setValue(`_uucfgver-${this.id}`, lastFmtVer), this.setValue(`_uucfgenc-${this.id}`, this.encodingEnabled()) ]); return this.cachedData = __spreadValues({}, newData); }); } /** * Tries to migrate the currently saved persistent data from one or more old IDs to the ID set in the constructor. * If no data exist for the old ID(s), nothing will be done, but some time may still pass trying to fetch the non-existent data. */ migrateId(oldIds) { return __async(this, null, function* () { const ids = Array.isArray(oldIds) ? oldIds : [oldIds]; yield Promise.all(ids.map((id) => __async(this, null, function* () { const data = yield this.getValue(`_uucfg-${id}`, JSON.stringify(this.defaultData)); const fmtVer = Number(yield this.getValue(`_uucfgver-${id}`, NaN)); const isEncoded = Boolean(yield this.getValue(`_uucfgenc-${id}`, false)); if (data === void 0 || isNaN(fmtVer)) return; const parsed = yield this.deserializeData(data, isEncoded); yield Promise.allSettled([ this.setValue(`_uucfg-${this.id}`, yield this.serializeData(parsed)), this.setValue(`_uucfgver-${this.id}`, fmtVer), this.setValue(`_uucfgenc-${this.id}`, isEncoded), this.deleteValue(`_uucfg-${id}`), this.deleteValue(`_uucfgver-${id}`), this.deleteValue(`_uucfgenc-${id}`) ]); }))); }); } //#region serialization /** Serializes the data using the optional this.encodeData() and returns it as a string */ serializeData(data, useEncoding = true) { return __async(this, null, function* () { const stringData = JSON.stringify(data); if (!this.encodingEnabled() || !useEncoding) return stringData; const encRes = this.encodeData(stringData); if (encRes instanceof Promise) return yield encRes; return encRes; }); } /** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */ deserializeData(data, useEncoding = true) { return __async(this, null, function* () { let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : void 0; if (decRes instanceof Promise) decRes = yield decRes; return JSON.parse(decRes != null ? decRes : data); }); } //#region misc /** Copies a JSON-compatible object and loses all its internal references in the process */ deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } //#region storage /** Gets a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */ getValue(name, defaultValue) { return __async(this, null, function* () { var _a, _b; switch (this.storageMethod) { case "localStorage": return (_a = localStorage.getItem(name)) != null ? _a : defaultValue; case "sessionStorage": return (_b = sessionStorage.getItem(name)) != null ? _b : defaultValue; default: return GM.getValue(name, defaultValue); } }); } /** * Sets a value in persistent storage - can be overwritten in a subclass if you want to use something other than GM storage. * The default storage engines will stringify all passed values like numbers or booleans, so be aware of that. */ setValue(name, value) { return __async(this, null, function* () { switch (this.storageMethod) { case "localStorage": return localStorage.setItem(name, String(value)); case "sessionStorage": return sessionStorage.setItem(name, String(value)); default: return GM.setValue(name, String(value)); } }); } /** Deletes a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */ deleteValue(name) { return __async(this, null, function* () { switch (this.storageMethod) { case "localStorage": return localStorage.removeItem(name); case "sessionStorage": return sessionStorage.removeItem(name); default: return GM.deleteValue(name); } }); } }; // lib/DataStoreSerializer.ts var DataStoreSerializer = class { constructor(stores, options = {}) { __publicField(this, "stores"); __publicField(this, "options"); if (!getUnsafeWindow().crypto || !getUnsafeWindow().crypto.subtle) throw new Error("DataStoreSerializer has to run in a secure context (HTTPS)!"); this.stores = stores; this.options = __spreadValues({ addChecksum: true, ensureIntegrity: true }, options); } /** Calculates the checksum of a string */ calcChecksum(input) { return __async(this, null, function* () { return computeHash(input, "SHA-256"); }); } /** Serializes a DataStore instance */ serializeStore(storeInst) { return __async(this, null, function* () { const data = storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData()); const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : void 0; return { id: storeInst.id, data, formatVersion: storeInst.formatVersion, encoded: storeInst.encodingEnabled(), checksum }; }); } /** Serializes the data stores into a string */ serialize() { return __async(this, null, function* () { const serData = []; for (const store of this.stores) serData.push(yield this.serializeStore(store)); return JSON.stringify(serData); }); } /** * Deserializes the data exported via {@linkcode serialize()} and imports it into the DataStore instances. * Also triggers the migration process if the data format has changed. */ deserialize(serializedData) { return __async(this, null, function* () { const deserStores = JSON.parse(serializedData); for (const storeData of deserStores) { const storeInst = this.stores.find((s) => s.id === storeData.id); if (!storeInst) throw new Error(`DataStore instance with ID "${storeData.id}" not found! Make sure to provide it in the DataStoreSerializer constructor.`); if (this.options.ensureIntegrity && typeof storeData.checksum === "string") { const checksum = yield this.calcChecksum(storeData.data); if (checksum !== storeData.checksum) throw new Error(`Checksum mismatch for DataStore with ID "${storeData.id}"! Expected: ${storeData.checksum} Has: ${checksum}`); } const decodedData = storeData.encoded && storeInst.encodingEnabled() ? yield storeInst.decodeData(storeData.data) : storeData.data; if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion) yield storeInst.runMigrations(JSON.parse(decodedData), Number(storeData.formatVersion), false); else yield storeInst.setData(JSON.parse(decodedData)); } }); } /** * Loads the persistent data of the DataStore instances into the in-memory cache. * Also triggers the migration process if the data format has changed. * @returns Returns a PromiseSettledResult array with the results of each DataStore instance in the format `{ id: string, data: object }` */ loadStoresData() { return __async(this, null, function* () { return Promise.allSettled(this.stores.map( (store) => __async(this, null, function* () { return { id: store.id, data: yield store.loadData() }; }) )); }); } /** Resets the persistent data of the DataStore instances to their default values. */ resetStoresData() { return __async(this, null, function* () { return Promise.allSettled(this.stores.map((store) => store.saveDefaultData())); }); } /** * Deletes the persistent data of the DataStore instances. * Leaves the in-memory data untouched. */ deleteStoresData() { return __async(this, null, function* () { return Promise.allSettled(this.stores.map((store) => store.deleteData())); }); } }; // node_modules/nanoevents/index.js var createNanoEvents = () => ({ emit(event, ...args) { for (let i = 0, callbacks = this.events[event] || [], length = callbacks.length; i < length; i++) { callbacks[i](...args); } }, events: {}, on(event, cb) { var _a; ((_a = this.events)[event] || (_a[event] = [])).push(cb); return () => { var _a2; this.events[event] = (_a2 = this.events[event]) == null ? void 0 : _a2.filter((i) => cb !== i); }; } }); // lib/NanoEmitter.ts var NanoEmitter = class { constructor(options = {}) { __publicField(this, "events", createNanoEvents()); __publicField(this, "eventUnsubscribes", []); __publicField(this, "emitterOptions"); this.emitterOptions = __spreadValues({ publicEmit: false }, options); } /** Subscribes to an event - returns a function that unsubscribes the event listener */ on(event, cb) { let unsub; const unsubProxy = () => { if (!unsub) return; unsub(); this.eventUnsubscribes = this.eventUnsubscribes.filter((u) => u !== unsub); }; unsub = this.events.on(event, cb); this.eventUnsubscribes.push(unsub); return unsubProxy; } /** Subscribes to an event and calls the callback or resolves the Promise only once */ once(event, cb) { return new Promise((resolve) => { let unsub; const onceProxy = (...args) => { unsub(); cb == null ? void 0 : cb(...args); resolve(args); }; unsub = this.on(event, onceProxy); }); } /** Emits an event on this instance - Needs `publicEmit` to be set to true in the constructor! */ emit(event, ...args) { if (this.emitterOptions.publicEmit) { this.events.emit(event, ...args); return true; } return false; } /** Unsubscribes all event listeners */ unsubscribeAll() { for (const unsub of this.eventUnsubscribes) unsub(); this.eventUnsubscribes = []; } }; // lib/Dialog.ts var defaultDialogCss = `.uu-no-select { user-select: none; } .uu-dialog-bg { --uu-dialog-bg: #333333; --uu-dialog-bg-highlight: #252525; --uu-scroll-indicator-bg: rgba(10, 10, 10, 0.7); --uu-dialog-separator-color: #797979; --uu-dialog-border-radius: 10px; } .uu-dialog-bg { display: block; position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 5; background-color: rgba(0, 0, 0, 0.6); } .uu-dialog { --uu-calc-dialog-height: calc(min(100vh - 40px, var(--uu-dialog-height-max))); position: absolute; display: flex; flex-direction: column; width: calc(min(100% - 60px, var(--uu-dialog-width-max))); border-radius: var(--uu-dialog-border-radius); height: auto; max-height: var(--uu-calc-dialog-height); left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 6; color: #fff; background-color: var(--uu-dialog-bg); } .uu-dialog.align-top { top: 0; transform: translate(-50%, 40px); } .uu-dialog.align-bottom { top: 100%; transform: translate(-50%, -100%); } .uu-dialog-body { font-size: 1.5rem; padding: 20px; } .uu-dialog-body.small { padding: 15px; } #uu-dialog-opts { display: flex; flex-direction: column; position: relative; padding: 30px 0px; overflow-y: auto; } .uu-dialog-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; padding: 15px 20px 15px 20px; background-color: var(--uu-dialog-bg); border: 2px solid var(--uu-dialog-separator-color); border-style: none none solid none !important; border-radius: var(--uu-dialog-border-radius) var(--uu-dialog-border-radius) 0px 0px; } .uu-dialog-header.small { padding: 10px 15px; border-style: none none solid none !important; } .uu-dialog-header-pad { content: " "; min-height: 32px; } .uu-dialog-header-pad.small { min-height: 24px; } .uu-dialog-titlecont { display: flex; align-items: center; } .uu-dialog-titlecont-no-title { display: flex; justify-content: flex-end; align-items: center; } .uu-dialog-title { position: relative; display: inline-block; font-size: 22px; } .uu-dialog-close { cursor: pointer; } .uu-dialog-header-img, .uu-dialog-close { width: 32px; height: 32px; } .uu-dialog-header-img.small, .uu-dialog-close.small { width: 24px; height: 24px; } .uu-dialog-footer { font-size: 17px; text-decoration: underline; } .uu-dialog-footer.hidden { display: none; } .uu-dialog-footer-cont { margin-top: 6px; padding: 15px 20px; background: var(--uu-dialog-bg); background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, var(--uu-dialog-bg) 30%, var(--uu-dialog-bg) 100%); border: 2px solid var(--uu-dialog-separator-color); border-style: solid none none none !important; border-radius: 0px 0px var(--uu-dialog-border-radius) var(--uu-dialog-border-radius); } .uu-dialog-footer-buttons-cont button:not(:last-of-type) { margin-right: 15px; }`; exports.currentDialogId = null; var openDialogs = []; var defaultStrings = { closeDialogTooltip: "Click to close the dialog" }; var Dialog = class _Dialog extends NanoEmitter { constructor(options) { super(); /** Options passed to the dialog in the constructor */ __publicField(this, "options"); /** ID that gets added to child element IDs - has to be unique and conform to HTML ID naming rules! */ __publicField(this, "id"); /** Strings used in the dialog (used for translations) */ __publicField(this, "strings"); __publicField(this, "dialogOpen", false); __publicField(this, "dialogMounted", false); const _a = options, { strings } = _a, opts = __objRest(_a, ["strings"]); this.strings = __spreadValues(__spreadValues({}, defaultStrings), strings != null ? strings : {}); this.options = __spreadValues({ closeOnBgClick: true, closeOnEscPress: true, destroyOnClose: false, unmountOnClose: true, removeListenersOnDestroy: true, small: false, verticalAlign: "center" }, opts); this.id = opts.id; } //#region public /** Call after DOMContentLoaded to pre-render the dialog and invisibly mount it in the DOM */ mount() { return __async(this, null, function* () { var _a; if (this.dialogMounted) return; this.dialogMounted = true; if (!document.querySelector("style.uu-dialog-css")) addGlobalStyle((_a = this.options.dialogCss) != null ? _a : defaultDialogCss).classList.add("uu-dialog-css"); const bgElem = document.createElement("div"); bgElem.id = `uu-${this.id}-dialog-bg`; bgElem.classList.add("uu-dialog-bg"); if (this.options.closeOnBgClick) bgElem.ariaLabel = bgElem.title = this.getString("closeDialogTooltip"); bgElem.style.setProperty("--uu-dialog-width-max", `${this.options.width}px`); bgElem.style.setProperty("--uu-dialog-height-max", `${this.options.height}px`); bgElem.style.visibility = "hidden"; bgElem.style.display = "none"; bgElem.inert = true; bgElem.appendChild(yield this.getDialogContent()); document.body.appendChild(bgElem); this.attachListeners(bgElem); this.events.emit("render"); return bgElem; }); } /** Closes the dialog and clears all its contents (unmounts elements from the DOM) in preparation for a new rendering call */ unmount() { var _a; this.close(); this.dialogMounted = false; const clearSelectors = [ `#uu-${this.id}-dialog-bg`, `#uu-style-dialog-${this.id}` ]; for (const sel of clearSelectors) (_a = document.querySelector(sel)) == null ? void 0 : _a.remove(); this.events.emit("clear"); } /** Clears the DOM of the dialog and then renders it again */ remount() { return __async(this, null, function* () { this.unmount(); yield this.mount(); }); } /** * Opens the dialog - also mounts it if it hasn't been mounted yet * Prevents default action and immediate propagation of the passed event */ open(e) { return __async(this, null, function* () { var _a; e == null ? void 0 : e.preventDefault(); e == null ? void 0 : e.stopImmediatePropagation(); if (this.isOpen()) return; this.dialogOpen = true; if (openDialogs.includes(this.id)) throw new Error(`A dialog with the same ID of '${this.id}' already exists and is open!`); if (!this.isMounted()) yield this.mount(); const dialogBg = document.querySelector(`#uu-${this.id}-dialog-bg`); if (!dialogBg) return console.warn(`Couldn't find background element for dialog with ID '${this.id}'`); dialogBg.style.visibility = "visible"; dialogBg.style.display = "block"; dialogBg.inert = false; exports.currentDialogId = this.id; openDialogs.unshift(this.id); for (const dialogId of openDialogs) if (dialogId !== this.id) (_a = document.querySelector(`#uu-${dialogId}-dialog-bg`)) == null ? void 0 : _a.setAttribute("inert", "true"); document.body.classList.remove("uu-no-select"); document.body.setAttribute("inert", "true"); this.events.emit("open"); return dialogBg; }); } /** Closes the dialog - prevents default action and immediate propagation of the passed event */ close(e) { var _a, _b; e == null ? void 0 : e.preventDefault(); e == null ? void 0 : e.stopImmediatePropagation(); if (!this.isOpen()) return; this.dialogOpen = false; const dialogBg = document.querySelector(`#uu-${this.id}-dialog-bg`); if (!dialogBg) return console.warn(`Couldn't find background element for dialog with ID '${this.id}'`); dialogBg.style.visibility = "hidden"; dialogBg.style.display = "none"; dialogBg.inert = true; openDialogs.splice(openDialogs.indexOf(this.id), 1); exports.currentDialogId = (_a = openDialogs[0]) != null ? _a : null; if (exports.currentDialogId) (_b = document.querySelector(`#uu-${exports.currentDialogId}-dialog-bg`)) == null ? void 0 : _b.removeAttribute("inert"); if (openDialogs.length === 0) { document.body.classList.add("uu-no-select"); document.body.removeAttribute("inert"); } this.events.emit("close"); if (this.options.destroyOnClose) this.destroy(); else if (this.options.unmountOnClose) this.unmount(); } /** Returns true if the dialog is currently open */ isOpen() { return this.dialogOpen; } /** Returns true if the dialog is currently mounted */ isMounted() { return this.dialogMounted; } /** Clears the DOM of the dialog and removes all event listeners */ destroy() { this.unmount(); this.events.emit("destroy"); this.options.removeListenersOnDestroy && this.unsubscribeAll(); } //#region static /** Returns the ID of the top-most dialog (the dialog that has been opened last) */ static getCurrentDialogId() { return exports.currentDialogId; } /** Returns the IDs of all currently open dialogs, top-most first */ static getOpenDialogs() { return openDialogs; } //#region protected getString(key) { var _a; return (_a = this.strings[key]) != null ? _a : defaultStrings[key]; } /** Called once to attach all generic event listeners */ attachListeners(bgElem) { if (this.options.closeOnBgClick) { bgElem.addEventListener("click", (e) => { var _a; if (this.isOpen() && ((_a = e.target) == null ? void 0 : _a.id) === `uu-${this.id}-dialog-bg`) this.close(e); }); } if (this.options.closeOnEscPress) { document.body.addEventListener("keydown", (e) => { if (e.key === "Escape" && this.isOpen() && _Dialog.getCurrentDialogId() === this.id) this.close(e); }); } } //#region protected /** * Adds generic, accessible interaction listeners to the passed element. * All listeners have the default behavior prevented and stop propagation (for keyboard events only as long as the captured key is valid). * @param listenerOptions Provide a {@linkcode listenerOptions} object to configure the listeners */ onInteraction(elem, listener, listenerOptions) { const _a = listenerOptions != null ? listenerOptions : {}, { preventDefault = true, stopPropagation = true } = _a, listenerOpts = __objRest(_a, ["preventDefault", "stopPropagation"]); const interactionKeys = ["Enter", " ", "Space"]; const proxListener = (e) => { if (e instanceof KeyboardEvent) { if (interactionKeys.includes(e.key)) { preventDefault && e.preventDefault(); stopPropagation && e.stopPropagation(); } else return; } else if (e instanceof MouseEvent) { preventDefault && e.preventDefault(); stopPropagation && e.stopPropagation(); } (listenerOpts == null ? void 0 : listenerOpts.once) && e.type === "keydown" && elem.removeEventListener("click", proxListener, listenerOpts); (listenerOpts == null ? void 0 : listenerOpts.once) && e.type === "click" && elem.removeEventListener("keydown", proxListener, listenerOpts); listener(e); }; elem.addEventListener("click", proxListener, listenerOpts); elem.addEventListener("keydown", proxListener, listenerOpts); } /** Returns the dialog content element and all its children */ getDialogContent() { return __async(this, null, function* () { var _a, _b, _c, _d; const header = (_b = (_a = this.options).renderHeader) == null ? void 0 : _b.call(_a); const footer = (_d = (_c = this.options).renderFooter) == null ? void 0 : _d.call(_c); const dialogWrapperEl = document.createElement("div"); dialogWrapperEl.id = `uu-${this.id}-dialog`; dialogWrapperEl.classList.add("uu-dialog"); dialogWrapperEl.ariaLabel = dialogWrapperEl.title = ""; dialogWrapperEl.role = "dialog"; dialogWrapperEl.setAttribute("aria-labelledby", `uu-${this.id}-dialog-title`); dialogWrapperEl.setAttribute("aria-describedby", `uu-${this.id}-dialog-body`); if (this.options.verticalAlign !== "center") dialogWrapperEl.classList.add(`align-${this.options.verticalAlign}`); const headerWrapperEl = document.createElement("div"); headerWrapperEl.classList.add("uu-dialog-header"); this.options.small && headerWrapperEl.classList.add("small"); if (header) { const headerTitleWrapperEl = document.createElement("div"); headerTitleWrapperEl.id = `uu-${this.id}-dialog-title`; headerTitleWrapperEl.classList.add("uu-dialog-title-wrapper"); headerTitleWrapperEl.role = "heading"; headerTitleWrapperEl.ariaLevel = "1"; headerTitleWrapperEl.appendChild(header instanceof Promise ? yield header : header); headerWrapperEl.appendChild(headerTitleWrapperEl); } else { const padEl = document.createElement("div"); padEl.classList.add("uu-dialog-header-pad", this.options.small ? "small" : ""); headerWrapperEl.appendChild(padEl); } if (this.options.renderCloseBtn) { const closeBtnEl = yield this.options.renderCloseBtn(); closeBtnEl.classList.add("uu-dialog-close"); this.options.small && closeBtnEl.classList.add("small"); closeBtnEl.tabIndex = 0; if (closeBtnEl.hasAttribute("alt")) closeBtnEl.setAttribute("alt", this.getString("closeDialogTooltip")); closeBtnEl.title = closeBtnEl.ariaLabel = this.getString("closeDialogTooltip"); this.onInteraction(closeBtnEl, () => this.close()); headerWrapperEl.appendChild(closeBtnEl); } dialogWrapperEl.appendChild(headerWrapperEl); const dialogBodyElem = document.createElement("div"); dialogBodyElem.id = `uu-${this.id}-dialog-body`; dialogBodyElem.classList.add("uu-dialog-body"); this.options.small && dialogBodyElem.classList.add("small"); const body = this.options.renderBody(); dialogBodyElem.appendChild(body instanceof Promise ? yield body : body); dialogWrapperEl.appendChild(dialogBodyElem); if (footer) { const footerWrapper = document.createElement("div"); footerWrapper.classList.add("uu-dialog-footer-cont"); dialogWrapperEl.appendChild(footerWrapper); footerWrapper.appendChild(footer instanceof Promise ? yield footer : footer); } return dialogWrapperEl; }); } }; // lib/misc.ts function autoPlural(word, num) { if (Array.isArray(num) || num instanceof NodeList) num = num.length; return `${word}${num === 1 ? "" : "s"}`; } function insertValues(input, ...values) { return input.replace(/%\d/gm, (match) => { var _a, _b; const argIndex = Number(match.substring(1)) - 1; return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString(); }); } function pauseFor(time) { return new Promise((res) => { setTimeout(() => res(), time); }); } function debounce(func, timeout = 300, edge = "falling") { let id; return function(...args) { if (edge === "rising") { if (!id) { func.apply(this, args); id = setTimeout(() => id = void 0, timeout); } } else { clearTimeout(id); id = setTimeout(() => func.apply(this, args), timeout); } }; } function fetchAdvanced(_0) { return __async(this, arguments, function* (input, options = {}) { var _a; const { timeout = 1e4 } = options; const { signal, abort } = new AbortController(); (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", abort); let signalOpts = {}, id = void 0; if (timeout >= 0) { id = setTimeout(() => abort(), timeout); signalOpts = { signal }; } try { const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts)); id && clearTimeout(id); return res; } catch (err) { id && clearTimeout(id); throw err; } }); } // lib/SelectorObserver.ts var domLoaded = false; document.addEventListener("DOMContentLoaded", () => domLoaded = true); var SelectorObserver = class { constructor(baseElement, options = {}) { __publicField(this, "enabled", false); __publicField(this, "baseElement"); __publicField(this, "observer"); __publicField(this, "observerOptions"); __publicField(this, "customOptions"); __publicField(this, "listenerMap"); this.baseElement = baseElement; this.listenerMap = /* @__PURE__ */ new Map(); const _a = options, { defaultDebounce, defaultDebounceEdge, disableOnNoListeners, enableOnAddListener } = _a, observerOptions = __objRest(_a, [ "defaultDebounce", "defaultDebounceEdge", "disableOnNoListeners", "enableOnAddListener" ]); this.observerOptions = __spreadValues({ childList: true, subtree: true }, observerOptions); this.customOptions = { defaultDebounce: defaultDebounce != null ? defaultDebounce : 0, defaultDebounceEdge: defaultDebounceEdge != null ? defaultDebounceEdge : "rising", disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false, enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true }; if (typeof this.customOptions.checkInterval !== "number") { this.observer = new MutationObserver(() => this.checkAllSelectors()); } else { this.checkAllSelectors(); setInterval(() => this.checkAllSelectors(), this.customOptions.checkInterval); } } /** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */ checkAllSelectors() { if (!this.enabled || !domLoaded) return; for (const [selector, listeners] of this.listenerMap.entries()) this.checkSelector(selector, listeners); } /** Checks if the element(s) with the given {@linkcode selector} exist in the DOM and calls the respective {@linkcode listeners} accordingly */ checkSelector(selector, listeners) { var _a; if (!this.enabled) return; const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement; if (!baseElement) return; const all = listeners.some((listener) => listener.all); const one = listeners.some((listener) => !listener.all); const allElements = all ? baseElement.querySelectorAll(selector) : null; const oneElement = one ? baseElement.querySelector(selector) : null; for (const options of listeners) { if (options.all) { if (allElements && allElements.length > 0) { options.listener(allElements); if (!options.continuous) this.removeListener(selector, options); } } else { if (oneElement) { options.listener(oneElement); if (!options.continuous) this.removeListener(selector, options); } } if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0) this.listenerMap.delete(selector); if (this.listenerMap.size === 0 && this.customOptions.disableOnNoListeners) this.disable(); } } /** * Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options} * @param selector The selector to observe * @param options Options for the selector observation * @param options.listener Gets called whenever the selector was found in the DOM * @param [options.all] Whether to use `querySelectorAll()` instead - default is false * @param [options.continuous] Whether to call the listener continuously instead of just once - default is false * @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default) * @returns Returns a function that can be called to remove this listener more easily */ addListener(selector, options) { options = __spreadValues({ all: false, continuous: false, debounce: 0 }, options); if (options.debounce && options.debounce > 0 || this.customOptions.defaultDebounce && this.customOptions.defaultDebounce > 0) { options.listener = debounce( options.listener, options.debounce || this.customOptions.defaultDebounce, options.debounceEdge || this.customOptions.defaultDebounceEdge ); } if (this.listenerMap.has(selector)) this.listenerMap.get(selector).push(options); else this.listenerMap.set(selector, [options]); if (this.enabled === false && this.customOptions.enableOnAddListener) this.enable(); this.checkSelector(selector, [options]); return () => this.removeListener(selector, options); } /** Disables the observation of the child elements */ disable() { var _a; if (!this.enabled) return; this.enabled = false; (_a = this.observer) == null ? void 0 : _a.disconnect(); } /** * Enables or reenables the observation of the child elements. * @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true) * @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found) */ enable(immediatelyCheckSelectors = true) { var _a; const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement; if (this.enabled || !baseElement) return false; this.enabled = true; (_a = this.observer) == null ? void 0 : _a.observe(baseElement, this.observerOptions); if (immediatelyCheckSelectors) this.checkAllSelectors(); return true; } /** Returns whether the observation of the child elements is currently enabled */ isEnabled() { return this.enabled; } /** Removes all listeners that have been registered with {@linkcode addListener()} */ clearListeners() { this.listenerMap.clear(); } /** * Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} * @returns Returns true when all listeners for the associated selector were found and removed, false otherwise */ removeAllListeners(selector) { return this.listenerMap.delete(selector); } /** * Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()} * @returns Returns true when the listener was found and removed, false otherwise */ removeListener(selector, options) { const listeners = this.listenerMap.get(selector); if (!listeners) return false; const index = listeners.indexOf(options); if (index > -1) { listeners.splice(index, 1); return true; } return false; } /** Returns all listeners that have been registered with {@linkcode addListener()} */ getAllListeners() { return this.listenerMap; } /** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */ getListeners(selector) { return this.listenerMap.get(selector); } }; // lib/translation.ts var trans = {}; var curLang; var trLang = (language, key, ...args) => { var _a; if (!language) return key; const trText = (_a = trans[language]) == null ? void 0 : _a[key]; if (!trText) return key; if (args.length > 0 && trText.match(/%\d/)) { return insertValues(trText, ...args); } return trText; }; function tr(key, ...args) { return trLang(curLang, key, ...args); } tr.forLang = trLang; tr.addLanguage = (language, translations) => { trans[language] = translations; }; tr.setLanguage = (language) => { curLang = language; }; tr.getLanguage = () => { return curLang; }; tr.getTranslations = (language) => { return trans[language != null ? language : curLang]; }; exports.DataStore = DataStore; exports.DataStoreSerializer = DataStoreSerializer; exports.Dialog = Dialog; exports.NanoEmitter = NanoEmitter; exports.SelectorObserver = SelectorObserver; exports.addGlobalStyle = addGlobalStyle; exports.addParent = addParent; exports.autoPlural = autoPlural; exports.clamp = clamp; exports.compress = compress; exports.computeHash = computeHash; exports.darkenColor = darkenColor; exports.debounce = debounce; exports.decompress = decompress; exports.defaultDialogCss = defaultDialogCss; exports.defaultStrings = defaultStrings; exports.fetchAdvanced = fetchAdvanced; exports.getSiblingsFrame = getSiblingsFrame; exports.getUnsafeWindow = getUnsafeWindow; exports.hexToRgb = hexToRgb; exports.insertValues = insertValues; exports.interceptEvent = interceptEvent; exports.interceptWindowEvent = interceptWindowEvent; exports.isScrollable = isScrollable; exports.lightenColor = lightenColor; exports.mapRange = mapRange; exports.observeElementProp = observeElementProp; exports.openDialogs = openDialogs; exports.openInNewTab = openInNewTab; exports.pauseFor = pauseFor; exports.preloadImages = preloadImages; exports.randRange = randRange; exports.randomId = randomId; exports.randomItem = randomItem; exports.randomItemIndex = randomItemIndex; exports.randomizeArray = randomizeArray; exports.rgbToHex = rgbToHex; exports.setInnerHtmlUnsafe = setInnerHtmlUnsafe; exports.takeRandomItem = takeRandomItem; exports.tr = tr; return exports; })({});