知乎浏览助手

知乎浏览助手. 如果想报 bug, 可以通过知乎私信联系我, zhihu.com/people/kougazhang

// ==UserScript==
// @name   知乎浏览助手
// @namespace    http://tampermonkey.net/
// @version      0.0.4
// @description 知乎浏览助手. 如果想报 bug, 可以通过知乎私信联系我, zhihu.com/people/kougazhang
// @author        kgzhang
// @match        https://www.zhihu.com/*
// @grant        GM_addStyle
// @grant        GM_log
// ==/UserScript==

// 问题页加宽
GM_addStyle(".Question-mainColumn { width: unset !important; } ");
GM_addStyle(".QuestionAnswers-answers { width: 1000px !important; } ");
// 推荐页加宽
GM_addStyle(".Topstory-mainColumn { width: 1000px !important; } ");
// 去除右边栏
GM_addStyle(".GlobalSideBar { display: none !important; } ");
// 去除问题页右边栏
GM_addStyle(".Question-sideColumn { display: none !important; } ");
// 去除 topic 右边栏
GM_addStyle(".ContentLayout-sideColumn { display: none !important; } ");
// topic 加宽
GM_addStyle(".Topic-bar--borderBottom {width: 1000px;}");
// 搜索页加宽
GM_addStyle(".ListShortcut {width: 1000px; font-size: 20px}");
// 搜索页隐藏右边栏 SearchSideBar
GM_addStyle(".SearchSideBar {display: none;}");
// 个人页面隐藏右边栏
GM_addStyle(".Profile-sideColumn {display: none; }");
// 加宽
GM_addStyle(".Profile-mainColumn {width: 1000px; }");

(function () {
    'use strict';
    // Your code here...

    const urlZhiHuFeed = "https://www.zhihu.com/";
    const reUrlZhiHuQuestion = /https:\/\/www.zhihu.com\/question\/.*/;
    const reUrlZhiHuFollow = /https:\/\/www.zhihu.com\/follow.*/;
    const color = [
        {name: 'blue', value: '#0084ff'},
        {name: 'green', value: '#5cb85c'},
        {name: 'red', value: '#d9534f'},
        {name: 'green2', value: '#004E00'},
        {name: 'gray', value: 'gray'},
    ];
    const btnStyle = 'margin-top: 15px; margin-bottom: 15px; margin-left:-18px; cursor:pointer; color: #fff; border-radius: 3px; border: 1px solid; padding: 3px 6px';

    const getColor = (name) => {
        for (let c of color) {
            if (c.name === name) {
                return c.value;
            }
        }
    };

    // 复制剪贴板
    function updateClipboard(newClip) {
        navigator.clipboard.writeText(newClip).then(function () {
            alert('succeed copy');
        }, function (err) {
            // clipboard write failed
            console.info('failed copy', err);
            alert('faild copy')
        });
    }

    // 防抖
    function debounce(fn, wait) {
        let timeout = null;
        return function () {
            if (timeout !== null) clearTimeout(timeout);
            timeout = setTimeout(fn, wait);
        }
    }

    class ElementEventSwitch {

        constructor(conf) {
            this.true = '1';
            this.false = '0';
            this.$all = {
                do: {
                    status: this.true,
                    color: conf.do.color,
                    confirmInfo: conf.do.confirmInfo,
                    eleText: conf.do.eleText,
                    action: () => {
                        if (confirm(this.$all.do.confirmInfo)) {
                            this.mountEvent();
                            this.setLocalStatus(this.$all.do.status);
                            this.changeColor(this.$all.do.color);
                            this.changeInnerText(this.$all.do.eleText);
                        }
                    }
                },
                undo: {
                    status: this.false,
                    color: conf.undo.color,
                    eleText: conf.undo.eleText,
                    confirmInfo: conf.undo.confirmInfo,
                    action: () => {
                        if (confirm(this.$all.undo.confirmInfo)) {
                            this.unmountEvent();
                            this.setLocalStatus(this.$all.undo.status);
                            this.changeColor(this.$all.undo.color);
                            this.changeInnerText(this.$all.undo.eleText);
                        }
                    }
                }
            };
            this.eleID = conf.eleID;
            this.eleType = conf.eleType;
            this.eleStyle = conf.eleStyle;
            this.text = conf.eleText;
            this.eventType = conf.eventType;
            this.eventAction = conf.eventAction;
        }

        create() {
            // 根据 localStatus 进行初始化动作
            let color = this.$all.do.color;
            let text = this.$all.do.eleText;
            if (this.localStatusIsTrue()) {
                this.mountEvent();
            } else {
                color = this.$all.undo.color;
                text = this.$all.undo.eleText;
            }
            // 创建元素
            const ele = createEle(this.eleType,
                text,
                {
                    id: this.eleID,
                    style: `background: ${color}; ${this.eleStyle}`
                });
            ele.addEventListener('click', (e) => {
                !this.localStatusIsTrue() ? this.$all.do.action() : this.$all.undo.action();
            });
            return ele;
        }

        localStatusIsTrue() {
            return localStorage.getItem(this.eleID) === this.$all.do.status
        }

        setLocalStatus(status) {
            localStorage.setItem(this.eleID, status);
        }

        mountEvent() {
            window.addEventListener(this.eventType, this.eventAction);
        }

        unmountEvent() {
            window.removeEventListener(this.eventType, this.eventAction);
        }

        changeColor(color) {
            document.getElementById(this.eleID).style.backgroundColor = color;
        }

        changeInnerText(text) {
            document.getElementById(this.eleID).innerText = text;
        }

    }

    // 创建元素
    function createEle(eleName, text, attrs) {
        let ele = document.createElement(eleName);
        ele.innerText = text;
        for (let k in attrs) {
            ele.setAttribute(k, attrs[k]);
        }
        return ele;
    }

    function block(item) {
        const move = (x) => {
            x.style.display = "none";
        };

        if (item === undefined) {
            return undefined;
        } else if (item instanceof window.NodeList) {
            item.forEach(i => block(i));
        } else {
            move(item);
        }
    }

    // 屏蔽非问题推荐
    function displayQuestionOnly() {
        // 只在 feed 页生效
        if (location.href !== urlZhiHuFeed) {
            return;
        }
        for (let item of document.getElementsByClassName('TopstoryItem-isRecommend')) {
            const title = item.getElementsByTagName('a')[0].innerText;
            if (!title.endsWith("?")) {
                block(item);
            } else {
            }
        }
    }

    // 屏蔽热榜
    function blockTopStoryHot() {
        block(document.querySelectorAll('a[href="/hot"]'));
    }

    function blockActivity() {
        block(document.querySelector('a[class="css-w3ttmg"]'));
    }

    // 过滤推荐
    function addBtnDisplayQuestion() {
        const conf = {
            do: {
                confirmInfo: '确认开启过滤推荐?',
                color: getColor('blue'),
                eleText: '过滤:)',
            },
            undo: {
                color: getColor('gray'),
                confirmInfo: '确认关闭过滤推荐?',
                eleText: '过滤:('
            },
            eleStyle: btnStyle,
            eleID: 'btnElementEventSwitch',
            eleType: 'a',
            eventType: 'scroll',
            eventAction: debounce(displayQuestionOnly, 100),
        };
        const btnQuestion = new ElementEventSwitch(conf);
        // 显示在热榜节点的后面
        const eleRecommend = document.querySelectorAll('a[href="/"]')[1];
        eleRecommend.parentElement.insertBefore(btnQuestion.create(), eleRecommend.nextSibling);
    }

    function addReprintBtn() {
        let num = 0;
        for (let item of document.querySelectorAll('div[class="List-item"]')) {
            const eleMeta = item.getElementsByClassName('ContentItem-meta')[0];
            const answerID = item.getElementsByTagName('div')[0].getAttribute('name');
            if (document.getElementById(answerID)) {
                continue;
            }
            const text = eleMeta.parentElement.getElementsByClassName("RichContent")[0].innerText;
            const reprintBtn = createEle('button',
                '一键转载',
                {
                    id: answerID,
                    style: `background:${color[num % color.length].value}; ${btnStyle}; margin-left:0`
                });
            // reprintBtn.setAttribute('value', text);
            reprintBtn.addEventListener('click', () => {
                updateClipboard(text)
            });
            eleMeta.append(reprintBtn);
            num++;
        }
    }


    function importantFollow() {
        let num = 0;
        const parseEleUrl = (eleUrl) => {
            if (!eleUrl) {
                return undefined;
            }
            const url = eleUrl.getAttribute('content');
            return {
                url: url,
                key: url ? url.split('/').pop() : '',
            }
        };
        for (let item of document.querySelectorAll('div[class~="TopstoryItem"]')) {
            const eleUrls = item.querySelectorAll('meta[itemprop="url"]');
            if (!eleUrls) {
                continue;
            }
            const author = parseEleUrl(eleUrls[0]);
            const question = parseEleUrl(eleUrls[1]);
            const answer = parseEleUrl(eleUrls[2]);
            if (!(author && question && answer)) {
                continue;
            }
            const cacheKey = `${author.key},${question.key},${answer.key}`;
            if (sessionStorage.getItem(cacheKey)) {
                continue;
            }
            const eleMeta = item.querySelector('div[class="FeedSource"]');
            const reprintBtn = createEle('button',
                '重点关注',
                {style: `background:${color[num % color.length].value}; ${btnStyle}; margin-left:0`});
            // // reprintBtn.setAttribute('value', text);
            // reprintBtn.addEventListener('click', () => {
            //     updateClipboard(text)
            // });
            eleMeta.append(reprintBtn);
            sessionStorage.setItem(cacheKey, '1');
            num++;
        }
    }

    window.onload = (e) => {

        // 屏蔽热榜和屏蔽活动栏
        if (location.href === urlZhiHuFeed || reUrlZhiHuFollow.test(location.href)) {
            // 屏蔽热榜
            blockTopStoryHot();
            // 屏蔽活动栏
            blockActivity();
            // 屏蔽顶部栏的发现按钮
            block(document.querySelector('a[href="//www.zhihu.com/explore"]').parentElement);
            // 屏蔽顶部栏等你来答
            block(document.querySelector('a[href="//www.zhihu.com/question/waiting"]').parentElement)
            // 屏蔽搜索框 placeholder
            document.getElementsByTagName('input')[0].placeholder = '';
        }

        // 对知乎首页的优化
        if (location.href === urlZhiHuFeed) {
            // 过滤推荐
            addBtnDisplayQuestion();
            // 跳转到关注页面
            document.querySelector('a[href="/follow"]').click();
        }
    };

    // follow 页去广告
    setInterval(() => {
        if (reUrlZhiHuFollow.test(location.href)) {
            block(document.querySelectorAll('div[class~="TopstoryItem--advertCard"]'));
        }
    }, 200);

    window.addEventListener('scroll', debounce(() => {
        // follow 页添加重点关注按钮
        if (reUrlZhiHuFollow.test(location.href)) {
            importantFollow();
            // 问题页增加转载按钮
        } else if (reUrlZhiHuQuestion.test(location.href)) {
            addReprintBtn();
        }

    }, 100));

})();