NH_web

Common patterns for working with the WEB API.

目前为 2023-10-28 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/478440/1271424/NH_web.js

  1. // ==UserScript==
  2. // ==UserLibrary==
  3. // @name NH_web
  4. // @description Common patterns for working with the WEB API.
  5. // @version 0
  6. // @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0-standalone.html
  7. // @homepageURL https://github.com/nexushoratio/userscripts
  8. // @supportURL https://github.com/nexushoratio/userscripts/issues
  9. // @match https://www.example.com/*
  10. // ==/UserLibrary==
  11. // ==/UserScript==
  12.  
  13. window.NexusHoratio ??= {};
  14.  
  15. window.NexusHoratio.web = (function web() {
  16. 'use strict';
  17.  
  18. /** @type {number} - Bumped per release. */
  19. const version = 0;
  20.  
  21. const NH = window.NexusHoratio.base.ensure([{name: 'base'}]);
  22.  
  23. /**
  24. * @typedef {object} Continuation
  25. * @property {boolean} done - Indicate whether the monitor is done
  26. * processing.
  27. * @property {object} [results] - Optional results object.
  28. */
  29.  
  30. /**
  31. * @callback Monitor
  32. * @param {MutationRecord[]} records - Standard mutation records.
  33. * @returns {Continuation} - Indicate whether done monitoring.
  34. */
  35.  
  36. /**
  37. * Simple function that takes no parameters and returns nothing.
  38. * @callback SimpleFunction
  39. */
  40.  
  41. /**
  42. * @typedef {object} OtmotWhat
  43. * @property {string} name - The name for this observer.
  44. * @property {Element} base - Element to observe.
  45. */
  46.  
  47. /**
  48. * @typedef {object} OtmotHow
  49. * @property {object} observeOptions - MutationObserver().observe() options.
  50. * @property {SimpleFunction} [trigger] - Function to call that triggers
  51. * observable results.
  52. * @property {Monitor} monitor - Callback used to process MutationObserver
  53. * records.
  54. * @property {number} [timeout] - Time to wait for completion in
  55. * milliseconds, default of 0 disables.
  56. */
  57.  
  58. /**
  59. * One time mutation observer with timeout.
  60. * @param {OtmotWhat} what - What to observe.
  61. * @param {OtmotHow} how - How to observe.
  62. * @returns {Promise<Continuation.results>} - Will resolve with the results
  63. * from monitor when done is true.
  64. */
  65. function otmot(what, how) {
  66. const prom = new Promise((resolve, reject) => {
  67. const {
  68. name,
  69. base,
  70. } = what;
  71. const {
  72. observeOptions,
  73. trigger = () => {}, // eslint-disable-line no-empty-function
  74. monitor,
  75. timeout = 0,
  76. } = how;
  77.  
  78. const logger = new NH.base.Logger(`otmot ${name}`);
  79. let timeoutID = null;
  80. let observer = null;
  81.  
  82. /** @param {MutationRecord[]} records - Standard mutation records. */
  83. const moCallback = (records) => {
  84. const {done, results} = monitor(records);
  85. logger.log('monitor:', done, results);
  86. if (done) {
  87. observer.disconnect();
  88. clearTimeout(timeoutID);
  89. logger.log('resolving');
  90. resolve(results);
  91. }
  92. };
  93.  
  94. /** Standard setTimeout callback. */
  95. const toCallback = () => {
  96. observer.disconnect();
  97. logger.log('rejecting after timeout');
  98. reject(new Error(`otmot ${name} timed out`));
  99. };
  100.  
  101. observer = new MutationObserver(moCallback);
  102. if (timeout) {
  103. timeoutID = setTimeout(toCallback, timeout);
  104. }
  105.  
  106. observer.observe(base, observeOptions);
  107. trigger();
  108. logger.log('running');
  109. });
  110.  
  111. return prom;
  112. }
  113.  
  114. /**
  115. * @typedef {object} OtrotWhat
  116. * @property {string} name - The name for this observer.
  117. * @property {Element} base - Element to observe.
  118. */
  119.  
  120. /**
  121. * @typedef {object} OtrotHow
  122. * @property {SimpleFunction} [trigger] - Function to call that triggers
  123. * observable events.
  124. * @property {number} timeout - Time to wait for completion in milliseconds.
  125. */
  126.  
  127. /**
  128. * One time resize observer with timeout.
  129. *
  130. * Will resolve automatically upon first resize change.
  131. * @param {OtrotWhat} what - What to observe.
  132. * @param {OtrotHow} how - How to observe.
  133. * @returns {Promise<OtrotWhat>} - Will resolve with the what parameter.
  134. */
  135. function otrot(what, how) {
  136. const prom = new Promise((resolve, reject) => {
  137. const {
  138. name,
  139. base,
  140. } = what;
  141. const {
  142. trigger = () => {}, // eslint-disable-line no-empty-function
  143. timeout,
  144. } = how;
  145. const {
  146. clientHeight: initialHeight,
  147. clientWidth: initialWidth,
  148. } = base;
  149.  
  150. const logger = new NH.base.Logger(`otrot ${name}`);
  151. let timeoutID = null;
  152. let observer = null;
  153. logger.log('initial dimensions:', initialWidth, initialHeight);
  154.  
  155. /** Standard ResizeObserver callback. */
  156. const roCallback = () => {
  157. const {clientHeight, clientWidth} = base;
  158. logger.log('observed dimensions:', clientWidth, clientHeight);
  159. if (clientHeight !== initialHeight || clientWidth !== initialWidth) {
  160. observer.disconnect();
  161. clearTimeout(timeoutID);
  162. logger.log('resolving');
  163. resolve(what);
  164. }
  165. };
  166.  
  167. /** Standard setTimeout callback. */
  168. const toCallback = () => {
  169. observer.disconnect();
  170. logger.log('rejecting after timeout');
  171. reject(new Error(`otrot ${name} timed out`));
  172. };
  173.  
  174. observer = new ResizeObserver(roCallback);
  175. timeoutID = setTimeout(toCallback, timeout);
  176.  
  177. observer.observe(base);
  178. trigger();
  179. logger.log('running');
  180. });
  181.  
  182. return prom;
  183. }
  184.  
  185. /**
  186. * @callback ResizeAction
  187. * @param {ResizeObserverEntry[]} entries - Standard resize entries.
  188. */
  189.  
  190. /**
  191. * @typedef {object} Otrot2How
  192. * @property {SimpleFunction} [trigger] - Function to call that triggers
  193. * observable events.
  194. * @property {ResizeAction} action - Function to call upon each event
  195. * observed and also at the end of duration.
  196. * @property {number} duration - Time to run in milliseconds.
  197. */
  198.  
  199. /**
  200. * One time resize observer with action callback and duration.
  201. *
  202. * Will resolve upon duration expiration. Uses the same what parameter as
  203. * {@link otrot}.
  204. * @param {OtrotWhat} what - What to observe.
  205. * @param {Otrow2How} how - How to observe.
  206. * @returns {Promise<string>} - Will resolve after duration expires.
  207. */
  208. function otrot2(what, how) {
  209. const prom = new Promise((resolve) => {
  210. const {
  211. name,
  212. base,
  213. } = what;
  214. const {
  215. trigger = () => {}, // eslint-disable-line no-empty-function
  216. action,
  217. duration,
  218. } = how;
  219.  
  220. const logger = new NH.base.Logger(`otrot2 ${name}`);
  221. let observer = null;
  222.  
  223. /** @param {ResizeObserverEntry[]} entries - Standard entries. */
  224. const roCallback = (entries) => {
  225. logger.log('calling action');
  226. action(entries);
  227. };
  228.  
  229. /** Standard setTimeout callback. */
  230. const toCallback = () => {
  231. observer.disconnect();
  232. action([]);
  233. logger.log('resolving');
  234. resolve(`otrot2 ${name} finished`);
  235. };
  236.  
  237. observer = new ResizeObserver(roCallback);
  238. setTimeout(toCallback, duration);
  239.  
  240. observer.observe(base);
  241. trigger();
  242. logger.log('running');
  243. });
  244.  
  245. return prom;
  246. }
  247.  
  248. /**
  249. * Wait for selector to match using querySelector.
  250. * @param {string} selector - CSS selector.
  251. * @param {number} timeout - Time to wait in milliseconds, 0 disables.
  252. * @returns {Promise<Element>} - Matched element.
  253. */
  254. function waitForSelector(selector, timeout) {
  255. const me = 'waitForSelector';
  256. const logger = new NH.base.Logger(me);
  257. logger.entered(me, selector, timeout);
  258.  
  259. /**
  260. * @implements {Monitor}
  261. * @returns {Continuation} - Indicate whether done monitoring.
  262. */
  263. const monitor = () => {
  264. const element = document.querySelector(selector);
  265. if (element) {
  266. logger.log(`match for ${selector}`, element);
  267. return {done: true, results: element};
  268. }
  269. logger.log('Still waiting for', selector);
  270. return {done: false};
  271. };
  272.  
  273. const what = {
  274. name: me,
  275. base: document,
  276. };
  277.  
  278. const how = {
  279. observeOptions: {childList: true, subtree: true},
  280. monitor: monitor,
  281. timeout: timeout,
  282. };
  283.  
  284. logger.leaving(me);
  285. return otmot(what, how);
  286. }
  287.  
  288. return {
  289. version: version,
  290. otmot: otmot,
  291. otrot: otrot,
  292. otrot2: otrot2,
  293. waitForSelector: waitForSelector,
  294. };
  295.  
  296. }());

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址