Last active
August 2, 2023 06:58
-
-
Save SUT0L/cf852bdf44f750949be561346c39d592 to your computer and use it in GitHub Desktop.
Quick Select buttons / anchor tags
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
| // ==UserScript== | |
| // @name Quick Click | |
| // @namespace http://tampermonkey.net/ | |
| // @version 0.0.3 | |
| // @description Quick Select buttons / anchor tags | |
| // @author Sutol | |
| // @license MIT | |
| // @match *://*/* | |
| // @grant none | |
| // @run-at document-end | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| let theme = { | |
| modal: { | |
| position: "fixed", | |
| zIndex: "9999", | |
| left: "0", | |
| top: "0", | |
| width: "100%", | |
| height: "100%", | |
| overflow: "auto", | |
| backgroundColor: "rgba(26, 32, 44, 0.95)", | |
| display: "none" | |
| }, | |
| searchContainer: { | |
| display: "flex", | |
| justifyContent: "center", | |
| marginBottom: "20px" | |
| }, | |
| modalContent: { | |
| backgroundColor: "#F7FAFC", | |
| margin: "15% auto", | |
| padding: "20px", | |
| border: "1px solid #A0AEC0", | |
| borderRadius: "0.375rem", | |
| width: "60%", | |
| boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)" | |
| }, | |
| input: { | |
| id: "search-box", | |
| autocomplete: "off", | |
| width: "100%", | |
| backgroundColor: "white", | |
| borderRadius: "0.375rem", | |
| padding: "0.5rem 1rem", | |
| border: "1px solid #E2E8F0" | |
| }, | |
| resultList: { | |
| listStyleType: "none", | |
| padding: "0", | |
| maxHeight: "600px", | |
| overflowY: "scroll", | |
| overflowX: "hidden" | |
| }, | |
| listItem: { | |
| display: "flex", | |
| justifyContent: "space-between", | |
| border: "1px solid #CBD5E0", | |
| padding: "0.5rem 1rem", | |
| borderRadius: "0.375rem", | |
| marginTop: "2px", | |
| normalBg: "#F7FAFC", | |
| highlightBg: '#F56565' | |
| }, | |
| href: { | |
| normalColor: "#bdbdbd", | |
| selectedColor: "#ffffff", | |
| textDecoration: "none", | |
| } | |
| } | |
| // If you're using the shift key, you must make the "key" the uppercase | |
| let keybinds = { | |
| search: { key: 'F', ctrlKey: true, metaKey: false, shiftKey: true }, | |
| navigateDown: { key: 'ArrowDown', ctrlKey: false, metaKey: false, shiftKey: false }, | |
| navigateUp: { key: 'ArrowUp', ctrlKey: false, metaKey: false, shiftKey: false }, | |
| select: { keys: ['Enter'], ctrlKey: false, metaKey: false, shiftKey: false }, | |
| newTabSelect: { key: 'Enter', ctrlKey: true, metaKey: false, shiftKey: false }, | |
| cancel: { key: 'Escape', ctrlKey: false, metaKey: false, shiftKey: false } | |
| }; | |
| let modal = document.createElement("div"); | |
| Object.assign(modal.style, theme.modal); | |
| document.body.appendChild(modal); | |
| let modalContent = document.createElement("div"); | |
| Object.assign(modalContent.style, theme.modalContent); | |
| modal.appendChild(modalContent); | |
| let input = document.createElement("input"); | |
| input.id = theme.input.id; | |
| input.setAttribute("autocomplete", theme.input.autocomplete); | |
| Object.assign(input.style, theme.input); | |
| modalContent.appendChild(input); | |
| let resultList = document.createElement("ul"); | |
| Object.assign(resultList.style, theme.resultList); | |
| modalContent.appendChild(resultList); | |
| let currentIndex = -1; | |
| let matches = []; | |
| window.addEventListener("keydown", function (e) { | |
| if (e.key === keybinds.search.key && e.ctrlKey === keybinds.search.ctrlKey && e.metaKey === keybinds.search.metaKey && e.shiftKey === keybinds.search.shiftKey) { | |
| e.preventDefault(); | |
| modal.style.display = "block"; | |
| input.value = ""; | |
| input.focus(); | |
| } | |
| if (e.key === keybinds.cancel.key && e.ctrlKey === keybinds.cancel.ctrlKey && e.metaKey === keybinds.cancel.metaKey && e.shiftKey === keybinds.cancel.shiftKey) { | |
| modal.style.display = "none"; | |
| } | |
| }); | |
| input.addEventListener("keydown", function (e) { | |
| if (e.key === keybinds.navigateDown.key && e.ctrlKey === keybinds.navigateDown.ctrlKey && e.metaKey === keybinds.navigateDown.metaKey && e.shiftKey === keybinds.navigateDown.shiftKey) { | |
| if (matches.length > 0) { | |
| resetHighlight(); | |
| currentIndex = (currentIndex + 1) % matches.length; | |
| updateHighlight(); | |
| } | |
| } else if (e.key === keybinds.navigateUp.key && e.ctrlKey === keybinds.navigateUp.ctrlKey && e.metaKey === keybinds.navigateUp.metaKey && e.shiftKey === keybinds.navigateUp.shiftKey) { | |
| if (matches.length > 0) { | |
| resetHighlight(); | |
| currentIndex = (currentIndex - 1 + matches.length) % matches.length; | |
| updateHighlight(); | |
| } | |
| } else if (keybinds.select.keys.includes(e.key) && e.ctrlKey === keybinds.select.ctrlKey && e.metaKey === keybinds.select.metaKey && e.shiftKey === keybinds.select.shiftKey) { | |
| if (currentIndex >= 0 && matches[currentIndex].click) { | |
| matches[currentIndex].click(); | |
| modal.style.display = "none"; | |
| } | |
| } | |
| else if (e.key === keybinds.newTabSelect.key && e.ctrlKey === keybinds.newTabSelect.ctrlKey && e.metaKey === keybinds.newTabSelect.metaKey && e.shiftKey === keybinds.newTabSelect.shiftKey) { | |
| if (currentIndex >= 0 && matches[currentIndex].click) { | |
| window.open(matches[currentIndex].href, '_blank'); | |
| modal.style.display = "none"; | |
| } | |
| } | |
| else if (e.key === keybinds.cancel.key && e.ctrlKey === keybinds.cancel.ctrlKey && e.metaKey === keybinds.cancel.metaKey && e.shiftKey === keybinds.cancel.shiftKey) { | |
| modal.style.display = "none"; | |
| } | |
| }); | |
| input.addEventListener("input", function (e) { | |
| let searchTerm = input.value.toLowerCase(); | |
| let isRegex = searchTerm.startsWith("$"); | |
| let isHash = searchTerm.startsWith("#"); | |
| if (isRegex) { | |
| searchTerm = searchTerm.substring(1); | |
| matches = Array.from(document.querySelectorAll('a, button')).filter(el => el.innerText && new RegExp(searchTerm, "i").test(el.innerText.toLowerCase())); | |
| } else { | |
| matches = Array.from(document.querySelectorAll('a, button')).filter(el => { | |
| if (isHash) { | |
| return (el.innerText && el.innerText.toLowerCase().includes(searchTerm.substring(1)) && el.href && /#.+/.test(el.href)) || (el.href && el.href.includes(searchTerm)); | |
| } else { | |
| return el.innerText && el.innerText.toLowerCase().includes(searchTerm) && (!el.href || !/#.+/.test(el.href)); | |
| } | |
| }); | |
| } | |
| // fk off the junk | |
| let seen = new Set(); | |
| matches = matches.filter(el => { | |
| let innerText = el.innerText.trim(); | |
| let href = el.href || ""; | |
| if (innerText === "" || href === "") { | |
| return false; | |
| } | |
| let identifier = innerText + href; | |
| if (seen.has(identifier)) { | |
| return false; | |
| } | |
| seen.add(identifier); | |
| return true; | |
| }); | |
| matches.sort((a, b) => { | |
| let aInnerText = a.innerText.toLowerCase(); | |
| let bInnerText = b.innerText.toLowerCase(); | |
| if (aInnerText === searchTerm && bInnerText !== searchTerm) { | |
| return -1; | |
| } | |
| if (aInnerText !== searchTerm && bInnerText === searchTerm) { | |
| return 1; | |
| } | |
| return aInnerText.length - bInnerText.length; | |
| }); | |
| resultList.innerHTML = ''; | |
| if (matches.length > 0) { | |
| currentIndex = 0; | |
| } else { | |
| currentIndex = -1; | |
| } | |
| matches.slice(0, 20).forEach((el, i) => { | |
| let li = document.createElement("li"); | |
| Object.assign(li.style, theme.listItem, { cursor: 'pointer' }); | |
| let name = document.createElement("span"); | |
| name.textContent = el.innerText; | |
| li.appendChild(name); | |
| let href = document.createElement("span"); | |
| href.style.textDecoration = theme.href.textDecoration; | |
| if (el.href) { | |
| href.textContent = el.href; | |
| } | |
| li.appendChild(href); | |
| if (i === currentIndex) { | |
| href.style.color = theme.href.selectedColor; | |
| li.style.backgroundColor = theme.listItem.highlightBg; | |
| } else { | |
| href.style.color = theme.href.normalColor; | |
| li.style.backgroundColor = theme.listItem.normalBg; | |
| } | |
| li.addEventListener('click', () => { | |
| if (currentIndex >= 0 && matches[currentIndex].click) { | |
| matches[currentIndex].click(); | |
| modal.style.display = "none"; | |
| } | |
| }); | |
| resultList.appendChild(li); | |
| }); | |
| }); | |
| function resetHighlight() { | |
| Array.from(resultList.children).forEach((el, i) => { | |
| el.style.backgroundColor = theme.listItem.normalBg; | |
| el.lastChild.style.color = theme.href.normalColor; | |
| }); | |
| } | |
| document.addEventListener('focusin', (event) => { | |
| if (modal.style.display === 'block') { | |
| event.preventDefault(); | |
| input.focus(); | |
| } | |
| }); | |
| function updateHighlight() { | |
| if (currentIndex >= 0) { | |
| let highlightedElement = Array.from(resultList.children)[currentIndex]; | |
| highlightedElement.style.backgroundColor = theme.listItem.highlightBg; | |
| highlightedElement.lastChild.style.color = theme.href.selectedColor; | |
| highlightedElement.scrollIntoView({ block: 'nearest', inline: 'start' }); | |
| } | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment