rBlock

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

目前為 2016-07-15 提交的版本,檢視 最新版本

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 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.7
// @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.key === "w" || e.key === "W")
                        {
                            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();