Adds a blacklist to Fur Affinity. Also adds the ability to replace typed terms with other terms. If installed correctly you should see a link titled "Edit Blacklist" below the search box on FA's search page.
当前为
// ==UserScript==
// @name FA Blacklist
// @namespace https://greasyfork.org/en/scripts/453103-fa-blacklist
// @version 1.0.2
// @description Adds a blacklist to Fur Affinity. Also adds the ability to replace typed terms with other terms. If installed correctly you should see a link titled "Edit Blacklist" below the search box on FA's search page.
// @author Nin
// @license GNU GPLv3
// @match https://www.furaffinity.net/*
// @icon https://www.google.com/s2/favicons?domain=furaffinity.net
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-end
// ==/UserScript==
// How many tabs to add between the blacklist and search
var tabBuffer = "\t".repeat(30);
// Save a script setting
function saveUserData(key, value) {
'use strict';
GM_setValue(key, JSON.stringify(value));
};
// Load a script setting
async function loadUserData(key, defaultValue) {
'use strict';
let data = await GM_getValue(key);
if (data === undefined) {
return defaultValue;
}
return JSON.parse(data);
};
// Add extra search settings for using this script to FA's Search Settings section
function generateSearchSettings(blacklist, replace) {
'use strict';
if (!window.location.pathname.startsWith("/controls/site-settings/")){
return;
}
let replaceList = [];
for (const property in replace) {
replaceList.push(property + "=" + replace[property]);
}
const replaceString = replaceList.join(", ");
prependSearchSetting(
"Find and Replace",
"A comma separated list of search terms to replace. In the format: <br><i><span style='color:darkgray'>term1=replacement1, term2=replacement2, tf=transformation</span></i><br> Replacements can contain advanced FA queries: <br><i><span style='color:darkgray'>noodles=(dragons|snakes), ramen=(snakes&soup)</span></i>",
replaceString, "replace", "Comma separated list...")
const blacklistString = blacklist.join(", ");
prependSearchSetting(
"Blacklist",
"A comma separated list of words to blacklist. In the format: <br><i><span style='color:darkgray'>these, are, search, terms, I, dislike</span></i>",
blacklistString, "blacklist", "Comma separated list...")
const saveButton = document.getElementsByName("save_settings")[0];
console.log(saveButton);
saveButton.addEventListener("click", function(){
let blacklist = document.getElementById("blacklist").value.replaceAll(/\s/g, "").split(",");
let replaceList = document.getElementById("replace").value.replaceAll(/\s/g, "").split(",");
let replace = {};
for (const replaceText of replaceList) {
const split = replaceText.split("=");
replace[split[0]] = split[1];
}
saveUserData("blacklist", blacklist);
saveUserData("replace", replace);
});
}
// Add a search setting option to the start of the list of search settings on the Global Site Settings page
function prependSearchSetting(title, description, data, id, placeholder) {
'use strict';
let html = `
<div class="control-panel-item-container">
<div class="control-panel-item-name">
<h4>${title}</h4>
</div>
<div class="control-panel-item-description">${description}</div>
<div class="control-panel-item-options">
<div class="select-dropdown">
<input type="text" id="${id}" value="${data}" placeholder="${placeholder}" class="textbox" autocomplete="off">
</div>
</div>
</div>`
const element = document.getElementsByClassName("section-body")[2];
element.innerHTML = html + element.innerHTML;
}
// Add a link to edit the blacklist to search pages
function addSearchSettingsLink(){
'use strict';
if (!window.location.pathname.startsWith("/search/")){
return;
}
const searchBox = document.getElementsByClassName("browser-sidebar-search-box")[0];
searchBox.outerHTML += "<a href='https://www.furaffinity.net/controls/site-settings/#blacklist' style='color:darkgray;float:right'>Edit Blacklist</a>"
}
// Remove the added query text from the query inputs on page load
function cleanInput() {
'use strict';
document.getElementsByName("q").forEach(function(input){
if (input.value !== "") {
console.log("Actual Search: " + input.value);
}
if (input.value.includes("\t")) {
input.value = input.value.substring(0, input.value.indexOf("\t"))
}
// Remove any sent zero width spaces
input.value = input.value.replaceAll("\u200B", "");
});
};
// Remove the blacklist text from FA's list of tags you searched
function cleanQueryStats(blacklist) {
'use strict';
if (document.getElementById("query-stats") !== null) {
var queryStats = document.getElementById("query-stats").children;
while (blacklist.includes(queryStats[queryStats.length - 1].children[0].children[0].innerHTML)) {
queryStats[queryStats.length - 1].remove();
}
}
};
// Replace keywords in the query string according to the specified replacements
function replaceKeywords(replace) {
'use strict';
document.getElementsByName("q").forEach(function(input){
let append = "";
for (const property in replace) {
const regex = new RegExp('(?<![(|&])\\b' + property + '\\b(?![)|&])', "gi");
let found = input.value.match(regex);
if (found !== null) {
for (const result of found) {
append += " " + replace[property];
}
}
// Insert a zero width space between each replaced character so FA ignores it
input.value = input.value.replaceAll(regex, [...property].join("\u200B"));
}
input.value += append;
});
};
// Adds the blacklist text to the end of all query forms
function addBlacklist(blacklist) {
'use strict';
document.getElementsByName("q").forEach(function(input){
if (blacklist.length && input.value.match(/\b-bl\b/) === null){
input.value += " -" + blacklist.join(" -");
}
});
};
// Adds a buffer of tabs to hide the added query text
function addBuffer() {
'use strict';
document.getElementsByName("q").forEach(function(input){
input.value += tabBuffer;
});
};
// Adds an onsubmit trigger for the element with the given ID to add the blacklist
function attachHandlers(elementID, blacklist, replace) {
'use strict';
var element = document.getElementById(elementID);
if (element !== null) {
element.addEventListener("submit", function(){
addBuffer();
replaceKeywords(replace);
addBlacklist(blacklist);
});
}
};
// Return some URI text that can be appended to a URI to add the blacklist
function blacklistURI(blacklist) {
'use strict';
return encodeURIComponent(tabBuffer + (blacklist.length ? "-" : "") + blacklist.join(" -"));
}
// If we somehow end up searching without the blacklist, redirect to add the blacklist
function redirect(blacklist) {
'use strict';
if ((!window.location.pathname.startsWith("/search/"))
|| (window.location.pathname === "/search/" && window.location.search === "")
|| window.location.pathname.includes("09%")
|| window.location.search.includes("09%")){
return;
}
console.log("Redirecting to add blacklist...");
window.location.href = window.location.href + blacklistURI(blacklist);
}
// Update the links on the tags of images to add the blacklist
function updateTags(blacklist) {
'use strict';
if (!window.location.pathname.startsWith("/view/")){
return;
}
[...document.getElementsByClassName("tags")].forEach(function(tag){
tag.firstChild.href += blacklistURI(blacklist);
console.log(tag.firstChild.href);
});
}
async function main() {
'use strict';
const blacklist = loadUserData("blacklist", []);
const replace = loadUserData("replace", {});
addSearchSettingsLink();
cleanInput();
cleanQueryStats(await blacklist);
attachHandlers("search-form", await blacklist, await replace);
attachHandlers("searchbox", await blacklist, await replace);
generateSearchSettings(await blacklist, await replace);
redirect(await blacklist);
updateTags(await blacklist);
};
main();