Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name AO3 Blocker
- // @description Fork of ao3 savior; blocks works based on certain conditions
- // @author JacenBoy
- // @namespace https://github.com/JacenBoy/ao3-blocker#readme
- // @license Apache-2.0; http://www.apache.org/licenses/LICENSE-2.0
- // @match http*://archiveofourown.org/*
- // @version 3.0
- // @require https://openuserjs.org/src/libs/sizzle/GM_config.js
- // @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js
- // @grant GM.getValue
- // @grant GM.setValue
- // @run-at document-end
- // @downloadURL https://update.greasyfork.org/scripts/409956/AO3%20Blocker.user.js
- // @updateURL https://update.greasyfork.org/scripts/409956/AO3%20Blocker.meta.js
- // ==/UserScript==
- /* globals $, GM_config */
- (function () {
- "use strict";
- window.ao3Blocker = {};
- // Initialize GM_config options
- GM_config.init({
- "id": "ao3Blocker",
- "title": "AO3 Blocker",
- "fields": {
- "tagBlacklist": {
- "label": "Lista negra de etiquetas",
- "type": "text",
- "default": ""
- },
- "tagWhitelist": {
- "label": "Lista blanca de etiquetas",
- "type": "text",
- "default": ""
- },
- "tagHighlights": {
- "label": "Etiquetas resaltadas",
- "type": "text",
- "default": ""
- },
- "authorBlacklist": {
- "label": "Lista negra de fickers",
- "type": "text",
- "default": ""
- },
- "titleBlacklist": {
- "label": "Lista negra para títulos",
- "type": "text",
- "default": ""
- },
- "summaryBlacklist": {
- "label": "Lista negra para descripciones",
- "type": "text",
- "default": ""
- },
- "showReasons": {
- "label": "Mostrar el motivo del bloqueo",
- "type": "checkbox",
- "default": true
- },
- "showPlaceholders": {
- "label": "Mostrar placeholder",
- "type": "checkbox",
- "default": true
- },
- "alertOnVisit": {
- "label": "Alertar cuando abres una obra bloqueada",
- "type": "checkbox",
- "default": false
- },
- "debugMode": {
- "label": "Modo Debug",
- "type": "checkbox",
- "default": false
- }
- },
- "events": {
- "save": () => {
- window.ao3Blocker.updated = true;
- alert("Tus cambios han sido guardados.");
- },
- "close": () => {
- if (window.ao3Blocker.updated) location.reload();
- },
- "init": () => {
- // Config is now available
- window.ao3Blocker.config = {
- "showReasons": GM_config.get("showReasons"),
- "showPlaceholders": GM_config.get("showPlaceholders"),
- "alertOnVisit": GM_config.get("alertOnVisit"),
- "authorBlacklist": GM_config.get("authorBlacklist").toLowerCase().split(/,(?:\s)?/g).map(i => i.trim()),
- "titleBlacklist": GM_config.get("titleBlacklist").toLowerCase().split(/,(?:\s)?/g).map(i => i.trim()),
- "tagBlacklist": GM_config.get("tagBlacklist").toLowerCase().split(/,(?:\s)?/g).map(i => i.trim()),
- "tagWhitelist": GM_config.get("tagWhitelist").toLowerCase().split(/,(?:\s)?/g).map(i => i.trim()),
- "tagHighlights": GM_config.get("tagHighlights").toLowerCase().split(/,(?:\s)?/g).map(i => i.trim()),
- "summaryBlacklist": GM_config.get("summaryBlacklist").toLowerCase().split(/,(?:\s)?/g).map(i => i.trim()),
- "debugMode": GM_config.get("debugMode")
- }
- addMenu();
- addStyle();
- setTimeout(checkWorks, 10);
- }
- },
- "css": ".config_var {display: grid; grid-template-columns: repeat(2, 0.7fr);}"
- });
- // Define the custom styles for the script
- const STYLE = "\n html body .ao3-blocker-hidden {\n display: none;\n }\n \n .ao3-blocker-cut {\n display: none;\n }\n \n .ao3-blocker-cut::after {\n clear: both;\n content: '';\n display: block;\n }\n \n .ao3-blocker-reason {\n margin-left: 5px;\n }\n \n .ao3-blocker-hide-reasons .ao3-blocker-reason {\n display: none;\n }\n \n .ao3-blocker-unhide .ao3-blocker-cut {\n display: block;\n }\n \n .ao3-blocker-fold {\n align-items: center;\n display: flex;\n justify-content: flex-start;\n }\n \n .ao3-blocker-unhide .ao3-blocker-fold {\n border-bottom: 1px dashed;\n margin-bottom: 15px;\n padding-bottom: 5px;\n }\n \n button.ao3-blocker-toggle {\n margin-left: auto;\n }\n";
- // addMenu() - Add a custom menu to the AO3 menu bar to control our configuration options
- function addMenu() {
- // Define our custom menu and add it to the AO3 menu bar
- const headerMenu = $("ul.primary.navigation.actions");
- const blockerMenu = $("<li class=\"dropdown\"></li>").html("<a>AO3 Blocker</a>");
- headerMenu.find("li.search").before(blockerMenu);
- const dropMenu = $("<ul class=\"menu dropdown-menu\"></ul>");
- blockerMenu.append(dropMenu);
- // Add the "Toggle Block Reason" option to the menu
- const reasonButton = $("<li></li>").html(`<a>${window.ao3Blocker.config.showReasons ? "Ocultar" : "Mostrar"} motivo de bloqueo</a>`);
- reasonButton.on("click", () => {
- if (window.ao3Blocker.config.showReasons) {
- GM_config.set("showReasons", false);
- } else {
- GM_config.set("showReasons", true);
- }
- GM_config.save();
- reasonButton.html(`<a>${window.ao3Blocker.config.showReasons ? "Ocultar" : "Mostrar"} motivo de bloqueo</a>`);
- });
- dropMenu.append(reasonButton);
- // Add the "Toggle Work Placeholder" option to the menu
- const placeholderButton = $("<li></li>").html(`<a>${window.ao3Blocker.config.showPlaceholders ? "Ocultar" : "Mostrar"} placeholder de obra</a>`);
- placeholderButton.on("click", () => {
- if (window.ao3Blocker.config.showPlaceholders) {
- GM_config.set("showPlaceholders", false);
- } else {
- GM_config.set("showPlaceholders", true);
- }
- GM_config.save();
- placeholderButton.html(`<a>${window.ao3Blocker.config.showPlaceholders ? "Ocultar" : "Mostrar"} placeholder de obra</a>`);
- });
- dropMenu.append(placeholderButton);
- // Add the "Toggle Block Alerts" option to the menu
- const alertButton = $("<li></li>").html(`<a>${window.ao3Blocker.config.alertOnVisit ? "No mostrar" : "Mostrar"} aviso de obras bloqueadas</a>`);
- alertButton.on("click", () => {
- if (window.ao3Blocker.config.alertOnVisit) {
- GM_config.set("alertOnVisit", false);
- } else {
- GM_config.set("alertOnVisit", true);
- }
- GM_config.save();
- alertButton.html(`<a>${window.ao3Blocker.config.alertOnVisit ? "No mostrar" : "Mostrar"} aviso de obras bloqueadas</a>`);
- });
- dropMenu.append(alertButton);
- // Add an option to show the config dialog
- const settingsButton = $("<li></li>").html("<a>Configuración</a>");
- settingsButton.on("click", () => { GM_config.open(); });
- dropMenu.append(settingsButton);
- }
- // Define the CSS namespace. All CSS classes are prefixed with this.
- const CSS_NAMESPACE = "ao3-blocker";
- // addStyle() - Apply the custom stylesheet to AO3
- function addStyle() {
- const style = $(`<style class="${CSS_NAMESPACE}"></style>`).html(STYLE);
- $("head").append(style);
- }
- // getCut(work) - Move standard AO3 work information (tags, summary, etc.) to a custom element for blocked works. This will be hidden by default on blocked works but can be shown if thre user chooses.
- function getCut(work) {
- const cut = $(`<div class="${CSS_NAMESPACE}-cut"></div>`);
- $.makeArray(work.children()).forEach((child) => {
- return cut.append(child);
- });
- return cut;
- }
- // getFold(reason) - Create the work placeholder for blocked works. Optionally, this will show why the work was blocked and give the user the option to unhide it.
- function getFold(reason) {
- const fold = $(`<div class="${CSS_NAMESPACE}-fold"></div>`);
- const note = $(`<span class="${CSS_NAMESPACE}-note"</span>`).text("¡Esta obra está oculta! ");
- fold.html(note);
- fold.append(getReasonSpan(reason));
- fold.append(getToggleButton());
- return fold;
- }
- // getToggleButton() - Create a button that will show or hide the "cut" on blocked works.
- function getToggleButton() {
- const button = $(`<button class="${CSS_NAMESPACE}-toggle"></button>`).text("Unhide");
- const unhideClassFragment = `${CSS_NAMESPACE}-unhide`;
- button.on("click", (event) => {
- const work = $(event.target).closest(`.${CSS_NAMESPACE}-work`);
- if (work.hasClass(unhideClassFragment)) {
- work.removeClass(unhideClassFragment);
- work.find(`.${CSS_NAMESPACE}-note`).text("Esta obra está oculta.");
- $(event.target).text("Unhide");
- } else {
- work.addClass(unhideClassFragment);
- work.find(`.${CSS_NAMESPACE}-note`).text("ℹ️ Esta obra está oculta.");
- $(event.target).text("Hide");
- }
- });
- return button;
- }
- // getReasonSpan(reason) - Create the element that holds the block reason information on blocked works.
- function getReasonSpan(reason) {
- const span = $(`<span class="${CSS_NAMESPACE}-reason"></span>`);
- let text = undefined;
- if (reason.tag) {
- text = `incluye la(s) etiqueta(s): <strong>${reason.tag}</strong>`;
- } else if (reason.author) {
- text = `ficker(s): <strong>${reason.author}</strong>`;
- } else if (reason.title) {
- text = `el título contiene: <strong>${reason.title}</strong>`;
- } else if (reason.summary) {
- text = `la descripción contiene: <strong>${reason.summary}</strong>`;
- }
- if (text) {
- span.html(`(Motivo: ${text}.)`);
- }
- return span;
- }
- // blockWork(work, reason, config) - Replace the standard AO3 work information with the placeholder "fold", and place the "cut" below it, hidden.
- function blockWork(work, reason, config) {
- if (!reason) return;
- if (config.showPlaceholders) {
- const fold = getFold(reason);
- const cut = getCut(work);
- work.addClass(`${CSS_NAMESPACE}-work`);
- work.html(fold);
- work.append(cut);
- if (!config.showReasons) {
- work.addClass(`${CSS_NAMESPACE}-hide-reasons`);
- }
- } else {
- work.addClass(`${CSS_NAMESPACE}-hidden`);
- }
- }
- function matchTermsWithWildCard(term0, pattern0) {
- const term = term0.toLowerCase();
- const pattern = pattern0.toLowerCase();
- if (term === pattern) return true;
- if (pattern.indexOf("*") === -1) return false;
- const lastMatchedIndex = pattern.split("*").filter(Boolean).reduce((prevIndex, chunk) => {
- const matchedIndex = term.indexOf(chunk);
- return prevIndex >= 0 && prevIndex <= matchedIndex ? matchedIndex : -1;
- }, 0);
- return lastMatchedIndex >= 0;
- }
- function isTagWhitelisted(tags, whitelist) {
- const whitelistLookup = whitelist.reduce((lookup, tag) => {
- lookup[tag.toLowerCase()] = true;
- return lookup;
- }, {});
- return tags.some((tag) => {
- return !!whitelistLookup[tag.toLowerCase()];
- });
- }
- function findBlacklistedItem(list, blacklist, comparator) {
- let matchingEntry = void 0;
- list.some((item) => {
- blacklist.some((entry) => {
- const matched = comparator(item.toLowerCase(), entry.toLowerCase());
- if (matched) matchingEntry = entry;
- return matched;
- });
- });
- return matchingEntry;
- }
- function equals(a, b) {
- return a === b;
- }
- function contains(a, b) {
- return a.indexOf(b) !== -1;
- }
- function getBlockReason(_ref, _ref2) {
- const _ref$authors = _ref.authors,
- authors = _ref$authors === undefined ? [] : _ref$authors,
- _ref$title = _ref.title,
- title = _ref$title === undefined ? "" : _ref$title,
- _ref$tags = _ref.tags,
- tags = _ref$tags === undefined ? [] : _ref$tags,
- _ref$summary = _ref.summary,
- summary = _ref$summary === undefined ? "" : _ref$summary;
- const _ref2$authorBlacklist = _ref2.authorBlacklist,
- authorBlacklist = _ref2$authorBlacklist === undefined ? [] : _ref2$authorBlacklist,
- _ref2$titleBlacklist = _ref2.titleBlacklist,
- titleBlacklist = _ref2$titleBlacklist === undefined ? [] : _ref2$titleBlacklist,
- _ref2$tagBlacklist = _ref2.tagBlacklist,
- tagBlacklist = _ref2$tagBlacklist === undefined ? [] : _ref2$tagBlacklist,
- _ref2$tagWhitelist = _ref2.tagWhitelist,
- tagWhitelist = _ref2$tagWhitelist === undefined ? [] : _ref2$tagWhitelist,
- _ref2$summaryBlacklis = _ref2.summaryBlacklist,
- summaryBlacklist = _ref2$summaryBlacklis === undefined ? [] : _ref2$summaryBlacklis;
- if (isTagWhitelisted(tags, tagWhitelist)) {
- return null;
- }
- const blockedTag = findBlacklistedItem(tags, tagBlacklist, matchTermsWithWildCard);
- if (blockedTag) {
- return { tag: blockedTag };
- }
- const author = findBlacklistedItem(authors, authorBlacklist, equals);
- if (author) {
- return { author: author };
- }
- const blockedTitle = findBlacklistedItem([title.toLowerCase()], titleBlacklist, matchTermsWithWildCard);
- if (blockedTitle) {
- return { title: blockedTitle };
- }
- const summaryTerm = findBlacklistedItem([summary.toLowerCase()], summaryBlacklist, contains);
- if (summaryTerm) {
- return { summary: summaryTerm };
- }
- return null;
- }
- const _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
- function getText(element) {
- return $(element).text().replace(/^\s*|\s*$/g, "");
- }
- function selectTextsIn(root, selector) {
- return $.makeArray($(root).find(selector)).map(getText);
- }
- function selectFromWork(container) {
- return _extends({}, selectFromBlurb(container), {
- title: selectTextsIn(container, ".title")[0],
- summary: selectTextsIn(container, ".summary .userstuff")[0]
- });
- }
- function selectFromBlurb(blurb) {
- return {
- authors: selectTextsIn(blurb, "a[rel=author]"),
- tags: [].concat(selectTextsIn(blurb, "a.tag"), selectTextsIn(blurb, ".required-tags .text")),
- title: selectTextsIn(blurb, ".header .heading a:first-child")[0],
- summary: selectTextsIn(blurb, "blockquote.summary")[0]
- };
- }
- // checkWorks() - Scan all works on the page and block them if they match one of the conditions set by the user.
- function checkWorks() {
- const debugMode = window.ao3Blocker.config.debugMode;
- const config = window.ao3Blocker.config;
- // If this is a work page, save the element for future use.
- const workContainer = $("#main.works-show") || $("#main.chapters-show");
- let blocked = 0;
- let total = 0;
- if (debugMode) {
- console.groupCollapsed("AO3 BLOCKER");
- if (!config) {
- console.warn("Exiting due to missing config.");
- return;
- }
- }
- // Loop through all works on the search page and check if they match one of the conditions.
- $.makeArray($("li.blurb")).forEach((blurb) => {
- blurb = $(blurb);
- const blockables = selectFromBlurb(blurb);
- const reason = getBlockReason(blockables, config);
- total++;
- if (reason) {
- blockWork(blurb, reason, config);
- blocked++;
- if (debugMode) {
- console.groupCollapsed(`- blocked ${blurb.attr("id")}`);
- console.log(blurb.html(), reason);
- console.groupEnd();
- }
- } else if (debugMode) {
- console.groupCollapsed(` skipped ${blurb.attr("id")}`);
- console.log(blurb.html());
- console.groupEnd();
- }
- blockables.tags.forEach((tag) => {
- if (config.tagHighlights.includes(tag.toLowerCase())) {
- blurb.css("background-color", "rgba(255,255,0,0.1)");
- if (debugMode) {
- console.groupCollapsed(`? highlighted ${blurb.attr("id")}`);
- console.log(blurb.html());
- console.groupEnd();
- }
- }
- });
- });
- // If this is a work page, the work was navigated to from another site (i.e. an external link), and the user had block alerts enabled, show a warning.
- if (config.alertOnVisit && workContainer && document.referrer.indexOf("//archiveofourown.org") === -1) {
- const blockables = selectFromWork(workContainer);
- const reason = getBlockReason(blockables, config);
- if (reason) {
- blocked++;
- blockWork(workContainer, reason, config);
- }
- }
- if (debugMode) {
- console.log(`Se bloquearon ${blocked} de un total de ${total} obras`);
- console.groupEnd();
- }
- }
- }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement