Greasy Fork is available in English.

[AO3] Fanfic Intensity Rating

Colors the backgrounds of fanfics based on intensity of warnings, ratings and additional tags.

// ==UserScript==
// @name         [AO3] Fanfic Intensity Rating
// @namespace    https://greasyfork.org/en/users/1138163-dreambones
// @version      1.0
// @description  Colors the backgrounds of fanfics based on intensity of warnings, ratings and additional tags.
// @author       DREAMBONES
// @match        http*://archiveofourown.org/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      Can modify w/credit.
// ==/UserScript==

(function() {
    'use strict';

    // ONLY alter the numbers for this section.
    var mainTags = {
        "General Audiences": 0,
        "Teen And Up Audiences": 1,
        "Mature": 3,
        "Explicit": 6,
        "Not Rated": 0,

        "No Archive Warnings Apply": 0,
        "Choose Not To Use Archive Warnings": 0,
        "Graphic Depictions Of Violence": 2,
        "Major Character Death": 2,
        "Rape/Non-Con": 5,
        "Underage": 4
    }

    // Add or alter any keys in this section. Matches via regex, but all special characters are escaped before matched.
    var freeformTags = {
        "Transphob": 3, //-e, -ia, -ic
        "Homophob": 3, //-e, -ia, -ic
        "Racis": 3, // -ism, -ist
        "Bigot": 3,
        "Dead Dove: Do Not Eat": 4,
        "Suicide": 5,
        "Child Abuse": 3,
        "Self-Harm": 3,
        "Drug Use": 1,
        "Torture": 3,
        "Horror": 1,
        "Drinking": 1,
        "Blood": 2,
        "Death": 2,
        "PTSD": 2,
        "Angst": 1,
        "Slurs": 2,
        "Kidnapping": 1
    }

    var whitelist = []; // Tags will not count towards intensity rating if they're included here. NOT regex based match.

    // Tweak the threshold for fanfic rating highlights.
    var harmless = 2; // Anything lower than or equal to.
    var intense = 6; // Anything higher than or equal to.
    var extreme = 12; // Anything higher than or equal to.

    var domainRe = /https?:\/\/archiveofourown\.org\/(works|tags|users).*/i
    var pageType = document.URL.match(domainRe)[1];
    if (domainRe.test(document.URL)) {
        var worksQuery = "ol.work.index.group, ul.index.group, #user-series > ol.index.group"
        var worksList = document.querySelectorAll(worksQuery);
        for (let section of worksList) {
            for (let work of section.children) {
                var pts = 0;
                var ptList = [];

                var tempDict = Object.assign({}, freeformTags);

                var reqTags = work.querySelector(".required-tags").children;
                var rating = reqTags[0].firstElementChild.firstElementChild.firstElementChild.innerHTML;
                var warnings = (reqTags[1].firstElementChild.firstElementChild.firstElementChild.innerHTML).split(", ");
                var freeforms = work.querySelectorAll("li.freeforms > a");

                pts += mainTags[rating];
                if (mainTags[rating] != 0) {ptList.push(rating + ": " + mainTags[rating]);}
                for (let tag of warnings) {
                    pts += mainTags[tag];
                    if (mainTags[tag] != 0) {ptList.push(tag + ": " + mainTags[tag]);}
                }
                for (let tag of freeforms) {
                    var keys = Object.keys(tempDict);
                    for (var key of keys) {
                        var cleanKey = escapeRegExp(key);
                        var tagRe = new RegExp(cleanKey, "i");
                        if (tagRe.test(tag.innerHTML) && !(whitelist.includes(tag.innerHTML))) {
                            pts += tempDict[key];
                            if (tempDict[key] != 0) {ptList.push(tag.innerHTML + ": " + tempDict[key]);}
                            delete tempDict[key]; // Tags are only counted once to avoid racking up points for dupes.
                        }
                    }
                }


                var info = document.createElement("a");
                if (ptList.length) {info.title = ptList.join("\n");}
                else {info.title = "Nothing to report!";}
                info.innerHTML = "Intensity: " + pts;
                info.style.position = "absolute";
                var authorActions = work.querySelector("ul.actions, p.actions");
                if (!authorActions) {info.style.left = "10px";}
                else {info.style.left = `${authorActions.offsetWidth + 15}px`;}
                info.style.opacity = "0.5";
                info.style.padding = "0px"
                info.style.cursor = "help";
                var stats = work.querySelector("dl.stats");
                stats.insertBefore(info, stats.firstElementChild);
                // Change the values for background colors via hex color codes or color shorthands (like "red").
                if (pts <= harmless) {
                    work.style["background-color"] = "#1A5432"; // Low intensity (green).
                }
                else if (harmless < pts && pts < intense) {
                    work.style["background-color"] = "#626123"; // Medium intensity (yellow).
                }

                else if (intense <= pts && pts < extreme) {
                    work.style["background-color"] = "#581E1F"; // High intensity (red).
                }

                else if (pts >= extreme) {
                    work.style["background-color"] = "#160723"; // Extreme intensity (black).
                }
            }
        }
    }

    // https://stackoverflow.com/questions/3115150/how-to-escape-regular-expression-special-characters-using-javascript
    function escapeRegExp(text) {
        return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
    }
})();