(() => { /** @typedef {{ key: string; elem: HTMLElement; labelElem: HTMLElement | null }} Match */ const addRoot = () => { const root = document.createElement('div'); document.body.appendChild(root); return root; } /** * @param match {Match} * @return {Promise} */ const createLabelElem = (match) => new Promise((resolve, reject) => { const label = Object.assign(document.createElement('div'), { className: '__nullbrowser_findlabel', textContent: match.key, }); requestAnimationFrame(() => { const rect = match.elem.getBoundingClientRect(); if (rect.top < 0 | rect.top > window.innerHeight) return reject(null); if (rect.left < 0 | rect.left > window.innerWidth) return reject(null); resolve(label); requestAnimationFrame(() => { Object.assign(label.style, { position: 'fixed', zIndex: 999999999, // maybe a few more 9s will do top: `${rect.top}px`, left: `${rect.left}px`, backgroundColor: 'yellow', border: '1px solid #000', boxShadow: '0 0 3px #555', color: '#222', padding: '1px 2px', fontSize: '10px', fontWeight: 'bold', fontFamily: 'monospace', }); }); }); }); const finder = { /** @type {string} */ keys: '', /** @type {Array} */ matches: [], /** @type {HTMLElement | null} */ labelsRoot: null, /** @type {boolean} */ openInNewView: false, stop() { finder.matches = []; finder.keys = ''; [...(finder.labelsRoot?.children ?? [])].forEach(child => { child.remove(); }); }, /** * @param selector {string} * @param new_view {boolean} */ start(selector, new_view) { finder.stop() finder.labelsRoot ??= addRoot(); finder.createMatches(selector); finder.openInNewView = new_view; }, /** @param char {string} */ filterOutByKey(char) { if (!/[0-9]+/.test(char)) return; // NOTE: temporary finder.keys = finder.keys + char; finder.matches = finder.matches.filter(m => { const isMatch = m.key.startsWith(finder.keys); if (!isMatch) { m.labelElem?.remove(); return false; } return true; }); if (finder.matches.length <= 1) { const match = finder.matches[0] finder.stop(); if (match) { if (match.elem?.href) { if (finder.openInNewView) { window.open(match.elem?.href) } else { location.href = match.elem?.href } } else { match.elem?.click() } } } }, /** @param selector {string} */ createMatches(selector) { finder.matches = []; const elements = document.querySelectorAll(selector); const matches = [...elements].map(async (elem, index) => { /** @type {Match} */ const match = { elem, key: `${index}`.padStart(`${elements.length}`.length, '0'), labelElem: null, } match.labelElem = await createLabelElem(match); return match }); Promise.allSettled(matches).then(matches => { matches.forEach(result => { if (result.status !== 'fulfilled') return; if (result.value.labelElem) finder.labelsRoot.appendChild(result.value.labelElem) finder.matches.push(result.value) }); }); }, }; window._nullbrowser ||= {}; window._nullbrowser.finder ||= finder; })();