rBlock

移除一些网站用于数据统计的跳转功能,加快访问目标地址的速度。包括 Google、GitHub、知乎等,并添加少量定制化。| Removes redirects of some sites, such as Google, GitHub, Zhihu..., add some customization.

当前为 2016-07-06 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         rBlock
// @description  移除一些网站用于数据统计的跳转功能,加快访问目标地址的速度。包括 Google、GitHub、知乎等,并添加少量定制化。| Removes redirects of some sites, such as Google, GitHub, Zhihu..., add some customization.
// @namespace    https://greasyfork.org/zh-CN/scripts/20568-rblock
// @homepageURL  https://github.com/nonoroazoro/rBlock
// @author       nonoroazoro
// @include      /^https?\:\/\/(www|encrypted|news)\.google\./
// @include      /^https?\:\/\/(www|zhuanlan)\.zhihu\./
// @include      /^https?\:\/\/(gist\.)?github\.com/
// @version      1.2.6
// @run-at       document-end
// @grant        none
// ==/UserScript==

if (typeof unsafeWindow === "undefined")
{
    unsafeWindow = window;
}

const rBlock = {

    _blockers: [],

    start: function ()
    {
        this.reset();
        for (let blocker of this._blockers)
        {
            blocker.start();
        }
    },

    reset: function ()
    {
        this._blockers = [];

        // 1. google
        this._blockers.push(
            {
                start: function ()
                {
                    if (/^https?\:\/\/(www|encrypted|news)\.google\./.test(unsafeWindow.location.href))
                    {
                        // 1. for static pages.
                        // when google instant predictions is disabled,
                        // this blocker will only be called once.
                        this._block();

                        // 2. for dynamic pages.
                        // when google instant predictions is enabled,
                        // this blocker will be call whenever the current page is updated.
                        _startMutationObserver(
                            {
                                "id":
                                [
                                    "search", // all/videos/news/shopping/books/apps
                                ],
                                "class":
                                [
                                    "irc_rit", // images
                                    "pspo-popout pspo-lpop", // shopping
                                ]
                            },
                            this._block
                        );
                    }
                },

                // block redirects of google
                _block: function ()
                {
                    const host = unsafeWindow.location.host;
                    if (host === "news.google.com")
                    {
                        // 1. news.google.com
                        const elems = document.querySelectorAll(`.content-pane-container a.article`);
                        for (let e of elems)
                        {
                            _removeListeners(e, "click mousedown");
                        }
                    }
                    else
                    {
                        const base = document.querySelector("div#search");
                        if (base)
                        {
                            // 1. all/videos/news/apps
                            let elems = base.querySelectorAll(`a[onmousedown^="return rwt("]`);
                            for (let e of elems)
                            {
                                _removeAttributes(e, "onmousedown");
                            }

                            // 2. images
                            elems = base.querySelectorAll(`a[class^="irc_"], a[class*=" irc_"]`);
                            for (let e of elems)
                            {
                                _removeListeners(e, "mousedown");
                            }

                            // 3. books
                            revealURL(
                                base.querySelectorAll(`a[href^="/aclk?"]`),
                                /\/aclk?.*url=(.*)/i
                            );

                            // 4. general
                            revealURL(
                                document.querySelectorAll(`a[href*="/url?"]`),
                                /(?:\/url\?.*url=)(.*?)(?:&usg=|$)/i
                            );

                            // 5. cached links
                            elems = document.querySelectorAll(`a[href^="http://webcache.googleusercontent."], a[href^="https://webcache.googleusercontent."]`);
                            let targets = document.querySelectorAll(`a.rblock-cached-link`);
                            if (elems.length != targets.length * 2)
                            {
                                // prevent duplication
                                let menuLink;
                                let cacheLink;
                                for (let elem of elems)
                                {
                                    elem.style.display = "inline";
                                    menuLink = elem.closest("div.action-menu.ab_ctl");

                                    cacheLink = document.createElement("a");
                                    cacheLink.setAttribute("href", elem.getAttribute("href").replace(/^http\:/, "https:"));
                                    cacheLink.setAttribute("class", "rblock-cached-link");
                                    cacheLink.target = "_blank";
                                    cacheLink.innerText = " Cached ";

                                    menuLink.parentNode.insertBefore(cacheLink, menuLink);
                                }
                            }
                        }
                    }
                }
            }
        );

        // 2. zhihu
        this._blockers.push(
            {
                start: function ()
                {
                    if (/^https?\:\/\/(www|zhuanlan)\.zhihu\./.test(unsafeWindow.location.href))
                    {
                        this._block();

                        _startMutationObserver(
                            {
                                "id":
                                [
                                    "js-home-feed-list", // 首页最新动态
                                    "zh-question-answer-wrap", // 具体内容
                                ],
                                "class":
                                [
                                    "zm-item-rich-text expandable js-collapse-body", // 首页、话题显示全部
                                    "zh-general-list clearfix", // 话题
                                ],
                                "data-type":
                                [
                                    "daily", // 发现
                                ]
                            },
                            this._block
                        );
                    }
                },

                _block: function ()
                {
                    // 1. general
                    revealURL(
                        document.querySelectorAll(`a[href^="http://link.zhihu.com"], a[href^="https://link.zhihu.com"], a[href^="//link.zhihu.com"]`),
                        /(?:https?\:)?\/\/link\.zhihu\.com\/\?target=(.*)/i
                    );

                    // 2. open in new tab
                    const elems = document.querySelectorAll(`a.internal`);
                    for (let elem of elems)
                    {
                        _openInNewTab(elem);
                    }
                }
            }
        );

        // 3. github
        this._blockers.push(
            {
                start: function ()
                {
                    if (/^https?\:\/\/(gist\.)?github\.com/.test(unsafeWindow.location.href))
                    {
                        this._block();
                    }
                },

                _block: function ()
                {
                    // 1. general
                    document.addEventListener("keydown", (e) =>
                    {
                        if (e.key === "s" || e.key === "S")
                        {
                            e.stopPropagation();
                        }
                    }, true);
                }
            }
        );
    }
};

