yssWaitForNode

Be smart!

Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greasyfork.org/scripts/454354/1145153/yssWaitForNode.js

// ==UserScript==
// @name         yssWaitForNode
// @namespace    https://ysslang.com/
// @version      1.2.4.1
// @description  Be smart!
// @author       ysslang
// @match        *://*/*
// @supportURL   https://greasyfork.org/scripts/454354
// @run-at       document-start
// @grant        none
// ==/UserScript==

/* Done
- 去除返回元素中的重复元素, 避免重复执行; bugfix: 忘记加const了;
*/

class WaitForNode {
  #currentURL;
  #observerList = [];
  #waitList = [];

  constructor() { this.#currentURL = window.location.href; }

  #checkIfUrlMatch(matcher) {
    var result = false;
    if (matcher === undefined || matcher === null) result = false;
    if (typeof matcher === "string") result = new RegExp(matcher.trim()).test(this.#currentURL);
    if (matcher instanceof RegExp) result = matcher.test(this.#currentURL);
    return result;
  }

  #determineParentElement(arg) {
    var result = document;
    if (typeof arg === "string" && document.querySelector(arg)) result = document.querySelector(arg);
    if (arg instanceof Element) result = arg;
    return result;
  }

  #mergeOptions(options) {
    options = options || {};
    var result = {
      immediate: [options.immediate, options.imdt, true].find((e) => typeof (e) !== 'undefined'),
      recursive: [options.recursive, options.rcs, true].find((e) => typeof (e) !== 'undefined'),
      once: [options.once, false].find((e) => typeof (e) !== 'undefined'),
      subtree: [options.subtree, options.sbt, true].find((e) => typeof (e) !== 'undefined'),
      childList: [options.childList, options.cld, true].find((e) => typeof (e) !== 'undefined'),
      parentEl: this.#determineParentElement(options.parent),
    };
    return result;
  }

  #extractMatchedElements(mutations, selector, recursive) {
    const matchedElements = [];
    for (const { addedNodes } of mutations) {
      for (const node of addedNodes) {
        if (!node.tagName) continue;
        else if (node.matches(selector)) matchedElements.push(node);
        else if (recursive && node.firstElementChild) matchedElements.push(...node.querySelectorAll(selector));
      }
    }
    const result = [...new Set(matchedElements)]
    return result;
  }

  #on(selector, callback, options) {
    if (options.immediate) {
      [...options.parentEl.querySelectorAll(selector)].forEach(callback);
    }
    const observer = new MutationObserver((mutations) => {
      const elements = this.#extractMatchedElements(mutations, selector, options.recursive);
      elements.forEach(callback);
      if (elements && options.once) this.disconnect();
    });
    observer.observe(options.parentEl, { subtree: options.subtree, childList: options.childList, });
    this.#observerList.push(observer);
    window.x = observer;
    return observer;
  }

  #injectStyle(styleString) {
    const style = document.createElement('style');
    style.textContent = styleString;
    return document.head.append(style);
  }

  add(name, url, selector, callback, options) {
    if (url === '') url = ['.*'];
    const urls = Array.isArray(url) ? url : [url];
    if (!urls.some(this.#checkIfUrlMatch, this)) return;
    const opts = this.#mergeOptions(options);
    const observer = this.#on(selector, callback, opts);
    this.#waitList.push({ 'name': name, 'url': url, 'selector': selector, 'callback': callback, 'options': opts, 'observer': observer });
  }

  batchAdd(name, url, lists, para4, para5) {
    const callback = typeof para4 === 'function' ? para4 : (typeof para5 === 'function' ? para5 : undefined);
    const options = typeof para4 === 'object' ? para4 : (typeof para5 === 'object' ? para5 : undefined)
    lists.forEach((p) => {
      if (!p.find(pp => typeof pp === 'function')) {
        if (p.length === 1) this.add(name, url, p[0], callback, options);
        else if (p.length === 2) this.add(`${name}-${p[0]}`, url, p[1], callback, options);
      }
      else if (typeof p[1] === 'function') this.add(name, url, p[0], p[1], p[2] || options);
      else if (typeof p[2] === 'function') this.add(`${name}-${p[0]}`, url, p[1], p[2], p[3] || options);
      else if (typeof p[3] === 'function') this.add(`${name}-${p[0]}`, p[1], p[2], p[3], p[4] || options);
      else return;
    })
  }

  addCss(name, url, cssString) {
    if (url === '') url = ['.*'];
    const urls = Array.isArray(url) ? url : [url];
    if (!urls.some(this.#checkIfUrlMatch, this)) return;
    this.#injectStyle(cssString);
  }

  stopAll() {
    while (this.#observerList.length) this.#observerList.pop().disconnect();
  }
}

const WFN = new WaitForNode();