Content Filter

Hide not interested content

当前为 2024-04-21 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name           Content Filter
// @name:ja        コンテンツフィルター
// @namespace      https://greasyfork.org/en/users/1264733
// @version        2024-04-21
// @description    Hide not interested content
// @description:ja 興味のない内容を隠す
// @author         LE37
// @license        MIT
// @include        https://forum.palemoon.org/viewforum*
// @include        https://forum.palemoon.org/viewtopic*
// @include        https://forum.vivaldi.net/category/*
// @include        https://forum.vivaldi.net/topic/*
// @include        https://kakuyomu.jp/pickup_works*
// @include        https://kakuyomu.jp/rankings/*
// @include        https://kakuyomu.jp/recent_works*
// @include        https://kakuyomu.jp/users/*
// @include        https://kakuyomu.jp/works/*
// @include        https://mypage.syosetu.com/*/
// @include        https://ncode.syosetu.com/*/
// @include        https://novelcom.syosetu.com/impression/*
// @include        https://yomou.syosetu.com/rank/*
// @include        https://social.vivaldi.net/
// @include        https://social.vivaldi.net/explore*
// @include        https://social.vivaldi.net/public*
// @include        https://syosetu.org/novel/*/
// @include        https://syosetu.org/user/*/
// @include        https://syosetu.org/?mode=rank*
// @include        https://syosetu.org/?mode=review*
// @include        https://www.alphapolis.co.jp/author/*
// @include        https://www.alphapolis.co.jp/novel/*
// @include        https://www.ign.com/articles/*
// @include        https://www.yandex.com/search/*
// @include        https://yandex.com/search/*
// @exclude        https://www.alphapolis.co.jp/novel/ranking/annual
// @exclude        https://yomou.syosetu.com/rank/top/
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_registerMenuCommand
// ==/UserScript==

