Google 新聞過濾篩選

可自行設定指定的新聞報社顯示或隱藏。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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

})();