rBlock

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

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل 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.8
// @run-at          document-end
// @include         /^https?:\/\/(.+\.)?google\./
// @include         /^https?:\/\/(.+\.)?zhihu\./
// @include         https://forum.gamer.com.tw/*
// ==/UserScript==

const rBlock = {

    _blockers: [],

    start()
    {
        this.init();
        for (const blocker of this._blockers)
        {
            blocker.start();
        }
    },

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

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

                _block()
                {
                    // 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()
                {
                    this._block();
                    _observe(this._block);
                },

                _block()
                {
                    // 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 _inner(...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 _inner(...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();