- // ==UserScript==
- // @name Google: News Filter
- // @name:zh-TW Google 新聞過濾篩選
- // @name:zh-CN Google 新闻过滤筛选
- // @name:ja Googleニュースフィルター
- // @name:ko Google 뉴스 필터
- // @name:ru Новостной фильтр Google
- // @version 1.0.6
- // @description Show or Hide news whatever you want.
- // @description:zh-TW 可自行設定指定的新聞報社顯示或隱藏。
- // @description:zh-CN 可自行设定指定的新闻报社显示或隐藏。
- // @description:ja 好きなようにニュースを表示または非表示にします。
- // @description:ko 원하는 뉴스를 표시하거나 숨 깁니다.
- // @description:ru Показать или скрыть новости, что вы хотите.
- // @author Hayao-Gai
- // @namespace https://github.com/HayaoGai
- // @icon https://i.imgur.com/zHU2Zt3.png
- // @match https://news.google.com/*
- // @grant GM_setValue
- // @grant GM_getValue
- // ==/UserScript==
-
- /* jshint esversion: 6 */
-
- (function() {
- 'use strict';
-
- // icon made by https://www.flaticon.com/authors/freepik
- const svg = `<svg width="15px" height="15px" viewBox="0 0 192.701 192.701" fill="#5f6368"><path d="M20.746,104.169l75.61-74.528l75.61,74.54c4.74,4.704,12.439,4.704,17.179,0s4.74-12.319,0-17.011l-84.2-82.997 c-4.559-4.511-12.608-4.535-17.191,0l-84.2,83.009c-4.74,4.692-4.74,12.319,0,17.011C8.307,108.873,16.006,108.873,20.746,104.169 z"/><path d="M104.946,88.373c-4.559-4.511-12.608-4.535-17.191,0l-84.2,82.997c-4.74,4.704-4.74,12.319,0,17.011 c4.74,4.704,12.439,4.704,17.179,0l75.622-74.528l75.61,74.54c4.74,4.704,12.439,4.704,17.179,0s4.74-12.319,0-17.011 L104.946,88.373z"/></svg>`;
- const css =
- `.article {
- max-height: 100px;
- overflow: hidden;
- transition: all 0.3s;
- }
- .hide {
- max-height: 0px;
- padding: 0px !important;
- }
- .panel {
- border: 1px solid #dadce0;
- border-radius: 8px;
- padding: 8px;
- margin-right: 10px;
- }
- .title {
- font-size: 1rem;
- font-weight: 500;
- font-family: 'Google Sans', sans-serif;
- display: flex;
- }
- .hover {
- position: fixed;
- width: 450px;
- max-height: 1000px;
- right: 8px;
- top: 60px;
- border: 1px solid #dadce0;
- border-radius: 8px;
- z-index: 999;
- background: white;
- overflow: hidden;
- transition: max-height 0.3s;
- }
- .collapse1 {
- max-height: 0px;
- }
- .collapse2 {
- border: 0px;
- }
- .arrow {
- margin-left: 15px;
- padding-top: 3px;
- cursor: pointer;
- transform: rotate(0deg);
- transition: all 0.3s ease-in-out;
- }
- .rotateArrow {
- transform: rotate(180deg);
- }
- .pressed {
- background-color: #498ce4 !important;
- color: white !important;
- }`;
- let scrolling = false;
-
- CSS();
- locationChange();
- window.addEventListener("load", init);
- window.addEventListener("scroll", update);
-
- function init(retry = 0) {
- // get all news title
- const titles = document.querySelectorAll("a.wEwyrc");
- // check
- if (!titles.length && retry < 5) {
- setTimeout(() => init(retry + 1), 500);
- return;
- }
- // title text
- const text = [...titles].map(title => title.innerText);
- const news = [...new Set(text)].sort();
- addMenu(news);
- }
-
- function addMenu(news) {
- // remove exist
- const exist1 = document.querySelector(".panel");
- const exist2 = document.querySelector(".hover");
- if (exist1) {
- exist2.firstElementChild.remove();
- addOption(exist2, news);
- return;
- }
- // get dynamic class.
- let dynamicClass = "";
- document.querySelector("[ng-non-bindable][data-ogsr-up]").classList.forEach(eachClass => {
- dynamicClass += `.${eachClass}`;
- });
- const parent = document.querySelector(dynamicClass);
- // create
- const panel = document.createElement("div");
- panel.className = "panel";
- parent.insertBefore(panel, parent.firstElementChild);
- const title = document.createElement("h2");
- title.className = "title";
-
- switch(document.querySelector("html").lang) {
- case "zh":
- title.innerText = "Google 新聞過濾篩選";
- break;
- case "ja":
- title.innerText = "Googleニュースフィルター";
- break;
- case "ko":
- title.innerText = "Google 뉴스 필터";
- break;
- case "ru":
- title.innerText = "Новостной фильтр Google";
- break;
- default:
- title.innerText = "Google News Filter";
- }
-
- panel.appendChild(title);
- const icon = document.createElement("div");
- icon.className = "arrow rotateArrow";
- icon.innerHTML = svg;
- icon.addEventListener("click", () => {
- const toggle = hover.classList.toggle("collapse1");
- setTimeout(() => hover.classList.toggle("collapse2"), toggle ? 300 : 0);
- icon.classList.toggle("rotateArrow");
- });
- title.appendChild(icon);
- const hover = document.createElement("div");
- hover.className = "hover collapse1 collapse2";
- document.body.appendChild(hover);
- // option
- addOption(hover, news);
- }
-
- function addOption(parent, news) {
- // create
- const div = document.createElement("div");
- div.className = "ndSf3d ttg1Pb j7vNaf Pz9Pcd a8arzf";
- // append
- parent.appendChild(div);
- // news
- news.forEach(text => singleOption(div, text));
- }
-
- function singleOption(div, text) {
- // create
- const div1 = document.createElement("div");
- div1.className = "To2ZZb u9jkpc hpDt6e DbQnIe rrijPb R7GTQ keNKEd";
- div1.style.cursor = "pointer";
- div1.addEventListener("click", () => setOption(div1, text));
- getOption(div1, text);
- const div2 = document.createElement("div");
- div2.className = "K9tMQ";
- const div3 = document.createElement("div");
- div3.className = "VgnMrb";
- const span1 = document.createElement("span");
- span1.className = "Ix4NZd";
- const span2 = document.createElement("span");
- span2.className = "pPbimc ljLXBd";
- span2.innerText = text;
- // append
- div.appendChild(div1);
- div1.appendChild(div2);
- div2.appendChild(div3);
- div3.appendChild(span1);
- span1.appendChild(span2);
- }
-
- function getOption(div, text) {
- // change color
- const isCollapse = GM_getValue(text, false);
- if (isCollapse) {
- div.classList.add("pressed");
- }
- // collapse or expand
- execution(isCollapse, text);
- }
-
- function setOption(div, text) {
- // change color and record
- const isCollapse = div.classList.toggle("pressed");
- GM_setValue(text, isCollapse);
- // collapse or expand
- execution(isCollapse, text);
- }
-
- function execution(isCollapse, text) {
- // add article class
- document.querySelectorAll("article:not(.article)").forEach(article => article.classList.add("article"));
- // collapse
- if (isCollapse) {
- document.querySelectorAll("article.article:not(.hide)").forEach(article => {
- const title = article.querySelector("a.wEwyrc").innerText;
- if (title.includes(text)) {
- article.classList.add("hide");
- }
- });
- }
- // expand
- else {
- document.querySelectorAll("article.article.hide").forEach(article => {
- const title = article.querySelector("a.wEwyrc").innerText;
- if (title.includes(text)) {
- article.classList.remove("hide");
- }
- });
- }
- }
-
- function update() {
- if (scrolling) return;
- scrolling = true;
- if (document.querySelectorAll("article:not(.article)").length) init();
- setTimeout(() => { scrolling = false; }, 1000);
- }
-
- function CSS() {
- const style = document.createElement("style");
- style.type = "text/css";
- style.innerHTML = css;
- document.head.appendChild(style);
- }
-
- function locationChange() {
- window.addEventListener('locationchange', init);
- // situation 1
- history.pushState = (f => function pushState(){
- var ret = f.apply(this, arguments);
- window.dispatchEvent(new Event('pushState'));
- window.dispatchEvent(new Event('locationchange'));
- return ret;
- })(history.pushState);
- // situation 2
- history.replaceState = (f => function replaceState(){
- var ret = f.apply(this, arguments);
- window.dispatchEvent(new Event('replaceState'));
- window.dispatchEvent(new Event('locationchange'));
- return ret;
- })(history.replaceState);
- // situation 3
- window.addEventListener('popstate', () => {
- window.dispatchEvent(new Event('locationchange'));
- });
- }
-
- })();