Last active
February 27, 2025 13:45
-
-
Save mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491 to your computer and use it in GitHub Desktop.
Derpibooru more tag slots on upload page
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 Booru Tag Slots | |
| // @version 1.0 | |
| // @author mdashlw | |
| // @namespace Booru Scripts | |
| // @match https://derpibooru.org/* | |
| // @match https://trixiebooru.org/* | |
| // @match https://tantabus.ai/* | |
| // @match https://furbooru.org/* | |
| // @grant none | |
| // @description 2/27/2025 | |
| // @updateURL https://gist.github.com/mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491/raw/booru-tag-slots.user.js | |
| // @downloadURL https://gist.github.com/mdashlw/0a2b4b2f7e37c4e0f9f59946ec339491/raw/booru-tag-slots.user.js | |
| // ==/UserScript== | |
| if (location.pathname !== "/images/new") { | |
| return; | |
| } | |
| function getTagInputs() { | |
| const _firstTagInput = localStorage.getItem("tag_input"); | |
| const firstTagInput = { | |
| name: "", | |
| value: _firstTagInput ? JSON.parse(_firstTagInput) : "", | |
| }; | |
| const _extraTagInputs = localStorage.getItem("tag_inputs"); | |
| const extraTagInputs = _extraTagInputs ? JSON.parse(_extraTagInputs) : []; | |
| return [firstTagInput, ...extraTagInputs]; | |
| } | |
| let tagInputList = getTagInputs(); | |
| function loadTagInput({ value }) { | |
| const tagInputArea = document.getElementById("image_tag_input"); | |
| if (!tagInputArea) { | |
| return; | |
| } | |
| for (const tag of value.split(",")) { | |
| tagInputArea.dispatchEvent( | |
| new CustomEvent("addtag", { detail: { name: tag }, bubbles: true }), | |
| ); | |
| } | |
| } | |
| function saveTagInput(index, tagInput) { | |
| localStorage.setItem( | |
| "tag_inputs", | |
| JSON.stringify(tagInputList.slice(1).filter((ti) => ti.name || ti.value)), | |
| ); | |
| if (index === 0) { | |
| localStorage.setItem("tag_input", JSON.stringify(tagInput.value)); | |
| } | |
| } | |
| document.getElementById("tagsinput-save")?.remove(); | |
| document.getElementById("tagsinput-load")?.remove(); | |
| const block = document.createElement("div"); | |
| block.className = "block"; | |
| const blockContent = document.createElement("div"); | |
| blockContent.className = "block__content"; | |
| block.appendChild(blockContent); | |
| function createTagInputFlex(index, tagInput) { | |
| const field = document.createElement("div"); | |
| field.className = "flex field"; | |
| const slotNameInput = document.createElement("input"); | |
| slotNameInput.type = "text"; | |
| slotNameInput.className = "input"; | |
| slotNameInput.value = tagInput.name; | |
| if (index === 0) { | |
| slotNameInput.placeholder = "default"; | |
| slotNameInput.setAttribute("disabled", ""); | |
| } else { | |
| slotNameInput.addEventListener("keydown", (event) => { | |
| if (event.keyCode === 38 || event.keyCode === 40) { | |
| event.preventDefault(); | |
| } | |
| }); | |
| slotNameInput.addEventListener("keyup", (event) => { | |
| if (event.keyCode === 38) { | |
| // arrow up | |
| if (field.previousElementSibling?.previousElementSibling) { | |
| const oldIndex = tagInputList.indexOf(tagInput); | |
| const newIndex = oldIndex - 1; | |
| const oldElement = tagInputList[newIndex]; | |
| tagInputList[newIndex] = tagInput; | |
| tagInputList[oldIndex] = oldElement; | |
| field.parentElement.insertBefore( | |
| field.previousElementSibling, | |
| field.nextElementSibling, | |
| ); | |
| } | |
| } else if (event.keyCode === 40) { | |
| // arrow down | |
| if (field.nextElementSibling?.nextElementSibling) { | |
| const oldIndex = tagInputList.indexOf(tagInput); | |
| const newIndex = oldIndex + 1; | |
| const oldElement = tagInputList[newIndex]; | |
| tagInputList[newIndex] = tagInput; | |
| tagInputList[oldIndex] = oldElement; | |
| field.parentElement.insertBefore(field.nextElementSibling, field); | |
| } | |
| } | |
| }); | |
| } | |
| const loadButton = document.createElement("button"); | |
| loadButton.type = "button"; | |
| loadButton.className = | |
| "button button--state-warning button--separate-left button--bold"; | |
| loadButton.textContent = "Load"; | |
| loadButton.addEventListener("click", () => { | |
| loadTagInput(tagInput); | |
| }); | |
| const valueInput = document.createElement("input"); | |
| valueInput.type = "text"; | |
| valueInput.className = "input spacing-left flex__grow"; | |
| valueInput.value = tagInput.value; | |
| const saveButton = document.createElement("button"); | |
| saveButton.type = "button"; | |
| saveButton.className = | |
| "button button--state-success button--separate-left button--bold"; | |
| saveButton.textContent = "Save"; | |
| saveButton.addEventListener("click", () => { | |
| tagInput.name = slotNameInput.value; | |
| tagInput.value = valueInput.value; | |
| saveTagInput(index, tagInput); | |
| }); | |
| const deleteButtonWrapper = document.createElement("label"); | |
| deleteButtonWrapper.className = "flex flex--centered button--separate-left"; | |
| const deleteButton = document.createElement("a"); | |
| const faTrash = document.createElement("i"); | |
| faTrash.className = "fa fa-trash"; | |
| deleteButton.href = "#"; | |
| deleteButton.append(faTrash, new Text(" Delete")); | |
| deleteButton.addEventListener("click", (event) => { | |
| event.preventDefault(); | |
| field.remove(); | |
| tagInputList = tagInputList.filter((ti) => ti !== tagInput); | |
| }); | |
| deleteButtonWrapper.appendChild(deleteButton); | |
| if (index === 0) { | |
| deleteButtonWrapper.style.visibility = "hidden"; | |
| } | |
| field.append( | |
| slotNameInput, | |
| loadButton, | |
| valueInput, | |
| saveButton, | |
| deleteButtonWrapper, | |
| ); | |
| return field; | |
| } | |
| for (const [index, tagInput] of tagInputList.entries()) { | |
| blockContent.appendChild(createTagInputFlex(index, tagInput)); | |
| } | |
| const addButton = document.createElement("button"); | |
| addButton.type = "button"; | |
| addButton.className = "button"; | |
| addButton.addEventListener("click", () => { | |
| const tagInput = { name: "", value: "" }; | |
| addButton.insertAdjacentElement( | |
| "beforebegin", | |
| createTagInputFlex(tagInputList.length, tagInput), | |
| ); | |
| tagInputList.push(tagInput); | |
| }); | |
| const faPlus = document.createElement("i"); | |
| faPlus.className = "fa fa-plus"; | |
| addButton.append(faPlus, new Text(" Add")); | |
| blockContent.appendChild(addButton); | |
| document | |
| .getElementById("tagsinput-clear") | |
| ?.insertAdjacentElement("afterend", block); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment