osu-web

Library to modify static and dynamic components of osu web pages

当前为 2023-08-31 提交的版本,查看 最新版本

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

  1. // ==UserScript==
  2. // @name osu-web
  3. // @namespace osu
  4. // @version 1.1.0
  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. const installedModules = require.c;
  72. for (const id in installedModules) {
  73. const exports = installedModules[id].exports;
  74. if (self.isNonEmptyObj(exports)) {
  75. this.modules[id] = exports;
  76. }
  77. }
  78. },
  79. },
  80. [[moduleId]],
  81. ];
  82. inject(entryPoint, data);
  83. }
  84. }
  85. return Webpack;
  86. }());
  87.  
  88. self.OsuWeb = self.OsuWeb || (function () {
  89. "use strict";
  90. let _instance;
  91. const _static = [];
  92. const _dynamic = [];
  93. const _before = {};
  94. const _after = {};
  95. const _keys = [];
  96. const appendStyle = (function () {
  97. const style = document.querySelector("#osu-web");
  98. if (!(style || this.style)) {
  99. this.style = document.createElement("style");
  100. this.style.id = "osu-web";
  101. document.head.append(this.style);
  102. }
  103. });
  104. const getTurboLinks = (function () {
  105. for (const id in this.webpack.modules) {
  106. const exports = this.webpack.modules[id];
  107. if ("controller" in exports) {
  108. this.turbolinks = exports;
  109. return;
  110. }
  111. }
  112. });
  113. const getReactModules = (function () {
  114. const reactModules = new Set();
  115. for (const id in this.webpack.modules) {
  116. const exports = this.webpack.modules[id];
  117. if ("__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED" in exports) {
  118. reactModules.add(exports);
  119. }
  120. }
  121. [this.React, this.ReactDOM] = reactModules;
  122. });
  123. const modify = ((obj, fn, key) => {
  124. const oldFn = obj[fn];
  125. obj[fn] = function () {
  126. beforeFn(key, arguments);
  127. const r = oldFn.apply(this, arguments);
  128. afterFn(key, arguments, r);
  129. return r;
  130. };
  131. });
  132. const beforeFn = ((key, args) => {
  133. const arr = _before[key] || [];
  134. for (const fn of arr) {
  135. fn(args);
  136. }
  137. });
  138. const afterFn = ((key, args, r) => {
  139. const arr = _after[key] || [];
  140. for (const fn of arr) {
  141. fn(args, r);
  142. }
  143. });
  144. const modifyFn = ((obj, fn, key, before, after) => {
  145. if (!(key in _keys)) {
  146. _keys.push(key);
  147. _before[key] = [];
  148. _after[key] = [];
  149. modify(obj, fn, key);
  150. }
  151. if (before) {
  152. _before[key].push(before);
  153. }
  154. if (after) {
  155. _after[key].push(after);
  156. }
  157. });
  158. const init = (function (body) {
  159. this.webpack = new self.Webpack();
  160. this.modifyFn = modifyFn;
  161. getTurboLinks.apply(this);
  162. getReactModules.apply(this);
  163. appendStyle.apply(this);
  164. for (const fn of _static) {
  165. fn.apply(this, body);
  166. }
  167. const controller = this.turbolinks.controller;
  168. modifyFn(controller, "render", "turbolinks.render", null, (args, r) => {
  169. for (const fn of _static) {
  170. fn.apply(this, r.newBody);
  171. }
  172. });
  173. for (const fn of _dynamic) {
  174. fn.apply(this);
  175. }
  176. });
  177. class OsuWeb {
  178. constructor(staticFn, dynamicFn) {
  179. if (staticFn) {
  180. _static.push(staticFn);
  181. }
  182. if (dynamicFn) {
  183. _dynamic.push(dynamicFn);
  184. }
  185. if (_instance) {
  186. console.log(_instance);
  187. return _instance;
  188. }
  189. _instance = this;
  190. self.loaded("html", document).then((html) => {
  191. self.loaded("body", html).then((body) => {
  192. init.apply(_instance, body);
  193. });
  194. });
  195. }
  196.  
  197. addStyle(css) {
  198. this.style.innerHTML += `\n${css}`;
  199. }
  200. }
  201. return OsuWeb;
  202. }());