Greasy Fork

Fanfiction.net - Beautify Status Scrolling For Dark Reader

Changes colors and formats stats to make it easier to read while scrolling, with Fandom blacklist included in the script.

目前为 2022-07-21 提交的版本。查看 最新版本

// ==UserScript==
// @name         Fanfiction.net - Beautify Status Scrolling For Dark Reader
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  Changes colors and formats stats to make it easier to read while scrolling, with Fandom blacklist included in the script.
// @author       バカなやつ
// @license      MIT
// @match        https://www.fanfiction.net/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=fanfiction.net
// @grant        GM_addStyle
// ==/UserScript==
GM_addStyle(".lightGreenHighlight {color:#C4FFCA;}");
GM_addStyle(".greenHighlight {color:#04AD5C;}");
GM_addStyle(".redHighlight {color:#CC5500;}");
GM_addStyle(".yellowHighlight {color:#FFC300;}");
GM_addStyle(".pinkHighlight {color:#FFC0CB;}");
GM_addStyle(".z-indent {padding-left: 60px;}");
GM_addStyle(".reviews {color: rgb(255, 102, 26); text-decoration-color: initial;}");
(function() {
    //https://www.fanfiction.net/community/The-Archive-for-Self-Inserts-and-Original-Characters/117072/
    'use strict';

    // const blacklist_fandoms = ["Harry", "Doctor Who", "Crossover - Star Wars", "Yu-Gi-Oh"]
    const blacklist_fandoms = [];
    // Makes "Fiction Rating: All" default the first time you enter a story listing page!
    const allRatings = true;

    const URL = document.URL;
    const isReadingPage = URL.startsWith("https://www.fanfiction.net/s/");
    const isCommunityPage = URL.startsWith("https://www.fanfiction.net/community/");
    const communities = ["anime", "book", "cartoon", "comic", "game", "misc", "play", "movie", "tv"];
    const isStoriesPage = communities.some(v => URL.startsWith("https://www.fanfiction.net/" + v));

    if (isCommunityPage && allRatings &&
        // If URL does not contain any filter id's
        URL.search(/\/\d\d\d\d*\/$/) != -1) {
        let l = URL.split("/");
        let endURL = 99 + "/";
        location.href = URL + endURL;
    }

    if (isStoriesPage && allRatings &&
        // If URL does not contains any filter id's
        URL.search(/\/\?/) == -1) {
        location.href = URL + "?&srt=1&r=10";
    }

    // Replaces pattern on text with string
    function rep(pattern, text, replace){
        let re = new RegExp(pattern);
        return text.replace(re, replace);
    }
    // Matches the pattern and returns the second group
    function mat(pattern, text){
        let re = new RegExp(pattern)
        return re.exec(text)[1];
    }
    // Short-form for replacematch
    function repmat(pattern, text, replace){
        let re = new RegExp(pattern);
        let num = re.exec(text)[1];
        if (replace.startsWith("<a")){
            return text.replace(re, replace + num + "</a> ");
        }else{
            return text.replace(re, replace + num + "</span> ");
        }
    }
    // Content List
    function cList(story, status){
        let n_sub = status.innerHTML;
        // Checks if these exists, returns true or false
        let isReviews = (isReadingPage) ? story.innerHTML.search(/Reviews:\s.*?>(.*?)<.*?>/) != -1 : story.innerHTML.search(/<a class="reviews".*?f="(.*?)">/) != -1;
        let isChapters = n_sub.search(/Chapters:\s(.*?)\s/) != -1;
        let isFavs = n_sub.search(/Favs:\s(.*?)\s/) != -1;
        let isFollows = n_sub.search(/Follows:\s(.*?)\s/) != -1;
        let isUpdated = n_sub.search(/Updated:\s.*?>(.*?)<.*?\>/) != -1;

        let ahref;
        if (isReadingPage && isReviews) {
            ahref = mat(/Reviews:\s.*?>(.*?)<.*?>/g, n_sub);
        } else if (isReviews) {
            ahref = mat(/<a class="reviews".*?f="(.*?)">/g, story.innerHTML);
            // Removes the original Review Link
            let review = story.getElementsByClassName("reviews")[0];
            review.parentNode.removeChild(review);
        }

        if (isCommunityPage) {
            let storyName = mat(/(.*?)\s-\sRated:/g, n_sub);
            n_sub = "<span class='greenHighlight'>" + storyName + rep(/(.*?)\s-\sRated:/g, n_sub, "</span> - Rated:");
        }

        if (isChapters) {
            n_sub = repmat(/Chapters:\s(.*?)\s/g, n_sub, "<br><span class='yellowHighlight'>Ch: ");
        }

        n_sub = repmat(/Words:\s(.*?)\s/g, n_sub, "<span class='pinkHighlight'>W: ");

        // Err
        if (isReadingPage && isReviews) {
            n_sub = repmat(/Reviews:\s.*?>(.*?)<.*?>/g, n_sub, "<a class='reviews'>Reviews: ");
        } else if (isReviews) {
            n_sub = repmat(/Reviews:\s(.*?)\s/g, n_sub, "<a class='reviews'>Reviews: ");
        }

        if (isFavs) {
            n_sub = repmat(/Favs:\s(.*?)\s/g, n_sub, "<span class='lightGreenHighlight'>Favs: ");
        }

        if (isFollows) {
            n_sub = repmat(/Follows:\s(.*?)\s/g, n_sub, "<span class='lightGreenHighlight'>Follows: ");
        }

        if (isUpdated){
            n_sub = repmat(/Updated:\s.*?>(.*?)<.*?\>/g, n_sub, "<span class='lightGreenHighlight'>Updated: ");
        }
        // Moves the "Publish: ... Characters:" before "<br>Chapters:"
        let t = n_sub.slice(n_sub.search(/Published:/));
        console.log(n_sub);
        // Remove previous "Publish: ... Characters:"
        n_sub = n_sub.slice(0, n_sub.search(/\s\-\sPublished:/));
        // puts <br> before <span class='yellowHighlight'>W:
        if (!isChapters) {
            n_sub = rep(/\-\s\</, n_sub, t + "<br> <");
        } else {
            n_sub = rep(/<br>/, n_sub, t + "<br>");
        }

        status.innerHTML = n_sub;
        // Adds the review link to status
        if (isReviews) {
            status.getElementsByClassName("reviews")[0].setAttribute("href", ahref);
        }
    }

    window.addEventListener("load", function() {
        if (isReadingPage) {
            let story = document.getElementById("profile_top");
            let status = document.getElementsByClassName("xgray")[0];
            cList(story, status);
        } else {
            let stories = document.getElementsByClassName("z-list");
            let statuses = document.getElementsByClassName("z-padtop2");
            for (let i = stories.length - 1; i > -1; i--){
                let status = stories[i].getElementsByClassName("z-padtop2");
                let text = status[0].innerHTML;
                if (blacklist_fandoms.some(v => text.includes(v))) {
                    stories[i].parentNode.removeChild(stories[i]);
                    continue;
                }
                cList(stories[i], statuses[i]);
            }
        }
    });
})();