UserUtils

Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more

当前为 2024-02-20 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/472956/1330699/UserUtils.js

  1. // ==UserScript==
  2. // @namespace https://github.com/Sv443-Network/UserUtils
  3. // @exclude *
  4. // @author Sv443
  5. // @supportURL https://github.com/Sv443-Network/UserUtils/issues
  6. // @homepageURL https://github.com/Sv443-Network/UserUtils#readme
  7. // @supportURL https://github.com/Sv443-Network/UserUtils/issues
  8.  
  9. // ==UserLibrary==
  10. // @name UserUtils
  11. // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more
  12. // @version 5.0.0
  13. // @license MIT
  14. // @copyright Sv443 (https://github.com/Sv443)
  15.  
  16. // ==/UserScript==
  17. // ==/UserLibrary==
  18.  
  19. // ==OpenUserJS==
  20. // @author Sv443
  21. // ==/OpenUserJS==
  22.  
  23. var UserUtils = (function (exports) {
  24. var __defProp = Object.defineProperty;
  25. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  26. var __hasOwnProp = Object.prototype.hasOwnProperty;
  27. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  28. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  29. var __spreadValues = (a, b) => {
  30. for (var prop in b || (b = {}))
  31. if (__hasOwnProp.call(b, prop))
  32. __defNormalProp(a, prop, b[prop]);
  33. if (__getOwnPropSymbols)
  34. for (var prop of __getOwnPropSymbols(b)) {
  35. if (__propIsEnum.call(b, prop))
  36. __defNormalProp(a, prop, b[prop]);
  37. }
  38. return a;
  39. };
  40. var __objRest = (source, exclude) => {
  41. var target = {};
  42. for (var prop in source)
  43. if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
  44. target[prop] = source[prop];
  45. if (source != null && __getOwnPropSymbols)
  46. for (var prop of __getOwnPropSymbols(source)) {
  47. if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
  48. target[prop] = source[prop];
  49. }
  50. return target;
  51. };
  52. var __publicField = (obj, key, value) => {
  53. __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  54. return value;
  55. };
  56. var __async = (__this, __arguments, generator) => {
  57. return new Promise((resolve, reject) => {
  58. var fulfilled = (value) => {
  59. try {
  60. step(generator.next(value));
  61. } catch (e) {
  62. reject(e);
  63. }
  64. };
  65. var rejected = (value) => {
  66. try {
  67. step(generator.throw(value));
  68. } catch (e) {
  69. reject(e);
  70. }
  71. };
  72. var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
  73. step((generator = generator.apply(__this, __arguments)).next());
  74. });
  75. };
  76.  
  77. // lib/math.ts
  78. function clamp(value, min, max) {
  79. return Math.max(Math.min(value, max), min);
  80. }
  81. function mapRange(value, range1min, range1max, range2min, range2max) {
  82. if (Number(range1min) === 0 && Number(range2min) === 0)
  83. return value * (range2max / range1max);
  84. return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
  85. }
  86. function randRange(...args) {
  87. let min, max;
  88. if (typeof args[0] === "number" && typeof args[1] === "number")
  89. [min, max] = args;
  90. else if (typeof args[0] === "number" && typeof args[1] !== "number") {
  91. min = 0;
  92. [max] = args;
  93. } else
  94. throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
  95. min = Number(min);
  96. max = Number(max);
  97. if (isNaN(min) || isNaN(max))
  98. return NaN;
  99. if (min > max)
  100. throw new TypeError(`Parameter "min" can't be bigger than "max"`);
  101. return Math.floor(Math.random() * (max - min + 1)) + min;
  102. }
  103. function randomId(length = 16, radix = 16) {
  104. const arr = new Uint8Array(length);
  105. crypto.getRandomValues(arr);
  106. return Array.from(
  107. arr,
  108. (v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
  109. ).join("");
  110. }
  111.  
  112. // lib/array.ts
  113. function randomItem(array) {
  114. return randomItemIndex(array)[0];
  115. }
  116. function randomItemIndex(array) {
  117. if (array.length === 0)
  118. return [void 0, void 0];
  119. const idx = randRange(array.length - 1);
  120. return [array[idx], idx];
  121. }
  122. function takeRandomItem(arr) {
  123. const [itm, idx] = randomItemIndex(arr);
  124. if (idx === void 0)
  125. return void 0;
  126. arr.splice(idx, 1);
  127. return itm;
  128. }
  129. function randomizeArray(array) {
  130. const retArray = [...array];
  131. if (array.length === 0)
  132. return retArray;
  133. for (let i = retArray.length - 1; i > 0; i--) {
  134. const j = Math.floor(randRange(0, 1e4) / 1e4 * (i + 1));
  135. [retArray[i], retArray[j]] = [retArray[j], retArray[i]];
  136. }
  137. return retArray;
  138. }
  139.  
  140. // lib/ConfigManager.ts
  141. var ConfigManager = class {
  142. /**
  143. * Creates an instance of ConfigManager to manage a user configuration that is cached in memory and persistently saved across sessions.
  144. * Supports migrating data from older versions of the configuration to newer ones and populating the cache with default data if no persistent data is found.
  145. *
  146. * ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
  147. * ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
  148. *
  149. * @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `config.defaultConfig`) - this should also be the type of the data format associated with the current `options.formatVersion`
  150. * @param options The options for this ConfigManager instance
  151. */
  152. constructor(options) {
  153. __publicField(this, "id");
  154. __publicField(this, "formatVersion");
  155. __publicField(this, "defaultConfig");
  156. __publicField(this, "cachedConfig");
  157. __publicField(this, "migrations");
  158. __publicField(this, "encodeData");
  159. __publicField(this, "decodeData");
  160. this.id = options.id;
  161. this.formatVersion = options.formatVersion;
  162. this.defaultConfig = options.defaultConfig;
  163. this.cachedConfig = options.defaultConfig;
  164. this.migrations = options.migrations;
  165. this.encodeData = options.encodeData;
  166. this.decodeData = options.decodeData;
  167. }
  168. /**
  169. * Loads the data saved in persistent storage into the in-memory cache and also returns it.
  170. * Automatically populates persistent storage with default data if it doesn't contain any data yet.
  171. * Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
  172. */
  173. loadData() {
  174. return __async(this, null, function* () {
  175. try {
  176. const gmData = yield GM.getValue(`_uucfg-${this.id}`, this.defaultConfig);
  177. let gmFmtVer = Number(yield GM.getValue(`_uucfgver-${this.id}`));
  178. if (typeof gmData !== "string") {
  179. yield this.saveDefaultData();
  180. return this.defaultConfig;
  181. }
  182. const isEncoded = yield GM.getValue(`_uucfgenc-${this.id}`, false);
  183. if (isNaN(gmFmtVer))
  184. yield GM.setValue(`_uucfgver-${this.id}`, gmFmtVer = this.formatVersion);
  185. let parsed = yield this.deserializeData(gmData, isEncoded);
  186. if (gmFmtVer < this.formatVersion && this.migrations)
  187. parsed = yield this.runMigrations(parsed, gmFmtVer);
  188. return this.cachedConfig = parsed;
  189. } catch (err) {
  190. console.warn("Error while loading config data, resetting it to the default value.", err);
  191. yield this.saveDefaultData();
  192. return this.defaultConfig;
  193. }
  194. });
  195. }
  196. /**
  197. * Returns a copy of the data from the in-memory cache.
  198. * Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
  199. */
  200. getData() {
  201. return this.deepCopy(this.cachedConfig);
  202. }
  203. /** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
  204. setData(data) {
  205. this.cachedConfig = data;
  206. const useEncoding = Boolean(this.encodeData && this.decodeData);
  207. return new Promise((resolve) => __async(this, null, function* () {
  208. yield Promise.all([
  209. GM.setValue(`_uucfg-${this.id}`, yield this.serializeData(data, useEncoding)),
  210. GM.setValue(`_uucfgver-${this.id}`, this.formatVersion),
  211. GM.setValue(`_uucfgenc-${this.id}`, useEncoding)
  212. ]);
  213. resolve();
  214. }));
  215. }
  216. /** Saves the default configuration data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
  217. saveDefaultData() {
  218. return __async(this, null, function* () {
  219. this.cachedConfig = this.defaultConfig;
  220. const useEncoding = Boolean(this.encodeData && this.decodeData);
  221. return new Promise((resolve) => __async(this, null, function* () {
  222. yield Promise.all([
  223. GM.setValue(`_uucfg-${this.id}`, yield this.serializeData(this.defaultConfig, useEncoding)),
  224. GM.setValue(`_uucfgver-${this.id}`, this.formatVersion),
  225. GM.setValue(`_uucfgenc-${this.id}`, useEncoding)
  226. ]);
  227. resolve();
  228. }));
  229. });
  230. }
  231. /**
  232. * Call this method to clear all persistently stored data associated with this ConfigManager instance.
  233. * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
  234. * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
  235. *
  236. * ⚠️ This requires the additional directive `@grant GM.deleteValue`
  237. */
  238. deleteConfig() {
  239. return __async(this, null, function* () {
  240. yield Promise.all([
  241. GM.deleteValue(`_uucfg-${this.id}`),
  242. GM.deleteValue(`_uucfgver-${this.id}`),
  243. GM.deleteValue(`_uucfgenc-${this.id}`)
  244. ]);
  245. });
  246. }
  247. /** Runs all necessary migration functions consecutively - may be overwritten in a subclass */
  248. runMigrations(oldData, oldFmtVer) {
  249. return __async(this, null, function* () {
  250. if (!this.migrations)
  251. return oldData;
  252. let newData = oldData;
  253. const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
  254. let lastFmtVer = oldFmtVer;
  255. for (const [fmtVer, migrationFunc] of sortedMigrations) {
  256. const ver = Number(fmtVer);
  257. if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
  258. try {
  259. const migRes = migrationFunc(newData);
  260. newData = migRes instanceof Promise ? yield migRes : migRes;
  261. lastFmtVer = oldFmtVer = ver;
  262. } catch (err) {
  263. console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
  264. yield this.saveDefaultData();
  265. return this.getData();
  266. }
  267. }
  268. }
  269. yield Promise.all([
  270. GM.setValue(`_uucfg-${this.id}`, yield this.serializeData(newData)),
  271. GM.setValue(`_uucfgver-${this.id}`, lastFmtVer),
  272. GM.setValue(`_uucfgenc-${this.id}`, Boolean(this.encodeData && this.decodeData))
  273. ]);
  274. return newData;
  275. });
  276. }
  277. /** Serializes the data using the optional this.encodeData() and returns it as a string */
  278. serializeData(data, useEncoding = true) {
  279. return __async(this, null, function* () {
  280. const stringData = JSON.stringify(data);
  281. if (!this.encodeData || !this.decodeData || !useEncoding)
  282. return stringData;
  283. const encRes = this.encodeData(stringData);
  284. if (encRes instanceof Promise)
  285. return yield encRes;
  286. return encRes;
  287. });
  288. }
  289. /** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
  290. deserializeData(data, useEncoding = true) {
  291. return __async(this, null, function* () {
  292. let decRes = this.decodeData && this.encodeData && useEncoding ? this.decodeData(data) : void 0;
  293. if (decRes instanceof Promise)
  294. decRes = yield decRes;
  295. return JSON.parse(decRes != null ? decRes : data);
  296. });
  297. }
  298. /** Copies a JSON-compatible object and loses its internal references */
  299. deepCopy(obj) {
  300. return JSON.parse(JSON.stringify(obj));
  301. }
  302. };
  303.  
  304. // lib/dom.ts
  305. function getUnsafeWindow() {
  306. try {
  307. return unsafeWindow;
  308. } catch (e) {
  309. return window;
  310. }
  311. }
  312. function insertAfter(beforeElement, afterElement) {
  313. var _a;
  314. (_a = beforeElement.parentNode) == null ? void 0 : _a.insertBefore(afterElement, beforeElement.nextSibling);
  315. return afterElement;
  316. }
  317. function addParent(element, newParent) {
  318. const oldParent = element.parentNode;
  319. if (!oldParent)
  320. throw new Error("Element doesn't have a parent node");
  321. oldParent.replaceChild(newParent, element);
  322. newParent.appendChild(element);
  323. return newParent;
  324. }
  325. function addGlobalStyle(style) {
  326. const styleElem = document.createElement("style");
  327. styleElem.innerHTML = style;
  328. document.head.appendChild(styleElem);
  329. return styleElem;
  330. }
  331. function preloadImages(srcUrls, rejects = false) {
  332. const promises = srcUrls.map((src) => new Promise((res, rej) => {
  333. const image = new Image();
  334. image.src = src;
  335. image.addEventListener("load", () => res(image));
  336. image.addEventListener("error", (evt) => rejects && rej(evt));
  337. }));
  338. return Promise.allSettled(promises);
  339. }
  340. function openInNewTab(href) {
  341. const openElem = document.createElement("a");
  342. Object.assign(openElem, {
  343. className: "userutils-open-in-new-tab",
  344. target: "_blank",
  345. rel: "noopener noreferrer",
  346. href
  347. });
  348. openElem.style.display = "none";
  349. document.body.appendChild(openElem);
  350. openElem.click();
  351. setTimeout(openElem.remove, 50);
  352. }
  353. function interceptEvent(eventObject, eventName, predicate = () => true) {
  354. if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
  355. Error.stackTraceLimit = 1e3;
  356. }
  357. (function(original) {
  358. eventObject.__proto__.addEventListener = function(...args) {
  359. var _a, _b;
  360. const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a = args[1]) == null ? void 0 : _a.handleEvent) != null ? _b : () => void 0;
  361. args[1] = function(...a) {
  362. if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
  363. return;
  364. else
  365. return origListener.apply(this, a);
  366. };
  367. original.apply(this, args);
  368. };
  369. })(eventObject.__proto__.addEventListener);
  370. }
  371. function interceptWindowEvent(eventName, predicate = () => true) {
  372. return interceptEvent(getUnsafeWindow(), eventName, predicate);
  373. }
  374. function isScrollable(element) {
  375. const { overflowX, overflowY } = getComputedStyle(element);
  376. return {
  377. vertical: (overflowY === "scroll" || overflowY === "auto") && element.scrollHeight > element.clientHeight,
  378. horizontal: (overflowX === "scroll" || overflowX === "auto") && element.scrollWidth > element.clientWidth
  379. };
  380. }
  381. function observeElementProp(element, property, callback) {
  382. const elementPrototype = Object.getPrototypeOf(element);
  383. if (elementPrototype.hasOwnProperty(property)) {
  384. const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property);
  385. Object.defineProperty(element, property, {
  386. get: function() {
  387. var _a;
  388. return (_a = descriptor == null ? void 0 : descriptor.get) == null ? void 0 : _a.apply(this, arguments);
  389. },
  390. set: function() {
  391. var _a;
  392. const oldValue = this[property];
  393. (_a = descriptor == null ? void 0 : descriptor.set) == null ? void 0 : _a.apply(this, arguments);
  394. const newValue = this[property];
  395. if (typeof callback === "function") {
  396. callback.bind(this, oldValue, newValue);
  397. }
  398. return newValue;
  399. }
  400. });
  401. }
  402. }
  403.  
  404. // lib/misc.ts
  405. function autoPlural(word, num) {
  406. if (Array.isArray(num) || num instanceof NodeList)
  407. num = num.length;
  408. return `${word}${num === 1 ? "" : "s"}`;
  409. }
  410. function pauseFor(time) {
  411. return new Promise((res) => {
  412. setTimeout(() => res(), time);
  413. });
  414. }
  415. function debounce(func, timeout = 300) {
  416. let timer;
  417. return function(...args) {
  418. clearTimeout(timer);
  419. timer = setTimeout(() => func.apply(this, args), timeout);
  420. };
  421. }
  422. function fetchAdvanced(_0) {
  423. return __async(this, arguments, function* (input, options = {}) {
  424. const { timeout = 1e4 } = options;
  425. let signalOpts = {}, id = void 0;
  426. if (timeout >= 0) {
  427. const controller = new AbortController();
  428. id = setTimeout(() => controller.abort(), timeout);
  429. signalOpts = { signal: controller.signal };
  430. }
  431. const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
  432. clearTimeout(id);
  433. return res;
  434. });
  435. }
  436. function insertValues(input, ...values) {
  437. return input.replace(/%\d/gm, (match) => {
  438. var _a, _b;
  439. const argIndex = Number(match.substring(1)) - 1;
  440. return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString();
  441. });
  442. }
  443. function compress(input, compressionFormat, outputType = "string") {
  444. return __async(this, null, function* () {
  445. const byteArray = typeof input === "string" ? new TextEncoder().encode(input) : input;
  446. const comp = new CompressionStream(compressionFormat);
  447. const writer = comp.writable.getWriter();
  448. writer.write(byteArray);
  449. writer.close();
  450. const buf = yield new Response(comp.readable).arrayBuffer();
  451. return outputType === "arrayBuffer" ? buf : ab2str(buf);
  452. });
  453. }
  454. function decompress(input, compressionFormat, outputType = "string") {
  455. return __async(this, null, function* () {
  456. const byteArray = typeof input === "string" ? str2ab(input) : input;
  457. const decomp = new DecompressionStream(compressionFormat);
  458. const writer = decomp.writable.getWriter();
  459. writer.write(byteArray);
  460. writer.close();
  461. const buf = yield new Response(decomp.readable).arrayBuffer();
  462. return outputType === "arrayBuffer" ? buf : new TextDecoder().decode(buf);
  463. });
  464. }
  465. function ab2str(buf) {
  466. return getUnsafeWindow().btoa(
  467. new Uint8Array(buf).reduce((data, byte) => data + String.fromCharCode(byte), "")
  468. );
  469. }
  470. function str2ab(str) {
  471. return Uint8Array.from(getUnsafeWindow().atob(str), (c) => c.charCodeAt(0));
  472. }
  473.  
  474. // lib/SelectorObserver.ts
  475. var SelectorObserver = class {
  476. constructor(baseElement, options = {}) {
  477. __publicField(this, "enabled", false);
  478. __publicField(this, "baseElement");
  479. __publicField(this, "observer");
  480. __publicField(this, "observerOptions");
  481. __publicField(this, "customOptions");
  482. __publicField(this, "listenerMap");
  483. this.baseElement = baseElement;
  484. this.listenerMap = /* @__PURE__ */ new Map();
  485. this.observer = new MutationObserver(() => this.checkAllSelectors());
  486. const _a = options, {
  487. defaultDebounce,
  488. disableOnNoListeners,
  489. enableOnAddListener
  490. } = _a, observerOptions = __objRest(_a, [
  491. "defaultDebounce",
  492. "disableOnNoListeners",
  493. "enableOnAddListener"
  494. ]);
  495. this.observerOptions = __spreadValues({
  496. childList: true,
  497. subtree: true
  498. }, observerOptions);
  499. this.customOptions = {
  500. defaultDebounce: defaultDebounce != null ? defaultDebounce : 0,
  501. disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
  502. enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
  503. };
  504. }
  505. checkAllSelectors() {
  506. for (const [selector, listeners] of this.listenerMap.entries())
  507. this.checkSelector(selector, listeners);
  508. }
  509. checkSelector(selector, listeners) {
  510. var _a;
  511. if (!this.enabled)
  512. return;
  513. const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
  514. if (!baseElement)
  515. return;
  516. const all = listeners.some((listener) => listener.all);
  517. const one = listeners.some((listener) => !listener.all);
  518. const allElements = all ? baseElement.querySelectorAll(selector) : null;
  519. const oneElement = one ? baseElement.querySelector(selector) : null;
  520. for (const options of listeners) {
  521. if (options.all) {
  522. if (allElements && allElements.length > 0) {
  523. options.listener(allElements);
  524. if (!options.continuous)
  525. this.removeListener(selector, options);
  526. }
  527. } else {
  528. if (oneElement) {
  529. options.listener(oneElement);
  530. if (!options.continuous)
  531. this.removeListener(selector, options);
  532. }
  533. }
  534. if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
  535. this.listenerMap.delete(selector);
  536. if (this.listenerMap.size === 0 && this.customOptions.disableOnNoListeners)
  537. this.disable();
  538. }
  539. }
  540. debounce(func, time) {
  541. let timeout;
  542. return function(...args) {
  543. clearTimeout(timeout);
  544. timeout = setTimeout(() => func.apply(this, args), time);
  545. };
  546. }
  547. /**
  548. * Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
  549. * @param selector The selector to observe
  550. * @param options Options for the selector observation
  551. * @param options.listener Gets called whenever the selector was found in the DOM
  552. * @param [options.all] Whether to use `querySelectorAll()` instead - default is false
  553. * @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
  554. * @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
  555. */
  556. addListener(selector, options) {
  557. options = __spreadValues({ all: false, continuous: false, debounce: 0 }, options);
  558. if (options.debounce && options.debounce > 0 || this.customOptions.defaultDebounce && this.customOptions.defaultDebounce > 0) {
  559. options.listener = this.debounce(
  560. options.listener,
  561. options.debounce || this.customOptions.defaultDebounce
  562. );
  563. }
  564. if (this.listenerMap.has(selector))
  565. this.listenerMap.get(selector).push(options);
  566. else
  567. this.listenerMap.set(selector, [options]);
  568. if (this.enabled === false && this.customOptions.enableOnAddListener)
  569. this.enable();
  570. this.checkSelector(selector, [options]);
  571. }
  572. /** Disables the observation of the child elements */
  573. disable() {
  574. if (!this.enabled)
  575. return;
  576. this.enabled = false;
  577. this.observer.disconnect();
  578. }
  579. /**
  580. * Enables or reenables the observation of the child elements.
  581. * @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
  582. * @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
  583. */
  584. enable(immediatelyCheckSelectors = true) {
  585. const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
  586. if (this.enabled || !baseElement)
  587. return false;
  588. this.enabled = true;
  589. this.observer.observe(baseElement, this.observerOptions);
  590. if (immediatelyCheckSelectors)
  591. this.checkAllSelectors();
  592. return true;
  593. }
  594. /** Returns whether the observation of the child elements is currently enabled */
  595. isEnabled() {
  596. return this.enabled;
  597. }
  598. /** Removes all listeners that have been registered with {@linkcode addListener()} */
  599. clearListeners() {
  600. this.listenerMap.clear();
  601. }
  602. /**
  603. * Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
  604. * @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
  605. */
  606. removeAllListeners(selector) {
  607. return this.listenerMap.delete(selector);
  608. }
  609. /**
  610. * Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
  611. * @returns Returns true when the listener was found and removed, false otherwise
  612. */
  613. removeListener(selector, options) {
  614. const listeners = this.listenerMap.get(selector);
  615. if (!listeners)
  616. return false;
  617. const index = listeners.indexOf(options);
  618. if (index > -1) {
  619. listeners.splice(index, 1);
  620. return true;
  621. }
  622. return false;
  623. }
  624. /** Returns all listeners that have been registered with {@linkcode addListener()} */
  625. getAllListeners() {
  626. return this.listenerMap;
  627. }
  628. /** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
  629. getListeners(selector) {
  630. return this.listenerMap.get(selector);
  631. }
  632. };
  633.  
  634. // lib/translation.ts
  635. var trans = {};
  636. var curLang;
  637. function tr(key, ...args) {
  638. var _a;
  639. if (!curLang)
  640. return key;
  641. const trText = (_a = trans[curLang]) == null ? void 0 : _a[key];
  642. if (!trText)
  643. return key;
  644. if (args.length > 0 && trText.match(/%\d/)) {
  645. return insertValues(trText, ...args);
  646. }
  647. return trText;
  648. }
  649. tr.addLanguage = (language, translations) => {
  650. trans[language] = translations;
  651. };
  652. tr.setLanguage = (language) => {
  653. curLang = language;
  654. };
  655. tr.getLanguage = () => {
  656. return curLang;
  657. };
  658.  
  659. exports.ConfigManager = ConfigManager;
  660. exports.SelectorObserver = SelectorObserver;
  661. exports.addGlobalStyle = addGlobalStyle;
  662. exports.addParent = addParent;
  663. exports.autoPlural = autoPlural;
  664. exports.clamp = clamp;
  665. exports.compress = compress;
  666. exports.debounce = debounce;
  667. exports.decompress = decompress;
  668. exports.fetchAdvanced = fetchAdvanced;
  669. exports.getUnsafeWindow = getUnsafeWindow;
  670. exports.insertAfter = insertAfter;
  671. exports.insertValues = insertValues;
  672. exports.interceptEvent = interceptEvent;
  673. exports.interceptWindowEvent = interceptWindowEvent;
  674. exports.isScrollable = isScrollable;
  675. exports.mapRange = mapRange;
  676. exports.observeElementProp = observeElementProp;
  677. exports.openInNewTab = openInNewTab;
  678. exports.pauseFor = pauseFor;
  679. exports.preloadImages = preloadImages;
  680. exports.randRange = randRange;
  681. exports.randomId = randomId;
  682. exports.randomItem = randomItem;
  683. exports.randomItemIndex = randomItemIndex;
  684. exports.randomizeArray = randomizeArray;
  685. exports.takeRandomItem = takeRandomItem;
  686. exports.tr = tr;
  687.  
  688. return exports;
  689.  
  690. })({});