UserUtils

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

As of 2023-09-04. See the latest version.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greasyforks.org/scripts/472956/1245169/UserUtils.js

// ==UserScript==
// @name         UserUtils
// @description  Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, modify the DOM more easily and more 
// @namespace    https://github.com/Sv443-Network/UserUtils
// @version      0.5.3
// @license      MIT
// @author       Sv443
// @copyright    Sv443 (https://github.com/Sv443)
// @supportURL   https://github.com/Sv443-Network/UserUtils/issues
// @homepageURL  https://github.com/Sv443-Network/UserUtils#readme
// ==/UserScript==

var UserUtils = (function (exports) {
  var __defProp = Object.defineProperty;
  var __defProps = Object.defineProperties;
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
  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 __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
  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, range_1_min, range_1_max, range_2_min, range_2_max) {
    if (Number(range_1_min) === 0 && Number(range_2_min) === 0)
      return value * (range_2_max / range_1_max);
    return (value - range_1_min) * ((range_2_max - range_2_min) / (range_1_max - range_1_min)) + range_2_min;
  }
  function randRange(...args) {
    let min, max;
    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[0];
    } else
      throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
    min = Number(min);
    max = Number(max);
    if (isNaN(min) || isNaN(max))
      throw new TypeError(`Parameters "min" and "max" can't be NaN`);
    if (min > max)
      throw new TypeError(`Parameter "min" can't be bigger than "max"`);
    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 array;
    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/dom.ts
  function getUnsafeWindow() {
    try {
      return unsafeWindow;
    } catch (e) {
      return window;
    }
  }
  function insertAfter(beforeElement, afterElement) {
    var _a;
    (_a = beforeElement.parentNode) == null ? void 0 : _a.insertBefore(afterElement, beforeElement.nextSibling);
    return afterElement;
  }
  function addParent(element2, newParent) {
    const oldParent = element2.parentNode;
    if (!oldParent)
      throw new Error("Element doesn't have a parent node");
    oldParent.replaceChild(newParent, element2);
    newParent.appendChild(element2);
    return newParent;
  }
  function addGlobalStyle(style) {
    const styleElem = document.createElement("style");
    styleElem.innerHTML = style;
    document.head.appendChild(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) {
    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) {
    if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
      Error.stackTraceLimit = 1e3;
    }
    (function(original) {
      element.__proto__.addEventListener = function(...args) {
        if (args[0] === eventName && predicate())
          return;
        else
          return original.apply(this, args);
      };
    })(eventObject.__proto__.addEventListener);
  }
  function interceptWindowEvent(eventName, predicate) {
    return interceptEvent(getUnsafeWindow(), eventName, predicate);
  }
  function amplifyMedia(mediaElement, multiplier = 1) {
    const context = new (window.AudioContext || window.webkitAudioContext)();
    const result = {
      mediaElement,
      amplify: (multiplier2) => {
        result.gain.gain.value = multiplier2;
      },
      getAmpLevel: () => result.gain.gain.value,
      context,
      source: context.createMediaElementSource(mediaElement),
      gain: context.createGain()
    };
    result.source.connect(result.gain);
    result.gain.connect(context.destination);
    result.amplify(multiplier);
    return result;
  }

  // lib/misc.ts
  function autoPlural(word, num) {
    if (Array.isArray(num) || num instanceof NodeList)
      num = num.length;
    return `${word}${num === 1 ? "" : "s"}`;
  }
  function pauseFor(time) {
    return new Promise((res) => {
      setTimeout(() => res(), time);
    });
  }
  function debounce(func, timeout = 300) {
    let timer;
    return function(...args) {
      clearTimeout(timer);
      timer = setTimeout(() => func.apply(this, args), timeout);
    };
  }
  function fetchAdvanced(_0) {
    return __async(this, arguments, function* (url, options = {}) {
      const { timeout = 1e4 } = options;
      const controller = new AbortController();
      const id = setTimeout(() => controller.abort(), timeout);
      const res = yield fetch(url, __spreadProps(__spreadValues({}, options), {
        signal: controller.signal
      }));
      clearTimeout(id);
      return res;
    });
  }

  // lib/onSelector.ts
  var selectorMap = /* @__PURE__ */ new Map();
  function onSelector(selector, options) {
    let selectorMapItems = [];
    if (selectorMap.has(selector))
      selectorMapItems = selectorMap.get(selector);
    selectorMapItems.push(options);
    selectorMap.set(selector, selectorMapItems);
    checkSelectorExists(selector, selectorMapItems);
  }
  function removeOnSelector(selector) {
    return selectorMap.delete(selector);
  }
  function checkSelectorExists(selector, options) {
    const deleteIndices = [];
    options.forEach((option, i) => {
      try {
        const elements = option.all ? document.querySelectorAll(selector) : document.querySelector(selector);
        if (elements !== null && elements instanceof NodeList && elements.length > 0 || elements !== null) {
          option.listener(elements);
          if (!option.continuous)
            deleteIndices.push(i);
        }
      } catch (err) {
        console.error(`Couldn't call listener for selector '${selector}'`, err);
      }
    });
    if (deleteIndices.length > 0) {
      const newOptsArray = options.filter((_, i) => !deleteIndices.includes(i));
      if (newOptsArray.length === 0)
        selectorMap.delete(selector);
      else {
        selectorMap.set(selector, newOptsArray);
      }
    }
  }
  function initOnSelector(options = {}) {
    const observer = new MutationObserver(() => {
      for (const [selector, options2] of selectorMap.entries())
        checkSelectorExists(selector, options2);
    });
    observer.observe(document.body, __spreadValues({
      subtree: true,
      childList: true
    }, options));
  }
  function getSelectorMap() {
    return selectorMap;
  }

  exports.addGlobalStyle = addGlobalStyle;
  exports.addParent = addParent;
  exports.amplifyMedia = amplifyMedia;
  exports.autoPlural = autoPlural;
  exports.clamp = clamp;
  exports.debounce = debounce;
  exports.fetchAdvanced = fetchAdvanced;
  exports.getSelectorMap = getSelectorMap;
  exports.getUnsafeWindow = getUnsafeWindow;
  exports.initOnSelector = initOnSelector;
  exports.insertAfter = insertAfter;
  exports.interceptEvent = interceptEvent;
  exports.interceptWindowEvent = interceptWindowEvent;
  exports.mapRange = mapRange;
  exports.onSelector = onSelector;
  exports.openInNewTab = openInNewTab;
  exports.pauseFor = pauseFor;
  exports.preloadImages = preloadImages;
  exports.randRange = randRange;
  exports.randomItem = randomItem;
  exports.randomItemIndex = randomItemIndex;
  exports.randomizeArray = randomizeArray;
  exports.removeOnSelector = removeOnSelector;
  exports.takeRandomItem = takeRandomItem;

  return exports;

})({});
长期地址
遇到问题?请前往 GitHub 提 Issues。