UserUtils

Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, modify the DOM more easily and more

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

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

  1. // ==UserScript==
  2. // @name UserUtils
  3. // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, modify the DOM more easily and more
  4. // @namespace https://github.com/Sv443-Network/UserUtils
  5. // @version 0.5.3
  6. // @license MIT
  7. // @author Sv443
  8. // @copyright Sv443 (https://github.com/Sv443)
  9. // @supportURL https://github.com/Sv443-Network/UserUtils/issues
  10. // @homepageURL https://github.com/Sv443-Network/UserUtils#readme
  11. // ==/UserScript==
  12.  
  13. var UserUtils = (function (exports) {
  14. var __defProp = Object.defineProperty;
  15. var __defProps = Object.defineProperties;
  16. var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
  17. var __getOwnPropSymbols = Object.getOwnPropertySymbols;
  18. var __hasOwnProp = Object.prototype.hasOwnProperty;
  19. var __propIsEnum = Object.prototype.propertyIsEnumerable;
  20. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  21. var __spreadValues = (a, b) => {
  22. for (var prop in b || (b = {}))
  23. if (__hasOwnProp.call(b, prop))
  24. __defNormalProp(a, prop, b[prop]);
  25. if (__getOwnPropSymbols)
  26. for (var prop of __getOwnPropSymbols(b)) {
  27. if (__propIsEnum.call(b, prop))
  28. __defNormalProp(a, prop, b[prop]);
  29. }
  30. return a;
  31. };
  32. var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
  33. var __async = (__this, __arguments, generator) => {
  34. return new Promise((resolve, reject) => {
  35. var fulfilled = (value) => {
  36. try {
  37. step(generator.next(value));
  38. } catch (e) {
  39. reject(e);
  40. }
  41. };
  42. var rejected = (value) => {
  43. try {
  44. step(generator.throw(value));
  45. } catch (e) {
  46. reject(e);
  47. }
  48. };
  49. var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
  50. step((generator = generator.apply(__this, __arguments)).next());
  51. });
  52. };
  53.  
  54. // lib/math.ts
  55. function clamp(value, min, max) {
  56. return Math.max(Math.min(value, max), min);
  57. }
  58. function mapRange(value, range_1_min, range_1_max, range_2_min, range_2_max) {
  59. if (Number(range_1_min) === 0 && Number(range_2_min) === 0)
  60. return value * (range_2_max / range_1_max);
  61. return (value - range_1_min) * ((range_2_max - range_2_min) / (range_1_max - range_1_min)) + range_2_min;
  62. }
  63. function randRange(...args) {
  64. let min, max;
  65. if (typeof args[0] === "number" && typeof args[1] === "number") {
  66. [min, max] = args;
  67. } else if (typeof args[0] === "number" && typeof args[1] !== "number") {
  68. min = 0;
  69. max = args[0];
  70. } else
  71. throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
  72. min = Number(min);
  73. max = Number(max);
  74. if (isNaN(min) || isNaN(max))
  75. throw new TypeError(`Parameters "min" and "max" can't be NaN`);
  76. if (min > max)
  77. throw new TypeError(`Parameter "min" can't be bigger than "max"`);
  78. return Math.floor(Math.random() * (max - min + 1)) + min;
  79. }
  80.  
  81. // lib/array.ts
  82. function randomItem(array) {
  83. return randomItemIndex(array)[0];
  84. }
  85. function randomItemIndex(array) {
  86. if (array.length === 0)
  87. return [void 0, void 0];
  88. const idx = randRange(array.length - 1);
  89. return [array[idx], idx];
  90. }
  91. function takeRandomItem(arr) {
  92. const [itm, idx] = randomItemIndex(arr);
  93. if (idx === void 0)
  94. return void 0;
  95. arr.splice(idx, 1);
  96. return itm;
  97. }
  98. function randomizeArray(array) {
  99. const retArray = [...array];
  100. if (array.length === 0)
  101. return array;
  102. for (let i = retArray.length - 1; i > 0; i--) {
  103. const j = Math.floor(randRange(0, 1e4) / 1e4 * (i + 1));
  104. [retArray[i], retArray[j]] = [retArray[j], retArray[i]];
  105. }
  106. return retArray;
  107. }
  108.  
  109. // lib/dom.ts
  110. function getUnsafeWindow() {
  111. try {
  112. return unsafeWindow;
  113. } catch (e) {
  114. return window;
  115. }
  116. }
  117. function insertAfter(beforeElement, afterElement) {
  118. var _a;
  119. (_a = beforeElement.parentNode) == null ? void 0 : _a.insertBefore(afterElement, beforeElement.nextSibling);
  120. return afterElement;
  121. }
  122. function addParent(element2, newParent) {
  123. const oldParent = element2.parentNode;
  124. if (!oldParent)
  125. throw new Error("Element doesn't have a parent node");
  126. oldParent.replaceChild(newParent, element2);
  127. newParent.appendChild(element2);
  128. return newParent;
  129. }
  130. function addGlobalStyle(style) {
  131. const styleElem = document.createElement("style");
  132. styleElem.innerHTML = style;
  133. document.head.appendChild(styleElem);
  134. }
  135. function preloadImages(srcUrls, rejects = false) {
  136. const promises = srcUrls.map((src) => new Promise((res, rej) => {
  137. const image = new Image();
  138. image.src = src;
  139. image.addEventListener("load", () => res(image));
  140. image.addEventListener("error", (evt) => rejects && rej(evt));
  141. }));
  142. return Promise.allSettled(promises);
  143. }
  144. function openInNewTab(href) {
  145. const openElem = document.createElement("a");
  146. Object.assign(openElem, {
  147. className: "userutils-open-in-new-tab",
  148. target: "_blank",
  149. rel: "noopener noreferrer",
  150. href
  151. });
  152. openElem.style.display = "none";
  153. document.body.appendChild(openElem);
  154. openElem.click();
  155. setTimeout(openElem.remove, 50);
  156. }
  157. function interceptEvent(eventObject, eventName, predicate) {
  158. if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
  159. Error.stackTraceLimit = 1e3;
  160. }
  161. (function(original) {
  162. element.__proto__.addEventListener = function(...args) {
  163. if (args[0] === eventName && predicate())
  164. return;
  165. else
  166. return original.apply(this, args);
  167. };
  168. })(eventObject.__proto__.addEventListener);
  169. }
  170. function interceptWindowEvent(eventName, predicate) {
  171. return interceptEvent(getUnsafeWindow(), eventName, predicate);
  172. }
  173. function amplifyMedia(mediaElement, multiplier = 1) {
  174. const context = new (window.AudioContext || window.webkitAudioContext)();
  175. const result = {
  176. mediaElement,
  177. amplify: (multiplier2) => {
  178. result.gain.gain.value = multiplier2;
  179. },
  180. getAmpLevel: () => result.gain.gain.value,
  181. context,
  182. source: context.createMediaElementSource(mediaElement),
  183. gain: context.createGain()
  184. };
  185. result.source.connect(result.gain);
  186. result.gain.connect(context.destination);
  187. result.amplify(multiplier);
  188. return result;
  189. }
  190.  
  191. // lib/misc.ts
  192. function autoPlural(word, num) {
  193. if (Array.isArray(num) || num instanceof NodeList)
  194. num = num.length;
  195. return `${word}${num === 1 ? "" : "s"}`;
  196. }
  197. function pauseFor(time) {
  198. return new Promise((res) => {
  199. setTimeout(() => res(), time);
  200. });
  201. }
  202. function debounce(func, timeout = 300) {
  203. let timer;
  204. return function(...args) {
  205. clearTimeout(timer);
  206. timer = setTimeout(() => func.apply(this, args), timeout);
  207. };
  208. }
  209. function fetchAdvanced(_0) {
  210. return __async(this, arguments, function* (url, options = {}) {
  211. const { timeout = 1e4 } = options;
  212. const controller = new AbortController();
  213. const id = setTimeout(() => controller.abort(), timeout);
  214. const res = yield fetch(url, __spreadProps(__spreadValues({}, options), {
  215. signal: controller.signal
  216. }));
  217. clearTimeout(id);
  218. return res;
  219. });
  220. }
  221.  
  222. // lib/onSelector.ts
  223. var selectorMap = /* @__PURE__ */ new Map();
  224. function onSelector(selector, options) {
  225. let selectorMapItems = [];
  226. if (selectorMap.has(selector))
  227. selectorMapItems = selectorMap.get(selector);
  228. selectorMapItems.push(options);
  229. selectorMap.set(selector, selectorMapItems);
  230. checkSelectorExists(selector, selectorMapItems);
  231. }
  232. function removeOnSelector(selector) {
  233. return selectorMap.delete(selector);
  234. }
  235. function checkSelectorExists(selector, options) {
  236. const deleteIndices = [];
  237. options.forEach((option, i) => {
  238. try {
  239. const elements = option.all ? document.querySelectorAll(selector) : document.querySelector(selector);
  240. if (elements !== null && elements instanceof NodeList && elements.length > 0 || elements !== null) {
  241. option.listener(elements);
  242. if (!option.continuous)
  243. deleteIndices.push(i);
  244. }
  245. } catch (err) {
  246. console.error(`Couldn't call listener for selector '${selector}'`, err);
  247. }
  248. });
  249. if (deleteIndices.length > 0) {
  250. const newOptsArray = options.filter((_, i) => !deleteIndices.includes(i));
  251. if (newOptsArray.length === 0)
  252. selectorMap.delete(selector);
  253. else {
  254. selectorMap.set(selector, newOptsArray);
  255. }
  256. }
  257. }
  258. function initOnSelector(options = {}) {
  259. const observer = new MutationObserver(() => {
  260. for (const [selector, options2] of selectorMap.entries())
  261. checkSelectorExists(selector, options2);
  262. });
  263. observer.observe(document.body, __spreadValues({
  264. subtree: true,
  265. childList: true
  266. }, options));
  267. }
  268. function getSelectorMap() {
  269. return selectorMap;
  270. }
  271.  
  272. exports.addGlobalStyle = addGlobalStyle;
  273. exports.addParent = addParent;
  274. exports.amplifyMedia = amplifyMedia;
  275. exports.autoPlural = autoPlural;
  276. exports.clamp = clamp;
  277. exports.debounce = debounce;
  278. exports.fetchAdvanced = fetchAdvanced;
  279. exports.getSelectorMap = getSelectorMap;
  280. exports.getUnsafeWindow = getUnsafeWindow;
  281. exports.initOnSelector = initOnSelector;
  282. exports.insertAfter = insertAfter;
  283. exports.interceptEvent = interceptEvent;
  284. exports.interceptWindowEvent = interceptWindowEvent;
  285. exports.mapRange = mapRange;
  286. exports.onSelector = onSelector;
  287. exports.openInNewTab = openInNewTab;
  288. exports.pauseFor = pauseFor;
  289. exports.preloadImages = preloadImages;
  290. exports.randRange = randRange;
  291. exports.randomItem = randomItem;
  292. exports.randomItemIndex = randomItemIndex;
  293. exports.randomizeArray = randomizeArray;
  294. exports.removeOnSelector = removeOnSelector;
  295. exports.takeRandomItem = takeRandomItem;
  296.  
  297. return exports;
  298.  
  299. })({});