Skip to content

Instantly share code, notes, and snippets.

@Zemanzo
Created April 4, 2024 19:17
Show Gist options
  • Save Zemanzo/8316957e6d5cab2c741265c1160e6753 to your computer and use it in GitHub Desktop.
Save Zemanzo/8316957e6d5cab2c741265c1160e6753 to your computer and use it in GitHub Desktop.
Userscript for Neal's inifinite-craft to add another search bar which scrolls to the exact item, and a button to download the found items JSON.
// ==UserScript==
// @name Exact search
// @namespace https://www.zemanzo.nl/
// @version 2024-03-31
// @description Make searching for short words a bit nicer
// @author Zemanzo
// @match https://neal.fun/infinite-craft/
// @icon https://www.google.com/s2/favicons?sz=64&domain=neal.fun
// @grant none
// ==/UserScript==
const PREFIX = "EXACTSEARCH_";
const INPUT_NAME = PREFIX + "newInput";
const INITIAL_ITEMS = 4;
/**
* Time in seconds. We poll to check if items have been loaded in, if after this amount of time the number is not more than four, we inject the elements anyway.
*/
const MAX_TIMEOUT = 3;
(function() {
'use strict';
let replacementFn, origFn;
document.addEventListener("DOMContentLoaded", () => {
addStyleSheet();
}, false);
const originalAddListener = window.addEventListener;
window.addEventListener = function(...args) {
if (args[0] === "keydown") {
origFn = args[1];
replacementFn = function(...evtArgs) {
if (!window[INPUT_NAME] || window[INPUT_NAME] !== document.activeElement) {
origFn(...evtArgs);
}
};
return originalAddListener(args[0], replacementFn, args[2]);
}
return originalAddListener(...args);
}
const originalRemoveListener = window.removeEventListener;
window.removeEventListener = function(...args) {
if (args[0] === "keydown" && args[1] === origFn) {
return originalRemoveListener(args[0], replacementFn, args[2]);
}
return originalRemoveListener(...args);
}
const INTERVAL_FREQUENCY = 50;
let attempts = 0;
const tryAddSidebar = () => {
attempts++;
if (
attempts >= (MAX_TIMEOUT * 1000) / INTERVAL_FREQUENCY
|| document.getElementsByClassName("item").length > INITIAL_ITEMS
) {
clearInterval(interval);
addSidebar();
}
}
const interval = setInterval(tryAddSidebar, INTERVAL_FREQUENCY);
})();
function addSidebar() {
const sidebar = document.querySelector(".sidebar");
const sidebarTop = document.createElement("div");
sidebarTop.id = PREFIX + "sidebarTop";
window[INPUT_NAME] = document.createElement("input");
window[INPUT_NAME].id = PREFIX + "newInput"
window[INPUT_NAME].placeholder = "Exact search (scroll to...)";
window[INPUT_NAME].addEventListener("input", function(evt) {
evt.stopPropagation();
const inputValue = capitalizeFirstLetter(window[INPUT_NAME].value);
window[INPUT_NAME].value = inputValue;
const foundItem = Array.from(document.querySelectorAll(".item"))
.find(item => item.childNodes[1].data.trim() === inputValue);
if (foundItem) {
foundItem.scrollIntoView({ block: "center", inline: "nearest" });
foundItem.classList.add(`${PREFIX}highlight`);
setTimeout(() => {
foundItem.classList.remove(`${PREFIX}highlight`);
}, 1000);
}
});
sidebarTop.appendChild(window[INPUT_NAME]);
const button = document.createElement("button");
button.id = PREFIX + "saveProgressButton";
button.innerHTML = "💾";
button.title = "Save progress to a file on disk";
button.addEventListener("click", downloadProgressAsJson, false);
sidebarTop.appendChild(button);
sidebar.insertAdjacentElement("afterbegin", sidebarTop);
}
function downloadProgressAsJson() {
const a = document.createElement("a");
const blob = new Blob([window.localStorage["infinite-craft-data"]], {type: "text/json"});
const url = URL.createObjectURL(blob);
a.setAttribute('href', url);
a.setAttribute('download', `infinite-craft-backup-${Date.now()}.json`)
a.click();
}
const latinRegex = /[a-z]/;
function capitalizeFirstLetter(string) {
const firstChar = string.charAt(0);
if (!latinRegex.test(firstChar)) {
return string;
}
return firstChar.toUpperCase() + string.slice(1);
}
function addStyleSheet() {
const styleElement = document.createElement("style");
styleElement.type = "text/css";
styleElement.dataset.description = "Injected by Exact Search userscript";
document.head.appendChild(styleElement);
styleElement.addEventListener("load", () => {
if (styleElement.sheet) {
for (const rule of INJECTED_CSS_RULES) {
styleElement.sheet.insertRule(rule.trim());
}
}
});
}
const INJECTED_CSS_RULES = [`
#${PREFIX}sidebarTop {
position: sticky;
top: 0;
left: 0;
width: 100%;
display: flex;
z-index: 9999;
background: var(--sidebar-bg);
}
`, `
#${PREFIX}newInput {
background: var(--sidebar-bg);
color: var(--text-color);
width: 100%;
flex: 1;
border: none;
border-bottom: 1px solid var(--border-color);
padding: 8px;
}
`, `
#${PREFIX}newInput:focus {
outline: none;
}
`, `
#${PREFIX}saveProgressButton {
cursor: pointer;
padding: 0 8px;
background: var(--sidebar-bg);
color: var(--text-color);
border: none;
border-left: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
}
`, `
.empty-sidebar {
top: 50px !important;
}
`, `
.items {
min-height: calc(100svh - 109px) !important;
}
`, `
@keyframes ${PREFIX}highlight-keyframes {
0% {
transform: scale(1,1);
}
50% {
transform: scale(1.4,1.4);
}
100% {
transform: scale(1,1);
}
}
`, `
.${PREFIX}highlight {
animation-name: ${PREFIX}highlight-keyframes;
animation-duration: .5s;
}
`];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment