// ==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'));
});
}
})();