rBlock

移除一些网站用于数据统计的链接跳转,加快网站访问速度。

当前为 2017-06-14 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name            rBlock
// @author          nonoroazoro
// @description     移除一些网站用于数据统计的链接跳转,加快网站访问速度。
// @description:en  Removes redirects of web sites.
// @homepageURL     https://github.com/nonoroazoro/firefox/tree/master/greasemonkey/rBlock
// @namespace       https://greasyfork.org/zh-CN/scripts/20568-rblock
// @grant           none
// @version         1.3.7
// @run-at          document-end
// @include         /^https?:\/\/(.+\.)?google\./
// @include         /^https?:\/\/(.+\.)?zhihu\./
// @include         https://forum.gamer.com.tw/*
// ==/UserScript==

const rBlock = {

    _blockers: [],

    start: function ()
    {
        this.init();
        for (const 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 (const 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 (const elem of document.querySelectorAll(`a.internal`))
                    {
                        _openInNewTab(elem);
                    }
                }
            });
        }

        // 3. 巴哈姆特
        if (_host === "forum.gamer.com.tw")
        {
            this._blockers.push({
                start: function ()
                {
                    this._block();
                    _observe(this._block);
                },

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

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 (const elem of p_elements)
        {
            for (const attr of attributes)
            {
                elem.removeAttribute(attr);
            }
        }
    }
}

/* eslint no-unused-vars: "off" */
function _removeListeners(p_element, p_events)
{
    if (p_element && typeof p_events === "string")
    {
        const events = p_events.split(/\W+/) || [];
        for (const 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 (const 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)
    {
        const now = +new Date();
        const 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();