您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Show a tag's fandom, its synned/unsynned status, and what it's synned to in the tag search results!!
当前为
// ==UserScript== // @name AO3: [Wrangling] Show fandoms in Tag Search! // @description Show a tag's fandom, its synned/unsynned status, and what it's synned to in the tag search results!! // @version 2.0.0 // @author owlwinter // @namespace N/A // @license MIT license // @match *://*.archiveofourown.org/tags/search?* // @grant none // ==/UserScript== (function() { 'use strict'; //So things like getElementsByTagName get a nodelist, not an array //This lets us convert the nodelists into an actual array so we can use array functions on it //See https://stackoverflow.com/questions/5145032/whats-the-use-of-array-prototype-slice-callarray-0 const array = a => Array.prototype.slice.call(a, 0) //Checks if tag is canonized const check_canon = function check_canon(xhr) { return xhr.responseXML.documentElement.querySelector("#tag_canonical")?.checked } //Returns array of a tag's fandoms //Does this by peeking at all the "fandoms to remove" on the tag's edit form and putting them into a list const check_fandoms = function check_fandoms(xhr) { const fandoms = [] const checks = array(xhr.responseXML.documentElement.querySelectorAll("label > input[name='tag[associations_to_remove][]']")).filter(foo => foo.id.indexOf("parent_Fandom_associations_to_remove") != -1); for (const check of checks) { const name = check.parentElement.nextElementSibling.innerText fandoms.push(name) } return fandoms } //Returns true or false if a tag is synned or not const check_issynned = function check_issynned(xhr) { if (xhr.responseXML.documentElement.querySelector("#tag_syn_string") != null) { return xhr.responseXML.documentElement.querySelector("#tag_syn_string").value } return false } //Returns what a tag is synned to, or an empty string if its unsynned const check_synnedto = function check_synnedto(xhr) { if (xhr.responseXML.documentElement.querySelector("#tag_syn_string") != null) { return xhr.responseXML.documentElement.querySelector("#tag_syn_string").value } return "" } // called for each tag in the search results list // `url` is the url of the edit tag page // `a` is the link, whose color we will change based on whether the tag is a syn // `result` is the span with the "Loading..." in it where we will put the results of our request const get_fandoms = function get_fandoms(url, a, result) { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = function xhr_onreadystatechange() { if (xhr.readyState == xhr.DONE ) { if (xhr.status == 200) { // the "Loading..." text that we show while our script is running is in italics - so clear that result.style.fontStyle = ""; // gets the info on the tag const fandoms = check_fandoms(xhr) const synned = check_issynned(xhr) const canon = check_canon(xhr) // figure out the text to display based on whether there were any fandoms or not const text = fandoms.length == 0 ? "Unwrangled" : fandoms.join(", "); result.innerText = " - " + text; // if the tag is synned, leave it alone, otherwise make the tag's text blue if (!synned & !canon) { a.style.color = "#00C"; } // hovering over a blue link defaults to a purple background that is just totally unreadable, this class // makes the hover background color white instead if (!synned) { a.classList.toggle("not_synned") } } else if (xhr.status == 429) { // ~ao3jail result.innerText = " - Rate limited. Sorry :(" } else { result.innerText = " - Unexpected error, check the console" console.log(xhr) } } } xhr.open("GET", url) xhr.responseType = "document" xhr.send() } // called for each tag in the search results list // `url` is the url of the edit tag page // `a` is the link, whose color we will change based on whether the tag is a syn // `result` is the span with the "Loading..." in it where we will put the results of our request const get_synnedto = function get_synnedto(url, a, result) { const xhr = new XMLHttpRequest(); xhr.onreadystatechange = function xhr_onreadystatechange() { if (xhr.readyState == xhr.DONE ) { if (xhr.status == 200) { // the "Loading..." text that we show while our script is running is in italics - so clear that result.style.fontStyle = ""; // gets what the tag is synned to const synned = check_issynned(xhr) const synnedto = check_synnedto(xhr) const canon = check_canon(xhr) result.style.fontStyle = "italic"; if (canon) { result.innerText = "" return } //If tag is synned, set text to " → Syn", otherwise set text to empty string result.innerText = synned ? " → " + synnedto : "" // if the tag is synned, leave it alone, otherwise make the tag's text blue if (!synned & !canon) { a.style.color = "#00C"; } // hovering over a blue link defaults to a purple background that is just totally unreadable, this class // makes the hover background color white instead if (!synned) { a.classList.toggle("not_synned") } } else if (xhr.status == 429) { // ~ao3jail result.innerText = " - Rate limited. Sorry :(" } else { result.innerText = " - Unexpected error, check the console" console.log(xhr) } } } xhr.open("GET", url) xhr.responseType = "document" xhr.send() } const button = document.createElement("button") const button2 = document.createElement("button") const buttondiv = document.createElement("div") const do_thing = function do_thing(e) { e.preventDefault() // find each item in the tag search results list const search_results = document.querySelector("#main > ol.tag.index.group").getElementsByClassName("tag") for (const a of search_results) { const span = a.parentElement; // this is the span that will hold the result when the request finishes const loading = document.createElement("span"); //Before our request finishes, shows a loading text so user knows something is happening loading.innerText = " - Loading..." loading.style.fontStyle = "italic" span.appendChild(loading); // trigger xhr (asynchronous) get_fandoms(a.href + "/edit", a, loading); } // add the unsynned color explanation text to the top of the page, replacing the button const div = document.createElement("div") div.style.fontSize = "0.875rem" div.style.fontStyle = "italic" div.appendChild(document.createTextNode("")) const span = document.createElement("span") span.style.color = "#00C" span.innerText = "Blue" div.appendChild(span) div.appendChild(document.createTextNode(" means tag is not synned")) div.style.marginTop = "10px" //Removes both buttons after either is clicked button.parentElement.parentElement.appendChild(div) button.parentElement.parentElement.removeChild(buttondiv) } const do_thing2 = function do_thing2(e) { e.preventDefault() // find each item in the tag search results list const search_results = document.querySelector("#main > ol.tag.index.group").getElementsByClassName("tag") for (const a of search_results) { const span = a.parentElement; // this is the span that will hold the result when the request finishes const loading = document.createElement("span"); //Before our request finishes, shows a loading text so user knows something is happening loading.innerText = " - Loading..." loading.style.fontStyle = "italic" span.appendChild(loading); // trigger xhr (asynchronous) get_synnedto(a.href + "/edit", a, loading); } // add the unsynned color explanation text to the top of the page, replacing the button const div = document.createElement("div") div.style.fontSize = "0.875rem" div.style.fontStyle = "italic" div.appendChild(document.createTextNode("")) const span = document.createElement("span") span.style.color = "#00C" span.innerText = "Blue" div.appendChild(span) div.appendChild(document.createTextNode(" means tag is not synned")) div.style.marginTop = "10px" //Removes both buttons after either is clicked button2.parentElement.parentElement.appendChild(div) button2.parentElement.parentElement.removeChild(buttondiv) } // Adds the load fandom button - since this can get someone rate limited, // we definitely don't want to have it happen automatically button.innerText = "Load fandoms" button.addEventListener("click", do_thing) button.style.display = "inline" button.style.fontSize = "0.627rem" button.style.marginTop = "10px" //Adds the load synned to button button2.innerText = "See synned to" button2.addEventListener("click", do_thing2) button2.style.display = "inline" button2.style.fontSize = "0.627rem" button2.style.marginTop = "10px" button2.style.marginLeft = "5px" buttondiv.append(button) buttondiv.append(button2) document.querySelector("#main > h3").append(buttondiv) // removes the color background color of the not synned tags are when the user's cursor hovers over them // trust me, keeping the same magenta but with the blue text was atrocious // comment out the next three lines out at your own risk // ......you really don't want to // .........don't say i didn't warn you...., const style = document.createElement("style") style.innerHTML = ".not_synned:hover { background-color: white !important; }" document.head.appendChild(style) })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址