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

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==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();