userscripts-core-library

Core library to handle webpages dom with userscripts from document-start

Version vom 24.09.2023. Aktuellste Version

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/476017/1255617/userscripts-core-library.js

// ==UserScript==
// @name        userscripts-core-library
// @version     0.1.0
// @author      lucianjp
// @description Core library to handle webpages dom with userscripts from document-start
// ==/UserScript==

const core = {
  ready: (callback) =>
    document.readyState !== "loading"
      ? callback()
      : document.addEventListener("DOMContentLoaded", callback),
  addStyle: (aCss) => {
    let head = document.getElementsByTagName("head")[0];
    if (!head) {
      console.error("Head element not found. Cannot add style.");
      return null;
    }

    let style = document.createElement("style");
    style.setAttribute("type", "text/css");
    style.textContent = aCss;
    head.appendChild(style);
    return style;
  },
  observe: (observableCollection, continuous = false) => {
    const observables = Array.from(observableCollection.entries()).filter(
      ([_, observable]) => !observable.currentValue
    );

    const observer = new MutationObserver(function (mutations) {
      for (var i = mutations.length - 1; i >= 0; i--) {
        const mutation = mutations[i];
        const addedNodesLength = mutation.addedNodes.length;
        if (addedNodesLength > 0) {
          for (var j = addedNodesLength - 1; j >= 0; j--) {
            const $node = mutation.addedNodes[j];
            if ($node && $node.nodeType === 1) {
              let observablesLength = observables.length;
              for (let k = observablesLength - 1; k >= 0; k--) {
                const [_, observable] = observables[k];

                if (observable.test($node)) {
                  observable.set($node);
                  const last = observables.pop();
                  if (k < observablesLength - 1) observables[k] = last;
                  observablesLength = observablesLength - 1;
                  break;
                }
              }
            }
          }

          if (observables.length === 0 && !continuous) {
            observer.disconnect();
            return;
          }
        }
      }
    });

    observer.observe(document, { childList: true, subtree: true });

    if (!continuous) core.ready(() => observer.disconnect());

    return observer;
  },
};

class Observable {
  constructor(lookup, test) {
    this.value = undefined;
    this.callbacks = [];
    this.lookup = lookup;
    this.test = test;

    if (typeof lookup === "function") {
      this.value = lookup();
    }
  }

  set(newValue) {
    this.value = newValue;
    this.executeCallbacks(this.value);
  }

  then(callback) {
    if (typeof callback === "function") {
      this.callbacks.push(callback);
      if (this.value) callback(this.value);
    }
    return this;
  }

  executeCallbacks(value) {
    this.callbacks.forEach((callback) => callback(value));
  }

  get currentValue() {
    return this.value;
  }
}

class ObservableCollection extends Map {
  constructor() {
    super();
  }

  add(name, observable) {
    this.set(name, observable);
    return observable;
  }
}