function _startMutationObserver(p_filter, p_handler)
{
    if (typeof p_handler === "function")
    {
        const filter = p_filter || {};
        const observer = new unsafeWindow.MutationObserver((p_mutations) =>
        {
            let needUpdate = false;
            for (let mutation of p_mutations)
            {
                for (let key in p_filter)
                {
                    if (p_filter[key].indexOf(mutation.target.getAttribute(key)) != -1)
                    {
                        needUpdate = true;
                        break;
                    }
                }

                if (needUpdate)
                {
                    break;
                }
            }

            if (needUpdate)
            {
                p_handler();
            }
        });
        observer.observe(document.body,
            {
                childList: true,
                subtree: true
            }
        );
    }
}

function _removeAttributes(p_element, p_attributes)
{
    if (p_element && typeof p_attributes === "string")
    {
        const attributes = p_attributes.split(/\W+/) || [];
        for (let attribute of attributes)
        {
            p_element.removeAttribute(attribute);
        }
    }
}

function _removeListeners(p_element, p_events)
{
    if (p_element && typeof p_events === "string")
    {
        const events = p_events.split(/\W+/) || [];
        for (let event of events)
        {
            p_element.removeEventListener(event, _preventDefaultAction, true);
            p_element.addEventListener(event, _preventDefaultAction, true);
        }
    }
}

function _preventDefaultAction(e)
{
    e.stopPropagation();
}

/**
 * reveal url
 */
function revealURL(p_elems, p_regex)
{
    if (p_elems && p_regex)
    {
        for (let e of p_elems)
        {
            // reveal url
            const m = e.getAttribute("href").match(p_regex);
            if (m && m[1])
            {
                e.setAttribute("href", decodeURIComponent(m[1]));
            }

            // open in new tab
            _openInNewTab(e);
        }
    }
}

function _openInNewTab(p_elem)
{
    if (p_elem && !p_elem.target)
    {
        p_elem.target = "_blank";
    }
}

rBlock.start();