Library to modify static and dynamic components of osu web pages
Version vom
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/473977/1241412/osu-web.js
// ==UserScript==
// @name osu-web
// @namespace osu
// @version 1.0.0
// @description Library to modify static and dynamic components of osu web pages
// @author Magnus Cosmos
// ==/UserScript==
function loaded(selector, parent, callback, options = { childList: true }) {
const el = parent.querySelector(selector);
if (el) {
callback(el);
} else {
new MutationObserver(function (_mutations, observer) {
const el = parent.querySelector(selector);
if (el) {
callback(el);
observer = observer ? observer : this;
observer.disconnect();
}
}).observe(parent, options);
}
}
class Module {
constructor() {
if (this.constructor == Module) {
throw new Error("Module class cannot be instantiated.");
}
this.loaded = false;
this.static = [];
this.dynamic = [];
this.before = {};
this.after = {};
this.keys = [];
}
init() {
this.webpack = new Webpack();
this.#getTurboLinks();
this.#getReactModules();
}
#getTurboLinks() {
for (const id in this.webpack.modules) {
const exports = this.webpack.modules[id];
if ("controller" in exports) {
this.turbolinks = exports;
return;
}
}
}
#getReactModules() {
const reactModules = new Set();
for (const id in this.webpack.modules) {
const exports = this.webpack.modules[id];
if ("__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED" in exports) {
reactModules.add(exports);
}
}
[this.React, this.ReactDOM] = reactModules;
}
modifyFn(obj, fn, key, _before, _after) {
if (!(key in this.keys)) {
this.keys.push(key);
this.before[key] = [];
this.after[key] = [];
this.#modify(obj, fn, key);
}
if (_before) {
this.before[key].push(_before);
}
if (_after) {
this.after[key].push(_after);
}
}
#modify(obj, fn, key) {
const self = this;
const oldFn = obj[fn];
obj[fn] = function () {
self.#beforeFn(key, arguments);
const r = oldFn.apply(this, arguments);
self.#afterFn(key, arguments, r);
return r;
};
}
#beforeFn(key, args) {
const arr = this.before[key] || [];
for (const fn of arr) {
fn(args);
}
}
#afterFn(key, args, r) {
const arr = this.after[key] || [];
for (const fn of arr) {
fn(args, r);
}
}
}
class OsuWeb extends Module {
constructor(staticFn, dynamicFn) {
super();
this.static = staticFn || (() => {});
this.dynamic = dynamicFn || (() => {});
loaded("html", document, (html) => {
loaded("body", html, () => {
this.init();
this.start();
});
});
}
start() {
this.static(document.body);
const controller = this.turbolinks.controller;
this.modifyFn(controller, "render", "turbolinks.render", null, (args, r) => {
this.static(r.newBody);
});
this.dynamic();
}
}