osu-web

Library to modify static and dynamic components of osu web pages

当前为 2023-09-12 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/473977/1249292/osu-web.js

  1. // ==UserScript==
  2. // @name osu-web
  3. // @namespace osu
  4. // @version 1.1.5
  5. // @description Library to modify static and dynamic components of osu web pages
  6. // @author Magnus Cosmos
  7. // ==/UserScript==
  8.  
  9. let self;
  10. try {
  11. self = unsafeWindow;
  12. } catch (err) {
  13. self = window;
  14. }
  15.  
  16. // Utils
  17. self.isNonEmptyObj = self.isNonEmptyObj || ((obj) => {
  18. if (obj === null || (typeof obj !== "function" && typeof obj !== "object")) {
  19. return false;
  20. }
  21. for (const _key in obj) {
  22. return true;
  23. }
  24. return false;
  25. });
  26.  
  27. self.loaded = self.loaded || ((selector, parent = document, options = { childList: true }) => {
  28. return new Promise((resolve) => {
  29. const el = parent.querySelector(selector);
  30. if (el) {
  31. resolve(el);
  32. } else {
  33. new MutationObserver(function (_mutations, observer) {
  34. const el = parent.querySelector(selector);
  35. if (el) {
  36. resolve(el);
  37. observer = observer ? observer : this;
  38. observer.disconnect();
  39. }
  40. }).observe(parent, options);
  41. }
  42. });
  43. });
  44.  
  45. // Classes
  46. self.Webpack = self.Webpack || (function () {
  47. "use strict";
  48. let _instance;
  49. const inject = (entryPoint, data) => {
  50. try {
  51. self[entryPoint].push(data);
  52. } catch (err) {
  53. throw new Error(`Injection failed: ${err.message}`);
  54. }
  55. }
  56. class Webpack {
  57. constructor(options) {
  58. if (_instance) {
  59. return _instance;
  60. }
  61. _instance = this;
  62. this.modules = {};
  63. let { moduleId, chunkId, entryPoint } = options || {};
  64. moduleId = moduleId || Math.random().toString(36).substring(2, 6);
  65. chunkId = chunkId || Math.floor(101 + Math.random() * 899);
  66. entryPoint = entryPoint || "webpackJsonp";
  67. const data = [
  68. [chunkId],
  69. {
  70. [moduleId]: (_module, _exports, require) => {
  71. for (const key of Object.keys(require.m)) {
  72. this.modules[key] = require(key);
  73. }
  74. },
  75. },
  76. (require) => {
  77. require(require.s = moduleId);
  78. },
  79. ];
  80. inject(entryPoint, data);
  81. }
  82. }
  83. return Webpack;
  84. }());
  85.  
  86. self.OsuWeb = self.OsuWeb || (function () {
  87. "use strict";
  88. let _instance;
  89. const _static = [];
  90. const _dynamic = [];
  91. const _before = {};
  92. const _after = {};
  93. const _keys = [];
  94. const appendStyle = (function () {
  95. const style = document.querySelector("#osu-web");
  96. if (!(style || this.style)) {
  97. this.style = document.createElement("style");
  98. this.style.id = "osu-web";
  99. document.head.append(this.style);
  100. }
  101. });
  102. const getTurboLinks = (function () {
  103. for (const id in this.webpack.modules) {
  104. const exports = this.webpack.modules[id];
  105. if ("controller" in exports) {
  106. this.turbolinks = exports;
  107. return;
  108. }
  109. }
  110. });
  111. const getReactModules = (function () {
  112. const reactModules = new Set();
  113. for (const id in this.webpack.modules) {
  114. const exports = this.webpack.modules[id];
  115. if ("__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED" in exports) {
  116. reactModules.add(exports);
  117. }
  118. }
  119. [this.React, this.ReactDOM] = reactModules;
  120. });
  121. const modify = ((obj, fn, key) => {
  122. const oldFn = obj[fn];
  123. obj[fn] = function () {
  124. beforeFn(key, arguments);
  125. const r = oldFn.apply(this, arguments);
  126. afterFn(key, arguments, r);
  127. return r;
  128. };
  129. });
  130. const beforeFn = ((key, args) => {
  131. const arr = _before[key] || [];
  132. for (const fn of arr) {
  133. fn(args);
  134. }
  135. });
  136. const afterFn = ((key, args, r) => {
  137. const arr = _after[key] || [];
  138. for (const fn of arr) {
  139. fn(args, r);
  140. }
  141. });
  142. const modifyFn = ((obj, fn, key, before, after) => {
  143. if (!_keys.includes(key)) {
  144. _keys.push(key);
  145. _before[key] = [];
  146. _after[key] = [];
  147. modify(obj, fn, key);
  148. }
  149. if (before) {
  150. _before[key].push(before);
  151. }
  152. if (after) {
  153. _after[key].push(after);
  154. }
  155. });
  156. const init = (function (body) {
  157. this.webpack = new self.Webpack({entryPoint: "webpackChunk"});
  158. this.modifyFn = modifyFn;
  159. getTurboLinks.call(this);
  160. getReactModules.call(this);
  161. appendStyle.call(this);
  162. for (const fn of _static) {
  163. fn.call(this, body);
  164. }
  165. const controller = this.turbolinks.controller;
  166. modifyFn(controller, "render", "turbolinks.render", null, (args, r) => {
  167. for (const fn of _static) {
  168. fn.call(this, r.newBody);
  169. }
  170. });
  171. for (const fn of _dynamic) {
  172. fn.call(this);
  173. }
  174. });
  175. class OsuWeb {
  176. constructor(staticFn, dynamicFn) {
  177. if (staticFn) {
  178. _static.push(staticFn);
  179. }
  180. if (dynamicFn) {
  181. _dynamic.push(dynamicFn);
  182. }
  183. if (_instance) {
  184. return _instance;
  185. }
  186. _instance = this;
  187. self.loaded("html", document).then((html) => {
  188. self.loaded("body", html).then((body) => {
  189. init.call(_instance, body);
  190. });
  191. });
  192. }
  193.  
  194. addStyle(css) {
  195. this.style.innerHTML += `\n${css}`;
  196. }
  197. }
  198. return OsuWeb;
  199. }());