// ==UserScript==
// @name MLPFicReviews Extras
// @namespace http://ponepaste.org/5555
// @version 0.2
// @description Adds a few handy functions to mlpficreviews.org.uk
// @author Fillyanon
// @match http://www.mlpficreviews.org.uk/*
// @grant none
// ==/UserScript==
const style = `
body {
background-image: url("https://u.smutty.horse/mdqwfehyeeo.png");
background-attachment: fixed;
background-repeat: no-repeat;
background-size: 100%;
}
.page {
width: 1100px;
background: #eef2ff;
padding: 0;
}
h1 {
text-align: center;
font-size: 2.5rem;
padding: 1rem;
background: hsl(60, 60%, 90%);
}
h1::before {
content: url("https://static.fimfiction.net/images/logo-2x.png");
display: block;
}
a {
text-decoration: none;
}
h2 {
margin-top: 1rem;
text-align: center;
}
.sorter {
margin: 1rem auto;
display: block;
width: min-content;
}
.sorter button {
width: 100%;
height: 1.5rem;
}
ul {
position:relative;
display: grid;
grid-template-columns: repeat(3, calc(33% - 0.5rem));
/*grid-auto-rows: 1fr; ̇*/
grid-gap: 1rem;
margin-top: 1.5rem !important;
margin-bottom: 1rem !important;
padding-left: 1rem !important;
padding-right: 0.8rem !important;
}
.entries li {
margin: 0;
border: 1.5px solid hsl(60, 20%, 80%);
}
.entries li > a {
background: hsl(60, 20%, 90%);
border-bottom: 1px solid hsl(60, 20%, 80%);
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 18px;
padding: 0.3rem 0.2rem;
text-decoration: none;
}
.entries li sup a {
display: inline;
float: right;
}
li small {
display: block;
padding: 0.3rem 0.2rem;
background: #efefef;
}
li {
position: relative;
}
li .intro {
width: 100%;
box-sizing: border-box;
position: absolute;
height: 0;
text-overflow: hidden;
overflow: hidden;
z-index: 1;
border: 1px solid hsl(60, 20%, 80%);
background: #efefef;
margin: 0;
padding: 0;
transition: height 1s ease-in-out, padding 0.1s ease-out 1s, z-index 0s ease-out 1s;
}
li:hover .intro
{
height: 15rem;
z-index: 2;
padding: 0.4rem 0.2rem;
transition: height 1s ease-in-out, padding 0.1s ease-out 0s, z-index 0s ease-out 0s;
}
/* Review */
h2 ~ p {
text-align: center;
}
dt {
text-align: center;
margin-bottom: 0.5rem;
margin-top: 1.5rem;
}
dd {
margin: 0;
}
blockquote {
margin: 0;
text-align: justify;
padding-left: 1rem;
padding-right: 1rem;
}
blockquote br {
margin-bottom: 0.5rem;
}
`
const getIntro = async (url) =>
{
const content = await fetch(url).then(res => res.text())
const parser = new DOMParser()
const page = parser.parseFromString(content, "text/html")
const quote = page.querySelector("blockquote").innerText.trim().split(" ").filter(e => e)
const intro = quote.splice(0, Math.min(50, quote.length))
return intro.join(" ") + "..."
}
const addIntroLoader = () =>
{
const entryUL = document.querySelector(".entries")
const entries = [...entryUL.querySelectorAll("li")]
entries.forEach(entry =>
{
const p = document.createElement("p")
p.innerText = "Loading...";
p.className = "intro";
entry.appendChild(p)
let timeout = null;
entry.addEventListener("mouseover", () =>
{
if (entry.className.indexOf("handled") == -1)
{
const url = entry.querySelector("a").getAttribute("href");
if (localStorage.getItem(url) !== null)
{
entry.className += " handled"
p.innerText = localStorage.getItem(url);
}
else
{
timeout = setTimeout(() => {
entry.className += " handled"
getIntro(url).then(intro => {
p.innerText = intro;
localStorage.setItem(url, intro);
})
}, 500)
}
}
})
entry.addEventListener("mouseout", () =>
{
if (timeout !== null) clearTimeout(timeout)
})
})
}
const sortType = Object.freeze(
{
ALPHABETICAL: "Sort alphabetically",
DATENEW: "Sort by Date (Newest first)",
DATEOLD: "Sort by Date (Oldest first)",
COMMENTSMANY: "Sort by Most Comments",
COMMENTSFEW: "Sort by Least Comments",
}
)
const getSort = (elem, type) =>
{
switch (type)
{
case sortType.ALPHABETICAL:
return elem.querySelector("a").innerText;
case sortType.DATENEW:
return -Number(elem.querySelector("a").getAttribute("href").split("/")[2]);
case sortType.DATEOLD:
return Number(elem.querySelector("a").getAttribute("href").split("/")[2]);
case sortType.COMMENTSMANY:
return -Number(elem.querySelector("small").innerText.substring(1).split(" ")[0]);
case sortType.COMMENTSFEW:
return Number(elem.querySelector("small").innerText.substring(1).split(" ")[0]);
}
console.warn("Bad sortType!");
}
const injectStyle = () =>
{
const styleElem = document.createElement("style");
styleElem.innerText = style;
document.head.appendChild(styleElem);
}
const addSortBar = () =>
{
const page = document.querySelector(".page")
const entryUL = document.querySelector(".entries")
const entries = [...entryUL.querySelectorAll("li")]
let isSorted = false;
let isReverse = true;
const select = document.createElement("select");
for (let value of Object.values(sortType))
{
const option = document.createElement("option");
option.setAttribute("value", value);
option.innerText = value;
select.appendChild(option);
}
select.value = window.localStorage.getItem("selected") ?? sortType.DATENEW
const button = document.createElement("button")
button.textContent = "Sort"
button.addEventListener("click", () =>
{
entries.sort((first, second) => {
const f = getSort(first, select.value)
const s = getSort(second, select.value)
if (select.value != sortType.ALPHABETICAL)
return Math.sign(f - s)
return f.localeCompare(s);
})
while (entryUL.firstChild)
{
entryUL.firstChild.remove()
}
entries.forEach(e => entryUL.appendChild(e))
window.localStorage.setItem("selected", select.value)
})
button.click()
const div = document.createElement("div");
div.className = "sorter";
div.appendChild(select);
div.appendChild(button);
page.insertBefore(div, entryUL)
}
const fixLinks = () =>
{
const links = document.querySelectorAll("a");
links.forEach(link => {
const url = link.getAttribute("href")?.split("/")
if (url !== undefined && url[2] == "boards.4chan.org")
{
url[2] = "desuarchive.org";
// replace quote anchor's p with q for desuarchive.org
let anchor = url[url.length-1].split("#");
anchor[1] = "q" + anchor[1].substring(1)
url[url.length-1] = anchor.join("#")
link.setAttribute("href", url.join("/"))
}
})
}
const makeHeader = () =>
{
const lis = [...document.querySelectorAll(".entries li")];
const length = lis.length
const totalReviews = lis.map(e => Number(e.querySelector("small").innerText.split(" ")[0].split("(")[1])).reduce((prev, curr) => prev+curr)
document.querySelector("h2").innerHTML+= `
(${length} Fics / ${totalReviews} Reviews)`
}
(function() {
'use strict';
injectStyle(style);
if (window.location.toString().split("/").length == 4)
{
addSortBar();
addIntroLoader();
makeHeader();
}
else
{
fixLinks()
}
})();