// ==UserScript==
// @name Fanfiction.net - Beautify Status Scrolling For Dark Reader
// @namespace http://tampermonkey.net/
// @version 3.0
// @description Changes colors and formats stats to make it easier to read while scrolling, works on android. "Fiction Rating: All" as default, blacklist included.
// @author バカなやつ
// @license MIT
// @match https://www.fanfiction.net/*
// @match https://m.fanfiction.net/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=fanfiction.net
// @grant GM_addStyle
// @downloadURL none
// ==/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;
// PC
const isReadingPage = URL.includes("fanfiction.net/s/");
const isCommunityPage = URL.includes("fanfiction.net/community/");
const communities = ["anime", "book", "cartoon", "comic", "game", "misc", "play", "movie", "tv"];
const isStoriesPage = communities.some(v => URL.includes("fanfiction.net/" + v));
// Mobile
const isMobile = URL.startsWith("https://m.fanfiction.net/");
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, wrapping the text pattern
function repmat(pattern, text, replace){
let re = new RegExp(pattern);
let num = re.exec(text)[1];
if (replace.startsWith(" ");
}else{
return text.replace(re, replace + num + " ");
}
}
function m_repmat(pattern, text, replace){
let re = new RegExp(pattern);
let num = re.exec(text)[1];
if (replace.startsWith(", ");
}else{
return text.replace(re, replace + num + ", ");
}
}
// 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(//) != -1;
let isChapters = n_sub.includes("Chapters:");
let isFavs = n_sub.includes("Favs:");
let isFollows = n_sub.includes("Follows:");
let isUpdated = n_sub.includes("Updated:");
let ahref;
if (isReadingPage && isReviews) {
ahref = status.getElementsByTagName("a")[1].getAttribute("href");
} else if (isReviews) {
ahref = mat(//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 = "" + storyName + rep(/(.*?)\s-\sRated:/g, n_sub, " - Rated:");
}
if (isChapters) {
n_sub = repmat(/Chapters:\s(.*?)\s/g, n_sub, "
Ch: ");
}
n_sub = repmat(/Words:\s(.*?)\s/g, n_sub, "W: ");
if (isReadingPage && isReviews) {
n_sub = repmat(/Reviews:\s.*?>(.*?)<.*?>/g, n_sub, "Reviews: ");
} else if (isReviews) {
n_sub = repmat(/Reviews:\s(.*?)\s/g, n_sub, "Reviews: ");
}
if (isFavs) {
n_sub = repmat(/Favs:\s(.*?)\s/g, n_sub, "Favs: ");
}
if (isFollows) {
n_sub = repmat(/Follows:\s(.*?)\s/g, n_sub, "Follows: ");
}
if (isUpdated){
n_sub = repmat(/Updated:\s.*?>(.*?)<.*?\>/g, n_sub, "Updated: ");
}
// Moves the "Publish: ... Characters:" before "
Chapters:"
let t = n_sub.slice(n_sub.search(/\s\-\sPublished:/));
// Remove previous "Publish: ... Characters:"
n_sub = n_sub.slice(0, n_sub.search(/\s\-\sPublished:/));
// Moves it depending on whether Chapter exists
if (!isChapters) {
n_sub = rep(/\-\s\, n_sub, t + "
<");
} else {
n_sub = rep(/
/, n_sub, t + "
");
}
status.innerHTML = n_sub;
// Adds the link to reviews
if (isReviews) {
status.getElementsByClassName("reviews")[0].setAttribute("href", ahref);
}
}
function m_cList(story, stats){
let n_sub = story.innerHTML;
let isReviews = n_sub.search(/Reviews:/i) != -1;
let isChapters = n_sub.search(/Chapters:/i) != -1;
let isFavs = n_sub.search(/Favs:/i) != -1;
let isFollows = n_sub.search(/Follows:/i) != -1;
let isUpdated = n_sub.search(/Updated:/i) != -1;
if (isChapters) {
n_sub = m_repmat(/Chapters:\s(.*?),/gi, n_sub, "
Ch: ");
n_sub = m_repmat(/Words:\s(.*?),/gi, n_sub, "W: ");
} else {
n_sub = m_repmat(/Words:\s(.*?),/gi, n_sub, "
W: ");
}
if (isReadingPage && isReviews) {
n_sub = m_repmat(/Reviews:\s.*?>(.*?)<.*?>/gi, n_sub, "Reviews: ");
}
if (isFavs) {
n_sub = m_repmat(/\sFavs:\s(.*?),/gi, n_sub, "Favs: ");
}
if (isFollows) {
n_sub = m_repmat(/Follows:\s(.*?),/gi, n_sub, "Follows: ");
}
let n = n_sub.slice(n_sub.search(/Published:/i), n_sub.search(/Updated:/));
if (isUpdated){
n_sub = m_repmat(/Updated:\s.*?>(.*?)<.*?\>\s/gi, n_sub, "Updated: ");
}
n_sub = n_sub.replace(/Published:\s.*?>.*?<.*?\>/i, "");
if (isReadingPage) {
n_sub = n_sub.replace(/<\/span>,\s
, " + n + "
,\s,/i, ", " + n + ",");
n_sub = n_sub.replace(/pub/, "Pub");
}
story.innerHTML = n_sub;
}
window.addEventListener("load", function() {
if (isMobile) {
if (isReadingPage) {
let story = document.getElementById("content");
m_cList(story);
} else {
let story = document.getElementsByClassName("bs");
let stats = document.getElementsByClassName("gray");
for (let i = story.length - 1; i > -1; i--){
let status = story[i].getElementsByClassName("gray");
let text = status[0].innerHTML;
if (blacklist_fandoms.some(v => text.includes(v))) {
story[i].parentNode.removeChild(story[i]);
continue;
}
m_cList(story[i], stats[i]);
}
}
} else {
if (isReadingPage) {
let story = document.getElementById("profile_top");
let status = document.getElementsByClassName("xgray")[0];
cList(story, status);
}
else if (!isMobile) {
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]);
}
}
}
});
})();