userscripts-core-library

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

От 24.09.2023. Виж последната версия.

Този скрипт не може да бъде инсталиран директно. Това е библиотека за други скриптове и може да бъде използвана с мета-директива // @require https://update.greasyfork.org/scripts/476017/1255617/userscripts-core-library.js

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==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;
  }
}