Last active
February 22, 2026 04:47
-
-
Save FOBshippingpoint/94ecfdd661a82548e94aab57e7c3418e to your computer and use it in GitHub Desktop.
Handy userscript toolkit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Selects a single element from the DOM. | |
| * | |
| * @param selectors - Either a CSS selector string or an existing DOM element. | |
| * @returns The selected element or null if not found. | |
| */ | |
| var $ = document.querySelector.bind(document); | |
| /** | |
| * Selects multiple elements from the DOM and returns them as an array. | |
| * | |
| * @param selectors - Either a CSS selector string or an existing DOM element. | |
| * @returns An array of the selected elements with additional methods. | |
| */ | |
| var $$ = (selector) => [...document.querySelectorAll(selector)]; | |
| /** | |
| * Creates a new HTML element with the specified tag name and optional attributes. | |
| * | |
| * This is an alias for `document.createElement`. | |
| * | |
| * @param tagName - The name of the HTML element to create. | |
| * @param options - Optional attributes for the element. | |
| * @returns The created HTML element. | |
| */ | |
| var $$$ = document.createElement.bind(document); | |
| /** | |
| * Converts an HTML string into either a single Element or DocumentFragment | |
| * | |
| * @param html - The HTML string to be converted | |
| * @returns A single Element if the HTML contains one root element, otherwise returns a DocumentFragment containing all elements | |
| * | |
| * @example | |
| * // Returns a strongly-typed HTMLDivElement | |
| * const div = h<HTMLDivElement>('<div>Hello</div>'); | |
| * | |
| * @example | |
| * // Returns a DocumentFragment containing multiple elements | |
| * const fragment = h('<div>First</div><div>Second</div>'); | |
| */ | |
| var h = (html) => { | |
| const template = $$$("template"); | |
| template.innerHTML = html; | |
| if (template.content.childElementCount === 1) { | |
| return template.content.firstElementChild; | |
| } else { | |
| return template.content; | |
| } | |
| }; | |
| /** | |
| * Add CSS stylesheet to current document. | |
| * | |
| * @param {string} css - CSS stylesheet to adopt. | |
| * @returns {CSSStyleSheet} stylesheet - injected CSS stylesheet. | |
| */ | |
| var addCss = (css) => { | |
| const extraSheet = new CSSStyleSheet(); | |
| extraSheet.replaceSync(css); | |
| document.adoptedStyleSheets = [...document.adoptedStyleSheets, extraSheet]; | |
| return extraSheet; | |
| }; | |
| /** | |
| * Mock browser's $x helper function in devtools. | |
| * | |
| * @param {string} xpath - the XPath to be evaluated. | |
| * @see {@link https://devhints.io/xpath} | |
| */ | |
| var $x = (xpath) => { | |
| const xpathResult = document.evaluate( | |
| xpath, | |
| document, | |
| null, | |
| XPathResult.ANY_TYPE, | |
| null, | |
| ); | |
| const elements = []; | |
| let el; | |
| while ((el = xpathResult.iterateNext())) { | |
| elements.push(el); | |
| } | |
| return elements; | |
| }; | |
| /** | |
| * Write text to clipboard. | |
| * Use Clipboard API if possible, fallback to document.exec when failed. | |
| * | |
| * @param {string} text - string to copy. | |
| */ | |
| var copyText = async (text) => { | |
| if ("clipboard" in navigator) { | |
| /* Won't work under HTTP */ | |
| navigator.clipboard | |
| .writeText(text) | |
| .then(() => { | |
| console.log("Text copied"); | |
| }) | |
| .catch((err) => console.error(err.name, err.message)); | |
| } else { | |
| const textArea = document.createElement("textarea"); | |
| textArea.value = text; | |
| textArea.style.opacity = 0; | |
| document.body.appendChild(textArea); | |
| textArea.focus({ preventScroll: true }); | |
| textArea.select(); | |
| try { | |
| const success = document.execCommand("copy"); | |
| console.log(`Text copy was ${success ? "successful" : "unsuccessful"}.`); | |
| } catch (err) { | |
| console.error(err.name, err.message); | |
| } | |
| document.body.removeChild(textArea); | |
| } | |
| }; | |
| /** | |
| * Forked from https://github.com/violentmonkey/vm-dom/blob/master/src/index.ts | |
| * | |
| * Observe an existing `node` until `callback` returns `true`. | |
| * The returned function can be called explicitly to disconnect the observer. | |
| * | |
| * ```js | |
| * observe(document.body, () => { | |
| * const node = document.querySelector('.profile'); | |
| * if (node) { | |
| * console.log('It\'s there!'); | |
| * return true; | |
| * } | |
| * }); | |
| * ``` | |
| */ | |
| var observe = (node, callback, options) => { | |
| const observer = new MutationObserver((mutations, ob) => { | |
| const result = callback(mutations, ob); | |
| if (result) disconnect(); | |
| }); | |
| observer.observe(node, { | |
| childList: true, | |
| subtree: true, | |
| ...options, | |
| }); | |
| const disconnect = () => observer.disconnect(); | |
| return disconnect; | |
| }; | |
| /** | |
| * Wait until querySelector found. | |
| * ```js | |
| * until("#username", (input) => { | |
| * input.value = "admin"; | |
| * }); | |
| * ``` | |
| */ | |
| var until = (querySelector, callback) => { | |
| const target = $(querySelector); | |
| if (target) { | |
| callback(target); | |
| } else { | |
| observe(document, (mutations) => { | |
| const target = $(querySelector); | |
| if (target) { | |
| callback(target); | |
| return true; | |
| } | |
| }); | |
| } | |
| }; | |
| /** | |
| * Creates a debounced function that delays invoking callback until after waitMs | |
| * milliseconds have elapsed since the last time the debounced function was invoked. | |
| * The callback is invoked with the last arguments provided to the debounced function. | |
| * | |
| * @typeParam T - The type of function to debounce | |
| * @param callback - The function to debounce | |
| * @param options - The options object | |
| * @param options.isLeadingEdge - Whether to invoke on the leading edge of the timeout | |
| * @param options.waitMs - The number of milliseconds to delay | |
| * @returns A new debounced function | |
| * | |
| * @example | |
| * ```javascript | |
| * // Avoid costly calls while typing | |
| * const debouncedSave = debounce((text: string) => saveToServer(text)); | |
| * input.addEventListener('input', (e) => debouncedSave(e.target.value)); | |
| * ``` | |
| */ | |
| var debounce = ( | |
| callback, | |
| { isLeadingEdge, waitMs } = { | |
| isLeadingEdge: false, | |
| waitMs: 3000, // 3 sec delay before saving | |
| }, | |
| ) => { | |
| let timeoutId; | |
| return (...args) => { | |
| const isCallNow = isLeadingEdge && !timeoutId; | |
| clearTimeout(timeoutId); | |
| timeoutId = window.setTimeout(() => { | |
| timeoutId = undefined; | |
| if (!isLeadingEdge) { | |
| callback(...args); | |
| } | |
| }, waitMs); | |
| if (isCallNow) { | |
| callback(...args); | |
| } | |
| }; | |
| }; | |
| /////////////////////////////////////////////////////////////////////////// | |
| ///////////////////////////////INITIALIZATION////////////////////////////// | |
| /////////////////////////////////////////////////////////////////////////// | |
| if (EventTarget.prototype.on) { | |
| console.warn(`EventTarget.prototype.on already in use.`); | |
| } else { | |
| EventTarget.prototype.on = Element.prototype.addEventListener; | |
| } | |
| if (EventTarget.prototype.off) { | |
| console.warn(`EventTarget.prototype.off already in use.`); | |
| } else { | |
| EventTarget.prototype.off = EventTarget.prototype.removeEventListener; | |
| } | |
| if (Element.prototype.$) { | |
| console.warn(`Element.prototype.$ already in use.`); | |
| } else { | |
| Element.prototype.$ = Element.prototype.querySelector; | |
| } | |
| if (Element.prototype.$$) { | |
| console.warn(`Element.prototype.$$ already in use.`); | |
| } else { | |
| Element.prototype.$$ = function (...args) { | |
| return [...Element.prototype.querySelectorAll.apply(this, args)]; | |
| }; | |
| } | |
| if (DocumentFragment.prototype.$) { | |
| console.warn(`DocumentFragment.prototype.$ already in use.`); | |
| } else { | |
| DocumentFragment.prototype.$ = DocumentFragment.prototype.querySelector; | |
| } | |
| if (DocumentFragment.prototype.$$) { | |
| console.warn(`DocumentFragment.prototype.$$ already in use.`); | |
| } else { | |
| DocumentFragment.prototype.$$ = function (selector) { | |
| return [...this.querySelectorAll(selector)]; | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment