rBlock

移除一些网站用于数据统计的链接跳转及部分其他功能,加快网站访问速度。

目前為 2016-12-02 提交的版本,檢視 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            rBlock
// @author          nonoroazoro
// @description     移除一些网站用于数据统计的链接跳转及部分其他功能,加快网站访问速度。
// @description:en  Remove redirects and some features of web sites.
// @homepageURL     https://github.com/nonoroazoro/rBlock
// @namespace       https://greasyfork.org/zh-CN/scripts/20568-rblock
// @grant           none
// @version         1.3.4
// @run-at          document-end
// @include         /^https?:\/\/(.+\.)?github\./
// @include         /^https?:\/\/(.+\.)?google\./
// @include         /^https?:\/\/(.+\.)?zhihu\./
// ==/UserScript==

const rBlock = {

    _blockers: [],

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

    init: function ()
    {
        this._blockers = [];
        const _host = window.location.host;

        // 1. google
        if (/^(.+\.)?google\./.test(_host))
        {
            this._blockers.push({
                start: function ()
                {
                    // 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.
                    _observe(this._block);
                },

                // block redirects of google
                _block: function ()
                {
                    // 1. general
                    let elems = document.querySelectorAll(`a[href*="url="]`);
                    revealURL(elems, /.*url=(http[^&]+)/i);

                    // 2. images
                    elems = document.querySelectorAll(`a[jsaction][href]:not([href="javascript:void(0)"]):not(.rg_l)`);
                    _removeAttributes(elems, "jsaction");

                    // 3. all/videos/news/apps
                    elems = document.querySelectorAll(`a[onmousedown^="return rwt("]`);
                    _removeAttributes(elems, "onmousedown");

                    // 4. cached links
                    elems = document.querySelectorAll(`a[href^="http://webcache.googleusercontent."], a[href^="https://webcache.googleusercontent."]`);
                    const 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
        if (/^(.+\.)?zhihu\./.test(_host))
        {
            this._blockers.push({
                start: function ()
                {
                    this._block();
                    _observe(this._block);
                },

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

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

        // 3. github
        if (/^(.+\.)?github\./.test(_host))
        {
            this._blockers.push({
                start: function ()
                {
                    this._block();
                },

                _block: function ()
                {
                    // 1. disable keyboard shortcuts: "s" and "w"
                    document.addEventListener("keydown", (e) =>
                    {
                        if (e.key === "s" || e.key === "S" || e.key === "w" || e.key === "W")
                        {
                            e.stopPropagation();
                        }
                    }, true);
                }
            });
        }
    }
};

function _observe(p_callback)
{
    if (typeof p_callback === "function" && document.body)
    {
        (new window.MutationObserver(debounce(p_callback))).observe(document.body, { childList: true, subtree: true });
    }
}

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

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

function revealURL(p_elems, p_regex)
{
    if (p_elems && p_regex)
    {
        let groups;
        for (let elem of p_elems)
        {
            // reveal url & open in new tab
            groups = decodeURIComponent(elem.getAttribute("href")).match(p_regex);
            if (groups && groups[1])
            {
                elem.setAttribute("href", groups[1]);
                _openInNewTab(elem);
            }
        }
    }
}

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

function debounce(p_callback, p_delay = 500)
{
    let timer = null;
    return function (...args)
    {
        const context = this;
        window.clearTimeout(timer);
        timer = window.setTimeout(() =>
        {
            p_callback.apply(context, args);
        }, p_delay);
    };
}

function throttle(p_callback, p_threshhold = 500, p_scope)
{
    let last;
    let timer;
    return function (...args)
    {
        let now = +new Date();
        let context = p_scope || this;
        if (last && now < last + p_threshhold)
        {
            window.clearTimeout(timer);
            timer = window.setTimeout(() =>
            {
                last = now;
                p_callback.apply(context, args);
            }, p_threshhold);
        }
        else
        {
            last = now;
            p_callback.apply(context, args);
        }
    };
}

rBlock.start();