-
1.
// ==UserScript==
-
2.
// @name MLPFicReviews Extras
-
3.
// @namespace http://ponepaste.org/5555
-
4.
// @version 0.2
-
5.
// @description Adds a few handy functions to mlpficreviews.org.uk
-
6.
// @author Fillyanon
-
7.
// @match http://www.mlpficreviews.org.uk/*
-
8.
// @grant none
-
9.
// ==/UserScript==
-
10.
-
11.
-
12.
const style = `
-
13.
body {
-
14.
background-image: url("https://u.smutty.horse/mdqwfehyeeo.png");
-
15.
background-attachment: fixed;
-
16.
background-repeat: no-repeat;
-
17.
background-size: 100%;
-
18.
}
-
19.
-
20.
.page {
-
21.
width: 1100px;
-
22.
background: #eef2ff;
-
23.
padding: 0;
-
24.
}
-
25.
-
26.
h1 {
-
27.
text-align: center;
-
28.
font-size: 2.5rem;
-
29.
padding: 1rem;
-
30.
background: hsl(60, 60%, 90%);
-
31.
}
-
32.
-
33.
h1::before {
-
34.
content: url("https://static.fimfiction.net/images/logo-2x.png");
-
35.
display: block;
-
36.
}
-
37.
-
38.
a {
-
39.
text-decoration: none;
-
40.
}
-
41.
-
42.
h2 {
-
43.
margin-top: 1rem;
-
44.
text-align: center;
-
45.
}
-
46.
-
47.
.sorter {
-
48.
margin: 1rem auto;
-
49.
display: block;
-
50.
width: min-content;
-
51.
}
-
52.
-
53.
.sorter button {
-
54.
width: 100%;
-
55.
height: 1.5rem;
-
56.
}
-
57.
-
58.
ul {
-
59.
position:relative;
-
60.
display: grid;
-
61.
grid-template-columns: repeat(3, calc(33% - 0.5rem));
-
62.
/*grid-auto-rows: 1fr; ̇*/
-
63.
grid-gap: 1rem;
-
64.
margin-top: 1.5rem !important;
-
65.
margin-bottom: 1rem !important;
-
66.
padding-left: 1rem !important;
-
67.
padding-right: 0.8rem !important;
-
68.
}
-
69.
-
70.
.entries li {
-
71.
margin: 0;
-
72.
border: 1.5px solid hsl(60, 20%, 80%);
-
73.
}
-
74.
-
75.
.entries li > a {
-
76.
background: hsl(60, 20%, 90%);
-
77.
border-bottom: 1px solid hsl(60, 20%, 80%);
-
78.
display: block;
-
79.
overflow: hidden;
-
80.
text-overflow: ellipsis;
-
81.
white-space: nowrap;
-
82.
font-size: 18px;
-
83.
padding: 0.3rem 0.2rem;
-
84.
-
85.
text-decoration: none;
-
86.
}
-
87.
-
88.
.entries li sup a {
-
89.
display: inline;
-
90.
float: right;
-
91.
}
-
92.
-
93.
li small {
-
94.
display: block;
-
95.
padding: 0.3rem 0.2rem;
-
96.
background: #efefef;
-
97.
}
-
98.
-
99.
li {
-
100.
position: relative;
-
101.
}
-
102.
-
103.
li .intro {
-
104.
width: 100%;
-
105.
box-sizing: border-box;
-
106.
position: absolute;
-
107.
height: 0;
-
108.
text-overflow: hidden;
-
109.
overflow: hidden;
-
110.
z-index: 1;
-
111.
border: 1px solid hsl(60, 20%, 80%);
-
112.
-
113.
background: #efefef;
-
114.
-
115.
margin: 0;
-
116.
padding: 0;
-
117.
-
118.
transition: height 1s ease-in-out, padding 0.1s ease-out 1s, z-index 0s ease-out 1s;
-
119.
}
-
120.
-
121.
li:hover .intro
-
122.
{
-
123.
height: 15rem;
-
124.
z-index: 2;
-
125.
-
126.
padding: 0.4rem 0.2rem;
-
127.
-
128.
transition: height 1s ease-in-out, padding 0.1s ease-out 0s, z-index 0s ease-out 0s;
-
129.
}
-
130.
-
131.
/* Review */
-
132.
-
133.
h2 ~ p {
-
134.
text-align: center;
-
135.
}
-
136.
-
137.
dt {
-
138.
text-align: center;
-
139.
margin-bottom: 0.5rem;
-
140.
margin-top: 1.5rem;
-
141.
}
-
142.
-
143.
dd {
-
144.
margin: 0;
-
145.
}
-
146.
-
147.
blockquote {
-
148.
margin: 0;
-
149.
text-align: justify;
-
150.
padding-left: 1rem;
-
151.
padding-right: 1rem;
-
152.
}
-
153.
-
154.
blockquote br {
-
155.
margin-bottom: 0.5rem;
-
156.
}
-
157.
`
-
158.
-
159.
const getIntro = async (url) =>
-
160.
{
-
161.
const content = await fetch(url).then(res => res.text())
-
162.
const parser = new DOMParser()
-
163.
const page = parser.parseFromString(content, "text/html")
-
164.
-
165.
const quote = page.querySelector("blockquote").innerText.trim().split(" ").filter(e => e)
-
166.
const intro = quote.splice(0, Math.min(50, quote.length))
-
167.
-
168.
return intro.join(" ") + "..."
-
169.
}
-
170.
-
171.
const addIntroLoader = () =>
-
172.
{
-
173.
const entryUL = document.querySelector(".entries")
-
174.
const entries = [...entryUL.querySelectorAll("li")]
-
175.
-
176.
entries.forEach(entry =>
-
177.
{
-
178.
const p = document.createElement("p")
-
179.
p.innerText = "Loading...";
-
180.
p.className = "intro";
-
181.
entry.appendChild(p)
-
182.
-
183.
let timeout = null;
-
184.
-
185.
entry.addEventListener("mouseover", () =>
-
186.
{
-
187.
if (entry.className.indexOf("handled") == -1)
-
188.
{
-
189.
const url = entry.querySelector("a").getAttribute("href");
-
190.
-
191.
if (localStorage.getItem(url) !== null)
-
192.
{
-
193.
entry.className += " handled"
-
194.
p.innerText = localStorage.getItem(url);
-
195.
}
-
196.
else
-
197.
{
-
198.
timeout = setTimeout(() => {
-
199.
entry.className += " handled"
-
200.
getIntro(url).then(intro => {
-
201.
p.innerText = intro;
-
202.
localStorage.setItem(url, intro);
-
203.
})
-
204.
}, 500)
-
205.
}
-
206.
}
-
207.
})
-
208.
-
209.
entry.addEventListener("mouseout", () =>
-
210.
{
-
211.
if (timeout !== null) clearTimeout(timeout)
-
212.
})
-
213.
})
-
214.
}
-
215.
-
216.
const sortType = Object.freeze(
-
217.
{
-
218.
ALPHABETICAL: "Sort alphabetically",
-
219.
DATENEW: "Sort by Date (Newest first)",
-
220.
DATEOLD: "Sort by Date (Oldest first)",
-
221.
COMMENTSMANY: "Sort by Most Comments",
-
222.
COMMENTSFEW: "Sort by Least Comments",
-
223.
}
-
224.
)
-
225.
-
226.
const getSort = (elem, type) =>
-
227.
{
-
228.
switch (type)
-
229.
{
-
230.
case sortType.ALPHABETICAL:
-
231.
return elem.querySelector("a").innerText;
-
232.
-
233.
case sortType.DATENEW:
-
234.
return -Number(elem.querySelector("a").getAttribute("href").split("/")[2]);
-
235.
-
236.
case sortType.DATEOLD:
-
237.
return Number(elem.querySelector("a").getAttribute("href").split("/")[2]);
-
238.
-
239.
case sortType.COMMENTSMANY:
-
240.
return -Number(elem.querySelector("small").innerText.substring(1).split(" ")[0]);
-
241.
-
242.
case sortType.COMMENTSFEW:
-
243.
return Number(elem.querySelector("small").innerText.substring(1).split(" ")[0]);
-
244.
}
-
245.
-
246.
console.warn("Bad sortType!");
-
247.
}
-
248.
-
249.
const injectStyle = () =>
-
250.
{
-
251.
const styleElem = document.createElement("style");
-
252.
styleElem.innerText = style;
-
253.
document.head.appendChild(styleElem);
-
254.
}
-
255.
-
256.
const addSortBar = () =>
-
257.
{
-
258.
const page = document.querySelector(".page")
-
259.
const entryUL = document.querySelector(".entries")
-
260.
const entries = [...entryUL.querySelectorAll("li")]
-
261.
-
262.
let isSorted = false;
-
263.
let isReverse = true;
-
264.
-
265.
const select = document.createElement("select");
-
266.
-
267.
for (let value of Object.values(sortType))
-
268.
{
-
269.
const option = document.createElement("option");
-
270.
option.setAttribute("value", value);
-
271.
option.innerText = value;
-
272.
select.appendChild(option);
-
273.
}
-
274.
-
275.
select.value = window.localStorage.getItem("selected") ?? sortType.DATENEW
-
276.
-
277.
const button = document.createElement("button")
-
278.
button.textContent = "Sort"
-
279.
-
280.
button.addEventListener("click", () =>
-
281.
{
-
282.
entries.sort((first, second) => {
-
283.
const f = getSort(first, select.value)
-
284.
const s = getSort(second, select.value)
-
285.
-
286.
if (select.value != sortType.ALPHABETICAL)
-
287.
return Math.sign(f - s)
-
288.
-
289.
return f.localeCompare(s);
-
290.
})
-
291.
-
292.
while (entryUL.firstChild)
-
293.
{
-
294.
entryUL.firstChild.remove()
-
295.
}
-
296.
-
297.
entries.forEach(e => entryUL.appendChild(e))
-
298.
window.localStorage.setItem("selected", select.value)
-
299.
})
-
300.
-
301.
button.click()
-
302.
-
303.
const div = document.createElement("div");
-
304.
div.className = "sorter";
-
305.
-
306.
div.appendChild(select);
-
307.
div.appendChild(button);
-
308.
-
309.
page.insertBefore(div, entryUL)
-
310.
}
-
311.
-
312.
const fixLinks = () =>
-
313.
{
-
314.
const links = document.querySelectorAll("a");
-
315.
-
316.
links.forEach(link => {
-
317.
const url = link.getAttribute("href")?.split("/")
-
318.
if (url !== undefined && url[2] == "boards.4chan.org")
-
319.
{
-
320.
url[2] = "desuarchive.org";
-
321.
-
322.
// replace quote anchor's p with q for desuarchive.org
-
323.
let anchor = url[url.length-1].split("#");
-
324.
anchor[1] = "q" + anchor[1].substring(1)
-
325.
url[url.length-1] = anchor.join("#")
-
326.
-
327.
link.setAttribute("href", url.join("/"))
-
328.
}
-
329.
})
-
330.
}
-
331.
-
332.
const makeHeader = () =>
-
333.
{
-
334.
const lis = [...document.querySelectorAll(".entries li")];
-
335.
const length = lis.length
-
336.
const totalReviews = lis.map(e => Number(e.querySelector("small").innerText.split(" ")[0].split("(")[1])).reduce((prev, curr) => prev+curr)
-
337.
-
338.
document.querySelector("h2").innerHTML+= `<br><small>(${length} Fics / ${totalReviews} Reviews)</small>`
-
339.
}
-
340.
-
341.
(function() {
-
342.
'use strict';
-
343.
-
344.
injectStyle(style);
-
345.
-
346.
if (window.location.toString().split("/").length == 4)
-
347.
{
-
348.
addSortBar();
-
349.
addIntroLoader();
-
350.
makeHeader();
-
351.
}
-
352.
else
-
353.
{
-
354.
fixLinks()
-
355.
}
-
356.
})();
740 8.14 KB 356
by Fillyanon
by Fillyanon
by Fillyanon
by Fillyanon
by Fillyanon