Library to modify static and dynamic components of osu web pages
Från och med
Detta skript bör inte installeras direkt. Det är ett bibliotek för andra skript att inkludera med meta-direktivet // @require https://updategf.qytechs.cn/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}`;
}
}