您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Common patterns for working with the WEB API.
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.greasyfork.icu/scripts/478440/1271424/NH_web.js
// ==UserScript== // ==UserLibrary== // @name NH_web // @description Common patterns for working with the WEB API. // @version 0 // @license GPL-3.0-or-later; https://www.gnu.org/licenses/gpl-3.0-standalone.html // @homepageURL https://github.com/nexushoratio/userscripts // @supportURL https://github.com/nexushoratio/userscripts/issues // @match https://www.example.com/* // ==/UserLibrary== // ==/UserScript== window.NexusHoratio ??= {}; window.NexusHoratio.web = (function web() { 'use strict'; /** @type {number} - Bumped per release. */ const version = 0; const NH = window.NexusHoratio.base.ensure([{name: 'base'}]); /** * @typedef {object} Continuation * @property {boolean} done - Indicate whether the monitor is done * processing. * @property {object} [results] - Optional results object. */ /** * @callback Monitor * @param {MutationRecord[]} records - Standard mutation records. * @returns {Continuation} - Indicate whether done monitoring. */ /** * Simple function that takes no parameters and returns nothing. * @callback SimpleFunction */ /** * @typedef {object} OtmotWhat * @property {string} name - The name for this observer. * @property {Element} base - Element to observe. */ /** * @typedef {object} OtmotHow * @property {object} observeOptions - MutationObserver().observe() options. * @property {SimpleFunction} [trigger] - Function to call that triggers * observable results. * @property {Monitor} monitor - Callback used to process MutationObserver * records. * @property {number} [timeout] - Time to wait for completion in * milliseconds, default of 0 disables. */ /** * One time mutation observer with timeout. * @param {OtmotWhat} what - What to observe. * @param {OtmotHow} how - How to observe. * @returns {Promise<Continuation.results>} - Will resolve with the results * from monitor when done is true. */ function otmot(what, how) { const prom = new Promise((resolve, reject) => { const { name, base, } = what; const { observeOptions, trigger = () => {}, // eslint-disable-line no-empty-function monitor, timeout = 0, } = how; const logger = new NH.base.Logger(`otmot ${name}`); let timeoutID = null; let observer = null; /** @param {MutationRecord[]} records - Standard mutation records. */ const moCallback = (records) => { const {done, results} = monitor(records); logger.log('monitor:', done, results); if (done) { observer.disconnect(); clearTimeout(timeoutID); logger.log('resolving'); resolve(results); } }; /** Standard setTimeout callback. */ const toCallback = () => { observer.disconnect(); logger.log('rejecting after timeout'); reject(new Error(`otmot ${name} timed out`)); }; observer = new MutationObserver(moCallback); if (timeout) { timeoutID = setTimeout(toCallback, timeout); } observer.observe(base, observeOptions); trigger(); logger.log('running'); }); return prom; } /** * @typedef {object} OtrotWhat * @property {string} name - The name for this observer. * @property {Element} base - Element to observe. */ /** * @typedef {object} OtrotHow * @property {SimpleFunction} [trigger] - Function to call that triggers * observable events. * @property {number} timeout - Time to wait for completion in milliseconds. */ /** * One time resize observer with timeout. * * Will resolve automatically upon first resize change. * @param {OtrotWhat} what - What to observe. * @param {OtrotHow} how - How to observe. * @returns {Promise<OtrotWhat>} - Will resolve with the what parameter. */ function otrot(what, how) { const prom = new Promise((resolve, reject) => { const { name, base, } = what; const { trigger = () => {}, // eslint-disable-line no-empty-function timeout, } = how; const { clientHeight: initialHeight, clientWidth: initialWidth, } = base; const logger = new NH.base.Logger(`otrot ${name}`); let timeoutID = null; let observer = null; logger.log('initial dimensions:', initialWidth, initialHeight); /** Standard ResizeObserver callback. */ const roCallback = () => { const {clientHeight, clientWidth} = base; logger.log('observed dimensions:', clientWidth, clientHeight); if (clientHeight !== initialHeight || clientWidth !== initialWidth) { observer.disconnect(); clearTimeout(timeoutID); logger.log('resolving'); resolve(what); } }; /** Standard setTimeout callback. */ const toCallback = () => { observer.disconnect(); logger.log('rejecting after timeout'); reject(new Error(`otrot ${name} timed out`)); }; observer = new ResizeObserver(roCallback); timeoutID = setTimeout(toCallback, timeout); observer.observe(base); trigger(); logger.log('running'); }); return prom; } /** * @callback ResizeAction * @param {ResizeObserverEntry[]} entries - Standard resize entries. */ /** * @typedef {object} Otrot2How * @property {SimpleFunction} [trigger] - Function to call that triggers * observable events. * @property {ResizeAction} action - Function to call upon each event * observed and also at the end of duration. * @property {number} duration - Time to run in milliseconds. */ /** * One time resize observer with action callback and duration. * * Will resolve upon duration expiration. Uses the same what parameter as * {@link otrot}. * @param {OtrotWhat} what - What to observe. * @param {Otrow2How} how - How to observe. * @returns {Promise<string>} - Will resolve after duration expires. */ function otrot2(what, how) { const prom = new Promise((resolve) => { const { name, base, } = what; const { trigger = () => {}, // eslint-disable-line no-empty-function action, duration, } = how; const logger = new NH.base.Logger(`otrot2 ${name}`); let observer = null; /** @param {ResizeObserverEntry[]} entries - Standard entries. */ const roCallback = (entries) => { logger.log('calling action'); action(entries); }; /** Standard setTimeout callback. */ const toCallback = () => { observer.disconnect(); action([]); logger.log('resolving'); resolve(`otrot2 ${name} finished`); }; observer = new ResizeObserver(roCallback); setTimeout(toCallback, duration); observer.observe(base); trigger(); logger.log('running'); }); return prom; } /** * Wait for selector to match using querySelector. * @param {string} selector - CSS selector. * @param {number} timeout - Time to wait in milliseconds, 0 disables. * @returns {Promise<Element>} - Matched element. */ function waitForSelector(selector, timeout) { const me = 'waitForSelector'; const logger = new NH.base.Logger(me); logger.entered(me, selector, timeout); /** * @implements {Monitor} * @returns {Continuation} - Indicate whether done monitoring. */ const monitor = () => { const element = document.querySelector(selector); if (element) { logger.log(`match for ${selector}`, element); return {done: true, results: element}; } logger.log('Still waiting for', selector); return {done: false}; }; const what = { name: me, base: document, }; const how = { observeOptions: {childList: true, subtree: true}, monitor: monitor, timeout: timeout, }; logger.leaving(me); return otmot(what, how); } return { version: version, otmot: otmot, otrot: otrot, otrot2: otrot2, waitForSelector: waitForSelector, }; }());