Library to modify static and dynamic components of osu web pages
Fra og med
Dette script bør ikke installeres direkte. Det er et bibliotek, som andre scripts kan inkludere med metadirektivet // @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();
}
}