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/1243370/osu-web.js
// ==UserScript==
// @name osu-web
// @namespace osu
// @version 1.0.7
// @description Library to modify static and dynamic components of osu web pages
// @author Magnus Cosmos
// @unwrap
// ==/UserScript==
// Utils
function isNonEmptyObj(obj) {
if (obj === null || (typeof obj !== "function" && typeof obj !== "object")) {
return false;
}
for (const _key in obj) {
return true;
}
return false;
}
// Classes
class webpack {
constructor() {
if (this.constructor == webpack) {
throw new Error("webpack class cannot be instantiated.");
}
this.loaded = false;
this.modules = {};
}
inject(entryPoint, data) {
try {
unsafeWindow[entryPoint].push(data);
} catch (e) {
try {
window[entryPoint].push(data);
} catch (err) {
throw new Error(`Injection failed: ${err.message}`);
}
}
}
}
// Based on `Webpack-module-crack` and `moduleRaid`
class Webpack extends webpack {
constructor(options) {
super();
if (this.loaded) {
return;
}
let { moduleId, chunkId, entryPoint } = options || {};
moduleId = moduleId || Math.random().toString(36).substring(2, 6);
chunkId = chunkId || Math.floor(101 + Math.random() * 899);
entryPoint = entryPoint || "webpackJsonp";
const data = [
[chunkId],
{
[moduleId]: (_module, _exports, require) => {
const installedModules = require.c;
for (const id in installedModules) {
const exports = installedModules[id].exports;
if (isNonEmptyObj(exports)) {
this.modules[id] = exports;
}
}
},
},
[[moduleId]],
];
this.inject(entryPoint, data);
this.loaded = true;
}
}
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 = [];
this.style = null;
}
init() {
this.webpack = new Webpack();
this.#getTurboLinks();
this.#getReactModules();
this.#appendStyle();
}
#appendStyle() {
const style = document.querySelector("#osu-web");
if (!(style || this.style)) {
this.style = document.createElement("style");
this.style.id = "osu-web";
document.head.append(this.style);
}
}
#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();
}
addStyle(css) {
this.style.innerHTML += `\n${css}`;
}
}