Minimizes unwanted subreddit posts and attempts to remove all ads on the Reddit feed.
当前为
// ==UserScript==
// @name Reddit Chemo
// @namespace https://lawrenzo.com/p/reddit-chemo
// @version 0.6.0
// @description Minimizes unwanted subreddit posts and attempts to remove all ads on the Reddit feed.
// @author Lawrence Sim
// @license WTFPL (http://www.wtfpl.net)
// @grant unsafeWindow
// @match *://*.reddit.com/*
// ==/UserScript==
(function() {
var showRemovedMessage = true,
darkMode = true,
banSubreddits = (window.bannedSubreddits || (unsafeWindow && unsafeWindow.bannedSubreddits) || [])
.map(n => n.trim().toLowerCase())
.map(n => n.startsWith("r/") ? n : `r/${n}`);
//-------------------------------------------------------------------------------------
function blockPost(post, sub) {
if(!showRemovedMessage) return post.remove();
post.style.background = darkMode ? "rgb(80 70 70)" : "rgb(255 240 240)";
let child, voteElem, subelm, icon;
for(let i = 0; i < post.children.length; ++i) {
child = post.children[i];
if(child.children[0] && child.children[0].id.startsWith("vote-arrows")) {
child.style.top = "-0.7em";
voteElem = child.children[0];
for(let j = voteElem.children.length-1; j >= 0; --j) {
subelm = voteElem.children[j];
if(subelm.nodeName.toLowerCase() === "button" && subelm.getAttribute("data-click-id") === "downvote") {
icon = subelm.querySelector(".icon");
icon.style["line-height"] = "15px";
icon.style["font-size"] = "14px";
} else {
subelm.remove();
}
}
} else if(child.getAttribute("data-click-id") === "background") {
child.style.background = "none";
child.style.color = "rgb(163 149 149)";
child.style.padding = "0.3em";
child.style["font-size"] = "0.7em";
child.innerHTML = `Post from ${sub} removed`;
} else {
child.remove()
}
}
}
//-------------------------------------------------------------------------------------
// watchers as React will sometimes restore/readd posts
//-------------------------------------------------------------------------------------
var emptyObserver = new MutationObserver(mutated => {
mutated.forEach(mutant => {
if(mutant.target.children.length) {
mutant.target.innerHTML = "";
mutant.target.style.border = "none";
mutant.target.style.fill = "none";
}
});
});
var blockObserver = new MutationObserver(mutated => {
mutated.forEach(mutant => {
if(mutant.target.children.length) {
let post = mutant.target.closest("div[data-testid='post-container']");
blockPost(post, post.getAttribute("chemo"));
}
});
});
function refreshObservers() {
emptyObserver.disconnect();
blockObserver.disconnect();
};
//-------------------------------------------------------------------------------------
// resolvers for when post data is not yet loaded
//-------------------------------------------------------------------------------------
function watchPost(post) {
(new MutationObserver((mutated, observer) => {
if(checkAd(post)) return observer.disconnect();
let subreddit = post.querySelector("a[data-click-id='subreddit']")
if(checkBanned(post, subreddit)) return observer.disconnect();
})).observe(post, {childList:true, subtree:true});
}
function watchSubreddit(post, subreddit) {
(new MutationObserver((mutated, observer) => {
if(checkBanned(post, subreddit)) observer.disconnect();
})).observe(subreddit, {childList:true, attributes:true});
}
//-------------------------------------------------------------------------------------
// process/check post functions
//-------------------------------------------------------------------------------------
function checkAd(post) {
if(
Array.from(post.querySelectorAll("span"))
.find(span => span.innerText && span.innerText.toLowerCase() === "promoted")
) {
post.innerHTML = "";
post.style.border = "none";
post.style.fill = "none";
console.log("Ad removed.");
emptyObserver.observe(post, {childList:true});
return 1;
}
return 0;
}
function checkBanned(post, subreddit) {
if(subreddit && subreddit.innerText) {
let subname = subreddit.innerText.toLowerCase();
if(~banSubreddits.indexOf(subname)) {
post.setAttribute("chemo", subreddit.innerText);
blockPost(post, subname);
console.log(`Banned subreddit (${subname}) post removed.`);
blockObserver.observe(
post.querySelector("div[data-click-id='background']"),
{childList:true}
);
return 1;
}
return -1;
}
return 0;
}
function processNodes(nodes) {
if(!nodes || !nodes.length) return;
nodes.forEach(node => {
if(!node || !node.querySelectorAll) return;
node.querySelectorAll("div[data-testid='post-container']").forEach(post => {
if(post.getAttribute("chemo")) return;
post.setAttribute("chemo", 1);
if(checkAd(post)) return;
let subreddit = post.querySelector("a[data-click-id='subreddit']");
if(!subreddit) return watchPost(post);
if(!checkBanned(post, subreddit)) watchSubreddit(post, subreddit);
});
});
}
//-------------------------------------------------------------------------------------
// init
//-------------------------------------------------------------------------------------
processNodes([document.querySelector(".ListingLayout-outerContainer")]);
//-------------------------------------------------------------------------------------
// if Reddit Watcher available, use it for update/change hooks
//-------------------------------------------------------------------------------------
let redditWatcher = window.redditWatcher || (unsafeWindow && unsafeWindow.redditWatcher);
if(redditWatcher) {
redditWatcher.feed.onChange(feed => {
refreshObservers();
processNodes([feed]);
});
redditWatcher.feed.onUpdate((feed, mutated) => {
feed && mutated.forEach(mutant => processNodes(mutant.addedNodes));
});
return;
}
//-------------------------------------------------------------------------------------
// otherwise manually create watcher
//-------------------------------------------------------------------------------------
function getFeedWrapper() {
let listingLayout = document.querySelector(".ListingLayout-outerContainer"),
firstPost = listingLayout && listingLayout.querySelector("div[data-testid='post-container']"),
feedWrapper = firstPost && firstPost.parentNode;
while(feedWrapper && !feedWrapper.nextSibling) {
if(feedWrapper == listingLayout) return null;
feedWrapper = feedWrapper.parentNode || null;
}
return feedWrapper && feedWrapper.parentNode;
}
var feedWatcher = new MutationObserver(mutated => mutated.forEach(mutant => processNodes(mutant.addedNodes))),
lastFeedWrapper = null;
(new MutationObserver(() => {
let feedWrapper = getFeedWrapper();
if(feedWrapper !== lastFeedWrapper) {
refreshObservers();
feedWatcher.disconnect();
if(feedWrapper) {
processNodes([feedWrapper]);
feedWatcher.observe(feedWrapper, {childList:true});
lastFeedWrapper = feedWrapper;
}
}
})).observe(document.body, {childList:true, subtree:true});
})();