Itsnotlupus' Tiny Utilities

small utilities that I'm tired of digging from old scripts to put in new ones.

Tính đến 07-08-2023. Xem phiên bản mới nhất.

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://update.greatest.deepsurf.us/scripts/468394/1231623/Itsnotlupus%27%20Tiny%20Utilities.js

// ==UserScript==
// @name         Itsnotlupus' Tiny Utilities
// @namespace    Itsnotlupus Industries
// @version      1.18
// @description  small utilities that I'm tired of digging from old scripts to put in new ones.
// @author       Itsnotlupus
// @license      MIT
// ==/UserScript==

/* jshint esversion:11 */
/* jshint -W138 */

/** DOM queries - CSS selectors and XPath */
const $ = (q,d=document)=>d.querySelector(q);
const $$ = (q,d=document)=>d.querySelectorAll(q);
const $$$ = (q,d=document,x=d.evaluate(q,d),a=[],n=x.iterateNext()) => n ? (a.push(n), $$$(q,d,x,a)) : a;

/** calls a function whenever the DOM changes */
const observeDOM = (fn, e=document, config = { attributes: 1, childList: 1, subtree: 1 }, o = new MutationObserver(fn)) => (o.observe(e,config),()=>o.disconnect());

/** check a condition upfront, and on every DOM change until true */
const untilDOM = async (v, e=document, f=v.sup?()=>$(v,e):v) => f() || new Promise((r,_,d = observeDOM(() => (_=f()) && d() | r(_), e)) => 0);

/** promisify setTimeout and setInterval */
const sleep = (w = 100) => new Promise(r=>setTimeout(r, w));
const until = async (v, w=100, t, f=v.sup?()=>$(v):v) => f() || new Promise(r => t=setInterval((s=f()) => s && (clearInterval(t), r(s)), w));

/** slightly less painful syntax to create DOM trees */
const crel = (name, attrs, ...children) => ((e = Object.assign(document.createElement(name), attrs)) => (e.append(...children), e))();

/** same, for SVG content. */
const svg = (name, attrs={}, ...children) => ((e=document.createElementNS('http://www.w3.org/2000/svg', name), _=Object.keys(attrs).forEach(k=>e.setAttribute(k,attrs[k])),__=e.append(...children)) => e)();

/** create a shadow dom with an isolated stylesheet */
const custom = (name, css, dom, e = crel(name), ss = e.attachShadow({mode:'closed'})) => (ss.adoptedStyleSheets = [ (s = new CSSStyleSheet(), s.replaceSync(css),s) ], ss.append(...dom), e);

/** add a stylesheet */
const addStyles = css => document.head.append(crel('style', { type: 'text/css', textContent: css }));

/** stolen from https://gist.github.com/nmsdvid/8807205 */
const slowDebounce = (a,b=250,c=0)=>(...d)=>clearTimeout(c,c=setTimeout(a,b,...d));

/** microtask debounce */
const fastDebounce = (f, l, s=0) => async (...a) => (l = a, !s && (await (s=1), s = 0, f(...l)));

/** remember and shortcut what a (pure) function returns */
const memoize = (f, mkKey=args=>args.join(), cache = Object.create(null)) => (...args) => cache[mkKey(args)] ??= f(...args);

/** requestAnimationFrame wrapper that allows a callback to request another run without referencing itself 
 * Use as: 
 * rAF((time, next) => {
 *   // cool animation code goes here.
 *   next(); // run again next frame
 * });
 */
const rAF = (f, n=t=>f(t,r), r=_=>requestAnimationFrame(n)) => r();

/** define a few event listeners in one shot - call the returned function to remove them. */
const  events = (o, t=window, opts, f=op=>Object.keys(o).forEach(e=>t[op](e,o[e],opts))) => (f("addEventListener"), () => f("removeEventListener"));

/** promisify a @grant-less XHR. probably useless. */
const xhr = (url, type='') => new Promise((r,e,x=Object.assign(new XMLHttpRequest(), {responseType: type,onload() { r(x.response); },onerror:e}),_=x.open('GET',url)) => x.send());

/** fetch and parse */
const fetchDOM = (url, mimeType) => fetch(url).then(r=>r.text()).then(t=>new DOMParser().parseFromString(t,mimeType));
const fetchHTML = url => fetchDOM(url, 'text/html');
const fetchJSON = url => fetch(url).then(r=>r.json());

/** Prefetch a URL */
const prefetch = url => document.head.append(crel('link', { rel: 'prefetch', href: url }));

/** Some sites break the `console` API. This attempts to restore a working console object. */
const fixConsole = (i=crel('iframe',{style:'display:none'}),_=document.body.append(i),c=unsafeWindow.console) => console.log.name!='log' ? (unsafeWindow.console = i.contentWindow.console,()=>(i.remove(),unsafeWindow.console=c)):()=>{};

/** Another take on logging */
let logger = console;
/** a cheap way to get logs to show up on sites that damaged their console.log */
async function withLogs(f) {
  if (logger.log.name == 'log') return await f();
  const iframe=crel('iframe', { style: 'display:none' });
  document.body.append(iframe);
  const prevLogger = logger;
  logger = iframe.contentWindow.console;
  try {
    return await f();
  } finally {
    iframe.remove();
    logger = prevLogger;
  }
}
const log = (msg, ...args) => logger.log(`%c ${GM_info.script.name}: ${msg}`, 'font-weight:600;color:#ccf;background:#114;padding:.2em', ...args);