|
/* SAVE CONVERT */ |
|
for (let key of Object.keys(localStorage)) { |
|
const scribbleUrlRegex = /https:\/\/www\.scribblehub\.com\/(series|profile)\/\d+\/,/; |
|
if (scribbleUrlRegex.test(key)) { |
|
localStorage.setItem(key.replace(scribbleUrlRegex, ""), localStorage.getItem(key)) |
|
localStorage.removeItem(key); |
|
} |
|
} |
|
|
|
|
|
////////////////////////////////// |
|
// Produce hideable IDs from URLs |
|
|
|
let seriesIdRegex = /https:\/\/www.scribblehub.com\/series\/(\d+)\//; |
|
function getSeriesId (seriesLink) { |
|
const seriesId = seriesLink.match(seriesIdRegex) || []; |
|
if (!seriesId) |
|
throw new Error(`Failed to get series ID, href: ${seriesLink}`); |
|
|
|
return seriesId[1]; |
|
} |
|
|
|
let authorIdRegex = /https:\/\/www.scribblehub.com\/profile\/(\d+)\//; |
|
function getAuthorId (authorLink) { |
|
const authorId = authorLink.match(authorIdRegex) || []; |
|
if (!authorId) |
|
throw new Error(`Failed to get author ID, href: ${authorLink}`); |
|
|
|
return authorId[1]; |
|
} |
|
|
|
let tagIdRegex = /https:\/\/www.scribblehub.com\/tag\/([^\/]+)\//; |
|
function getTagId (tagLink) { |
|
const tagId = tagLink.match(tagIdRegex) || []; |
|
if (!tagId) |
|
throw new Error(`Failed to get tag ID, href: ${tagLink}`); |
|
|
|
return tagId[1]; |
|
} |
|
|
|
|
|
|
|
// replace series titles with clickable links |
|
var seriesSeriesName = document.querySelector(".novel-container .fic_title"); |
|
if (seriesSeriesName) { |
|
const link = document.createElement("a"); |
|
link.classList.add("fic_title"); |
|
link.href = location.pathname; |
|
link.textContent = seriesSeriesName.textContent; |
|
seriesSeriesName.replaceWith(link); |
|
} |
|
|
|
// replace author names with clickable links |
|
var profileAuthorName = document.querySelector(".profile .auth_name_fic"); |
|
if (profileAuthorName) { |
|
const link = document.createElement("a"); |
|
link.classList.add("auth_name_fic"); |
|
link.href = location.pathname; |
|
link.textContent = profileAuthorName.textContent; |
|
profileAuthorName.replaceWith(link); |
|
} |
|
|
|
|
|
|
|
////////////////////////////////////// |
|
// Toggling whether things are hidden |
|
|
|
let lastPrompt; |
|
async function hidePrompt(hidingPrompt = lastPrompt) { |
|
if (!hidingPrompt || hidingPrompt.classList.contains("scribble-garbage-vaporiser-undo-hidden")) |
|
return; // already hiding |
|
|
|
hidingPrompt.classList.add("scribble-garbage-vaporiser-undo-hidden"); |
|
await sleep(1000); |
|
hidingPrompt.remove(); |
|
|
|
if (lastPrompt === hidingPrompt) |
|
lastPrompt = undefined; |
|
} |
|
|
|
async function toggleHidden(id, name, displayUndo = true) { |
|
const isHidden = !localStorage.getItem(id); |
|
if (isHidden) |
|
localStorage.setItem(id, true); |
|
else |
|
localStorage.removeItem(id); |
|
|
|
updateHidden(); |
|
|
|
// early exit if no display undo |
|
if (!displayUndo) |
|
return; |
|
|
|
hidePrompt(); |
|
|
|
const undoPrompt = document.createElement("button"); |
|
undoPrompt.classList.add("scribble-garbage-vaporiser-undo"); |
|
undoPrompt.innerHTML = `Undo ${isHidden ? "hiding" : "showing"} <i>${name}</i>`; |
|
|
|
undoPrompt.addEventListener("click", () => { |
|
if (undoPrompt.classList.contains("scribble-garbage-vaporiser-undo-hidden")) |
|
return; // can't click, hidden |
|
|
|
toggleHidden(id, name, false); |
|
hidePrompt(undoPrompt); |
|
}); |
|
|
|
document.body.appendChild(undoPrompt); |
|
lastPrompt = undoPrompt; |
|
|
|
await sleep(5000); |
|
hidePrompt(undoPrompt); |
|
} |
|
|
|
|
|
|
|
const seriesSelectors = [ |
|
".slick-slide", // carousel stories |
|
"#main_releases > tbody > tr", // latest updates |
|
".search_main_box", // tag page and probably searches page too? |
|
".ficsimilar_body/parent", // similar series |
|
".fic_title/body", // series page |
|
]; |
|
|
|
/** |
|
* Get the wrapper element for a series by its link, usually to hide it |
|
*/ |
|
function getSeriesDisplay (seriesLink) { |
|
for (let seriesSelector of seriesSelectors) { |
|
const instructions = []; |
|
let index; |
|
while ((index = seriesSelector.lastIndexOf("/")) !== -1) { |
|
instructions.unshift(seriesSelector.slice(index + 1)); |
|
seriesSelector = seriesSelector.slice(0, index); |
|
} |
|
|
|
let element = seriesLink.closest(seriesSelector); |
|
if (element) { |
|
for (const instruction of instructions) { |
|
if (instruction === "parent") |
|
element = element?.parentElement; |
|
if (instruction === "body") |
|
element = document.body; |
|
} |
|
} |
|
|
|
if (element) |
|
return element; |
|
} |
|
} |
|
|
|
|
|
|
|
//////////////////////////////////////////////////// |
|
// Functions for updating whether things are hidden |
|
|
|
function isHiddenSeries(seriesId) { |
|
return localStorage.getItem(`hiddenSeries.${seriesId}`); |
|
} |
|
|
|
function isHiddenAuthor(authorId) { |
|
return localStorage.getItem(`hiddenAuthor.${authorId}`); |
|
} |
|
|
|
function isHiddenTag(tagId) { |
|
return localStorage.getItem(`hiddenTag.${tagId}`); |
|
} |
|
|
|
function queryAllSeriesLinks (container = document) { |
|
return container.querySelectorAll("a[href^='https://www.scribblehub.com/series/'], a[href^='/series/']"); |
|
} |
|
|
|
function queryAllAuthorLinks (container = document) { |
|
return container.querySelectorAll("a[href^='https://www.scribblehub.com/profile/'], a[href^='/profile/']"); |
|
} |
|
|
|
function queryAllTagLinks (container = document) { |
|
return container.querySelectorAll("a[href^='https://www.scribblehub.com/tag/'], a[href^='/tag/']"); |
|
} |
|
|
|
function updateHidden () { |
|
updateHiddenSeries(); |
|
|
|
|
|
updateHiddenAuthors(); |
|
updateHiddenTags(); |
|
|
|
// hidden authors/tags can hide series, so we check series again |
|
updateHiddenSeries(); |
|
} |
|
|
|
function updateHiddenSeries() { |
|
const seriesWrapperElements = Array.from(queryAllSeriesLinks()) |
|
.map(seriesLink => [seriesLink, getSeriesDisplay(seriesLink)]); |
|
|
|
for (const [seriesLink, seriesWrapper] of seriesWrapperElements) { |
|
const hiddenSeries = isHiddenSeries(getSeriesId(seriesLink.href)); |
|
seriesWrapper?.classList.toggle("hidden-series", hiddenSeries); |
|
seriesLink?.classList.toggle("hidden-textlink", hiddenSeries); |
|
} |
|
} |
|
|
|
function updateHiddenAuthors() { |
|
const authorElements = Array.from(queryAllAuthorLinks()) |
|
.map(authorLink => [authorLink, getSeriesDisplay(authorLink)]); |
|
|
|
for (const [authorLink, seriesWrapper] of authorElements) { |
|
const hiddenAuthor = isHiddenAuthor(getAuthorId(authorLink.href)); |
|
authorLink?.classList.toggle("hidden-textlink", hiddenAuthor); |
|
if (hiddenAuthor && seriesWrapper && !seriesWrapper.classList.contains("hidden-series")) { |
|
// hidden author with a series that hasn't been hidden yet |
|
for (const seriesLink of queryAllSeriesLinks(seriesWrapper)) { |
|
const seriesId = `hiddenSeries.${getSeriesId(seriesLink.href)}`; |
|
localStorage.setItem(seriesId, true); |
|
seriesWrapper.classList.toggle("hidden-series", true); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
function updateHiddenTags() { |
|
for (const tagLink of queryAllTagLinks()) { |
|
const hiddenTag = isHiddenTag(getTagId(tagLink.href)); |
|
tagLink?.classList.toggle("hidden-textlink", hiddenTag); |
|
if (hiddenTag) { |
|
// hidden tag on a series that hasn't been hidden yet |
|
const seriesId = `hiddenSeries.${getSeriesId(location.href)}`; |
|
localStorage.setItem(seriesId, true); |
|
document.body.classList.toggle("hidden-series", true); |
|
} |
|
} |
|
} |
|
|
|
const STORAGE_KEY_SHOWING_HIDDEN = "showing-hidden"; |
|
function updateShowingHidden(shown = localStorage.getItem(STORAGE_KEY_SHOWING_HIDDEN)) { |
|
document.documentElement.classList.toggle("showing-hidden", shown); |
|
} |
|
|
|
|
|
// support for hidden series on profile (which can be toggled hidden/not) |
|
const profileSeriesWrapper = document.querySelector(".p_load_series"); |
|
if (profileSeriesWrapper) |
|
new MutationObserver(updateHidden) |
|
.observe(profileSeriesWrapper, { childList: true }); |
|
|
|
/** |
|
* Main event handler — everything is by contextmenu interactions |
|
*/ |
|
window.addEventListener("contextmenu", event => { |
|
if (location.pathname === "/reading-list/") |
|
return; |
|
|
|
if (event.target.closest(".header-title-main")) { |
|
// toggle whether hidden stuff is displayed (with special css of course) |
|
|
|
const isShown = !localStorage.getItem(STORAGE_KEY_SHOWING_HIDDEN); |
|
if (isShown) |
|
localStorage.setItem(STORAGE_KEY_SHOWING_HIDDEN, true); |
|
else |
|
localStorage.removeItem(STORAGE_KEY_SHOWING_HIDDEN); |
|
|
|
updateShowingHidden(isShown); |
|
event.preventDefault(); |
|
return; |
|
} |
|
|
|
// toggle things marked as garbage |
|
const seriesLink = event.target.closest("a[href^='https://www.scribblehub.com/series/'], a[href^='/series/']"); |
|
if (seriesLink) { |
|
toggleHidden(`hiddenSeries.${getSeriesId(seriesLink.href)}`, seriesLink?.closest(".novel_carousel_img")?.textContent ?? seriesLink.textContent); |
|
event.preventDefault(); |
|
return; |
|
} |
|
|
|
const authorLink = event.target.closest("a[href^='https://www.scribblehub.com/profile/'], a[href^='/profile/']"); |
|
if (authorLink) { |
|
toggleHidden(`hiddenAuthor.${getAuthorId(authorLink.href)}`, authorLink.textContent); |
|
event.preventDefault(); |
|
return; |
|
} |
|
|
|
const tagLink = event.target.closest("a[href^='https://www.scribblehub.com/tag/'], a[href^='/tag/']"); |
|
if (tagLink) { |
|
toggleHidden(`hiddenTag.${getTagId(tagLink.href)}`, tagLink.textContent); |
|
event.preventDefault(); |
|
return; |
|
} |
|
}); |
|
|
|
|
|
|
|
|
|
updateHidden(); |
|
updateShowingHidden(); |
|
|
|
|
|
|
|
|
|
|
|
///////////// |
|
// Utilities |
|
|
|
function sleep(ms) { |
|
return new Promise(resolve => setTimeout(resolve, ms)); |
|
} |