(()=>{
	'use strict';
	// GM Key
	let gMk;
	// Needed if remove include and exclude in future
	switch (location.host) {
		case "www.alphapolis.co.jp":
			gMk = "APS";
			break;
		case "syosetu.org":
			gMk = "HML";
			break;
		case "www.ign.com":
			gMk = "IGN";
			break;
		case "kakuyomu.jp":
			gMk = "KYU";
			break;
		case "novelcom.syosetu.com":
			gMk = "NUC";
			break;
		case "mypage.syosetu.com":
		case "ncode.syosetu.com":
		case "yomou.syosetu.com":
			gMk = "NRK";
			break;
		case "forum.palemoon.org":
			gMk = "PMF";
			break;
		case "forum.vivaldi.net":
			gMk = "VVF";
			break;
		case "social.vivaldi.net":
			gMk = "VVS";
			break;
		case "yandex.com":
		case "www.yandex.com":
			gMk = "YDX";
			break;
		default:
			//console.log("DoNothing");
	}

	if (gMk) {
		RUN();
	}

	function RUN() {
		// cSr: Filting shadowroot content, only used when target within shadowroot
		// 0:off[default] 1:on
		let cSr = 0;
		// Shadowhost, Shadowroot, used when cSr = 1
		let eSh , eSr;

		// cAp: Filtering author/novel page
		// 0:off[default] 1:on
		let cAp = 0;

		// Select mode, 0:off[default] 1:on
		let cSv = 0;

		// cFt: Script firetime, prevent script fires too early
		// 0:Normal[default] 1:ReadyState Complete 2:MutationObserver 3:IntersectionObserver
		let cFt = 0;
		// eOn: MutationObserver targetNode, used when cFt = 2
		let eOn;
		// eIo: IntersectionObserver target, used when cFt = 3
		let eIo;

		// eNo: target nodelist, eUl: userLink, sId: userID, sTg: tag
		let eNo, eUl, sId, sTg;
		// eAt: Alternate text(target's textContent), used when predefine eUl(userlink) doen't contain valid href
		let eAt;

		// rMb: Client type
		// true:Mobile false:Desktop
		const rMb = navigator.userAgent.includes("Mobile");

		const uRi = location.href;
		switch (gMk) {
			case "APS":
				// Alphapolis
				if (uRi.includes("comment")) {
					// Comments
					eNo = "div.comment";
					eUl = "span.name>a";
				} else if (uRi.includes("index") || uRi.includes("ranking")) {
					// Ranking
					eNo = "div.section";
					eUl = "div.author>a";
					sTg = "li.tag a";
				} else if (uRi.includes("author") || (/novel[0-9\/]+$/).test(uRi)) {
					// Author Page
					cAp = 1;
					eUl = uRi.includes("author") ? "div.name>h1" : "div.author a";
				}
				sId = /detail\/(\d+)$/;
				break;
			case "HML":
				// Hameln
				eNo = rMb ? "div.search_box" : "div.section3";
				if (uRi.includes("rank")) {
					// Ranking
					eUl = null;
					eAt = rMb ? "p:nth-child(2)" : "div.blo_title_sak";
					sId = /:(.*)/;
					sTg = rMb ? 'span[id^="tag_"]' : 'div.all_keyword:nth-child(9) a';
				} else if (uRi.includes("review")) {
					// Comments
					eUl = null;
					eAt = rMb ? "h4" : "h3";
					sId = /([^\s]+)/;
				} else if ((/novel|user\/\d+\/$/).test(uRi)) {
					// Author Page
					cAp = 1;
					eUl = null;
					if (/novel\/\d+\/$/.test(uRi)) {
						eAt = 'span[itemprop="author"]'
					} else {
						eAt = rMb ? 'h3>a' : 'h3';
					}
					sId = /([^/]+)/;
				}
				break;

			case "IGN":
				// IGN
				cSr = 1;
				cFt = 2;
				eIo = '#comments-section';
				eOn = '#__next';
				eNo = "li";
				eUl = null;
				eAt = 'span[data-spot-im-class="message-username"]';
				sId = /(.*)/;
				break;
			case "KYU":
				// Kakuyomu
				if (uRi.includes("/pickup") || uRi.includes("/rank") || uRi.includes("/recent")) {
					// Ranking
					eNo = "div.widget-work";
					eUl = "a.widget-workCard-authorLabel";
					sId = /users\/(.*)$/;
					sTg = "a[itemprop='keywords']";
				} else if (uRi.includes("comments")) {
					// Comments
					cFt = 2;
					eOn = '#__next';
					eNo = rMb ? 'div[class^="NewBox_box__"]>ul>li' : 'ul:nth-child(1) li';
					eUl = 'div.partialGiftWidgetActivityName>a';
				} else if (uRi.includes("episodes")) {
					// Comments in Episode
					cFt = 2;
					eOn = "#episodeFooter-cheerComments-panel-mainContents";
					eNo = "ul.widget-cheerCommentList li";
					eUl = "h5.widget-cheerComment-author a";
				} else if (uRi.includes("users") || (/works\/\d+$/).test(uRi)) {
					// Author Page
					cFt = 2;
					eOn = '#__next';
					cAp = 1;
					eUl = uRi.includes("users") ? 'div[class^="HeaderText"]>a' : 'div.partialGiftWidgetActivityName>a';
				}
				sId = /users\/(.*)$/;
				break;
			case "NUC":
				// Narou Comments
				eNo = rMb ? "div.impression" : "div.waku";
				eUl = "div.comment_authorbox>div>a";
				eAt = "div.comment_authorbox>div";
				sId = /\/(\d+)/;
				break;
			case "NRK":
				// Narou Ranking
				if (uRi.includes("mypage") || uRi.includes("ncode")) {
					// Author Page
					cAp = 1;
					eUl = uRi.includes("ncode") ? 'div.novel_writername>a' : 'div.p-userheader__username';
				} else {
					// Ranking
					eNo = "div.p-ranklist-item";
					eUl = "div.p-ranklist-item__author a";
					sTg = "div.p-ranklist-item__keyword a";
				}
				sId = /\/(\d+)/;
				break;
			case "PMF":
				// Palemoon Forum
				if (uRi.includes("viewtopic")) {
					// Topic
					eNo = "#page-body div.post";
					eUl = 'a[class^="username"]';
					sId = /u=(\d+)/;
				} else {
					// Index
					eNo = "ul.topiclist>li";
					eUl = "div.topic-poster>a";
					sId = /u=(\d+)/;
				}
				break;
			case "VVF":
				// Vivaldi Forum
				if (uRi.includes("topic")) {
					// Topic
					eNo = "ul.posts>li";
					eUl = "small.d-flex a";
					sId = /user\/(.*)/;
				} else {
					// Index
					eNo = "ul.topic-list li";
					eUl = "small.hidden-xs>a";
					sId = /user\/(.*)/;
				}
				break;
			case "VVS":
				// Vivaldi Social
				cFt = 2;
				eOn = "div.app-holder";
				eNo = "div.item-list>article";
				eUl = null;
				eAt = "span.display-name__account";
				sId = /@(.*)$/;
				break;
			case "YDX":
				// Yandex Search
				eNo = rMb ? "div.serp-item" : "#search-result>li";
				eUl = rMb ? null : "div.Path>a.Link";
				eAt = "span.Path-Item>b";
				sId = rMb ? /(.*)/ : /\/([^\/]+)/;
				break;
		}

		// GM Menu
		GM_registerMenuCommand("View", SVM);
		GM_registerMenuCommand("Save", USV);

		// Read List
		const URD = GM_getValue(gMk);
		let tlo = URD ? URD : { BAL:[], BTL:[] };
		const tal = tlo.BAL;
		const ttl = tlo.BTL;

		// Save List
		function USV() {
			tlo = { BAL:tal, BTL:ttl };
			GM_setValue(gMk, tlo);
		}

		// Script Fire Time
		switch (cFt) {
			case 1:
				// ReadyState complete
				RSC();
				break;
			case 2:
				// MutationObserver
				MOC();
				break;
			case 3:
				// IntersectionObserver
				IOC();
				break;
			case 0:
			default:
				// Normal
				FMD();
		}

		// ReadyState complete
		function RSC() {
			document.addEventListener("readystatechange", (e) => {
				if (e.target.readyState === "complete") {
					FMD();
				}
			});
		}

		// MutationObserver
		function MOC() {
			const obs = new MutationObserver(() => {
				// Shadowroot fix
				if (cSr) {
					if (!eSr) {
						eSh = document.querySelector('div[data-spotim-module]').firstElementChild;
						if (eSh) {
							eSr = eSh.shadowRoot;
							IOC();
							obs.disconnect();
						}
					} else {
						FMD();
					}
					//console.log(eSr);
				} else {
					FMD();
				}
				// Prevent infinite loop if add/remove element from observe targetnode
				//console.log("MOCXXX");
				//if (document.getElementById("cFbtn")) {
					//obs.disconnect();
				//}
			});
			// Shadowroot fix
			const obn = eSr ? eSr.querySelectorAll('.spcv_conversation')[0] : document.querySelector(eOn);
			obs.observe(obn, {
				childList: true,
				subtree: true
			});
		}

		// IntersectionObserver
		function IOC() {
			const ioc = new IntersectionObserver((entries) => {
				if (entries[0].intersectionRatio <= 0) return;
				// IGN fix
				if (gMk === "IGN") {
					MOC();
				}
				//console.log("IOCXXX");
				ioc.disconnect();
			});
			ioc.observe(document.querySelector(eIo));
		}

		// Filtering mode
		function FMD() {
			if (cAp) {
				// Filtering author/novel page
				FAP();
			} else {
				// Filtering ranking/comments
				FTR();
			}
		}

		// Filtering author/novel page
		function FAP() {
				let rBk = false;
				const eLk = eUl ? document.querySelector(eUl) : document.querySelector(eAt);
				let uId;
				// Narou author page fix
				if (gMk === "NRK") {
					uId = eLk.href ? eLk.href.match(sId)[1] : uRi.match(sId)[1];
				} else {
					uId = eUl ? eLk.href.match(sId)[1] : eLk.textContent.match(sId)[1];
				}
				rBk = CHK(eLk, tal, uId);
				eLk.style.opacity = rBk ? "0.5" : "1";
				eLk.style.color = rBk ? "red" : "lime";
		}

		// Filtering ranking/comments
		function FTR() {
			const no = cSr ? eSr.querySelectorAll(eNo) : document.querySelectorAll(eNo);
			for (let i = 0; i < no.length; i++) {
				let rBk = false;
				let uId;
				// Filtering content contain single id(link) or text
				let eLk = eUl ? no[i].querySelector(eUl) : no[i].querySelector(eAt);
				if (eLk !== null || gMk === "NUC") {
					// Narou nologin user fix
					if (!eLk) {
						eLk = no[i].querySelector(eAt);
						uId = eLk.textContent.split("\n")[2];
					} else {
						uId = eUl ? eLk.href.match(sId)[1] : eLk.textContent.match(sId)[1];
					}
					//console.log(uId);
					rBk = CHK(eLk, tal, uId);
				}

				if (sTg && !rBk) {
					// Filtering content contain multiple tags(text)
					// Tag node
					let tno;
					// Hameln mobile origin tag, custom tag
					let tot, tct;
					if (gMk === "HML" && rMb) {
						tot = no[i].querySelector(".trigger p:nth-child(4)");
						tct = no[i].querySelector(sTg);
						if (!tct) {
							tno = tot.textContent.slice(3).match(/[^\s]+/g);
							tot.innerHTML = "";
						} else {
							tno = no[i].querySelectorAll(sTg);
						}
					} else {
						tno = no[i].querySelectorAll(sTg);
					}
					for (let j = 0; j < tno.length; j++) {
						let tag;
						if (tot && !tct) {
							tag = tno[j];
							tot.innerHTML += '<span id="tag_' + j + '">' + tag + '</span>';
						} else {
							tag = tno[j].textContent;
						}
						//console.log(tag);
						rBk = tot && !tct ? CHK(no[i].querySelector("span#tag_"+j), ttl, tag) : CHK(tno[j], ttl, tag);
						if (rBk) {
							break;
						}
					}
				}

				// Blocked Show Type
				if (cSv === 0) {
					// Vivaldi Social flick fix
					if (gMk === "VVS") {
						no[i].style.visibility = rBk ? "hidden" : "visible";
					} else {
						no[i].style.display = rBk ? "none" : "";
					}
					no[i].style.opacity = "1";
				} else {
					if (gMk === "VVS") {
						no[i].style.visibility = "visible";
					} else {
						no[i].style.display = "";
					}
					no[i].style.opacity = rBk ? "0.5" : "1";
				}
			}
		}

		// CheckKeyword
		function CHK(ele, l, s) {
			const result = l.some((v) => s === v);
			if (cSv === 1) {
				ele.style.border = result ? "thin solid fuchsia" : "thin solid dodgerblue";
			} else {
				ele.style.border = "none";
			}
			return result;
		}

		// Select mode
		function SVM() {
			if (cSv === 0) {
				cSv = 1;
				// Disable default click
				document.addEventListener("click", PAC, true);
			} else {
				cSv = 0;
				// Disable default click
				document.removeEventListener("click", PAC, true);
				// Autosave list when turn off select mode
				USV();
			}
			FMD();
		}

		// PreventAnchorChange
		function PAC(e) {
			e.preventDefault();
			// Vivaldi Social fix
			if (gMk === "VVS") {
				e.stopPropagation();
			}
			const targetElement = cSr ? e.composedPath()[0] : e.target;
			//console.log(targetElement);
			let eLk;
			// Narou nologin user fix
			if (gMk === "NUC") {
				eLk = targetElement.href ? eUl : eAt;
			} else {
				eLk = eUl ? eUl : eAt;
			}
			// StopPropagation fix
			// StopPropagation will stop float button working,
			// So need binding #cFbtn to SVM temporary.
			if (targetElement.closest("#cFbtn")) {
				SVM;
			} else if (targetElement.closest(eLk)) {
				let ai;
				switch (gMk) {
					// Narou author page fix
					case "NRK":
						if (uRi.includes("mypage")) {
							// Author Page
							ai = uRi.match(sId)[1];
						} else {
							ai = eUl ? targetElement.href.match(sId)[1] : targetElement.textContent.match(sId)[1];
						}
						break;
					// Narou nologin user fix
					case "NUC":
						ai = targetElement.href ? targetElement.href.match(sId)[1] : targetElement.textContent.split("\n")[2];
						break;
					// Yandex fix
					case "YDX":
						ai = targetElement.href ? targetElement.href.match(sId)[1] : targetElement.parentElement.href.match(sId)[1];
						break;
					default:
						ai = eUl ? targetElement.href.match(sId)[1] : targetElement.textContent.match(sId)[1];
				}
				//console.log(ai);
				UTL(e, ai, tal);
				FMD();
			} else if (targetElement.closest(sTg)) {
				const kd = targetElement.textContent;
				UTL(e, kd, ttl);
				FMD();
			}
			return false;
		}

		// UpdateTempList
		function UTL(e, s, l) {
			const li = l.findIndex((v) => v === s);
			if (li !== -1) {
				l.splice(li,1);
			} else {
				l.push(s);
			}
			//console.log(l);
			return l;
		}

		// Create Float Button
		const cButton = document.body.appendChild(document.createElement("button"));
		// Button Style
		cButton.id = "cFbtn";
		cButton.textContent = "📘";
		cButton.style = "position: fixed; bottom: 20%; right: 10%; width: 44px; height: 44px; z-index: 9999; font-size: 200%; opacity: 50%;";
		cButton.type = "button";
		cButton.addEventListener("click", (e) => {
			SVM();
		});
		// Vivaldi Social CSP temp fix
		if (gMk === "VVS") {
			cButton.addEventListener("dblclick", (e) => {
				cSv = 1;
				SVM();
			});
		}
	}
})();