Force same-tab links (Ctrl to open new tab)

强制在当前标签打开原本会新开标签/窗口的链接;按 Ctrl/Meta/Shift 或中键仍允许新标签/新窗口。

// ==UserScript==
// @name         Force same-tab links (Ctrl to open new tab)
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  强制在当前标签打开原本会新开标签/窗口的链接;按 Ctrl/Meta/Shift 或中键仍允许新标签/新窗口。
// @author       ChatGPT
// @match        *://*/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 黑名单域(在这些站点禁用脚本,示例)
    const disabledHosts = [
        // 'paypal.com',
        // 'accounts.google.com',
    ];
    if (disabledHosts.some(h => location.host.includes(h))) return;

    // 记录最后一次用户操作(用于判断是否是按住 Ctrl/Meta/Shift/中键)
    let lastUser = { ctrl:false, meta:false, shift:false, button:0, ts:0 };
    function updateLast(e){
        lastUser.ctrl = !!e.ctrlKey;
        lastUser.meta = !!e.metaKey;
        lastUser.shift = !!e.shiftKey;
        if ('button' in e) lastUser.button = e.button;
        lastUser.ts = Date.now();
    }
    document.addEventListener('mousedown', updateLast, true);
    document.addEventListener('keydown', updateLast, true);

    function userRequestedNewTab() {
        // 认为 800ms 内的用户操作有效
        if (Date.now() - lastUser.ts > 800) return false;
        return lastUser.ctrl || lastUser.meta || lastUser.shift || lastUser.button === 1;
    }

    // 拦截 <a> 的点击(优先级高,capture 阶段)
    document.addEventListener('click', function(e) {
        try {
            // 如果用户按住 Ctrl/Meta/Shift 或中键,放行(允许新标签)
            if (e.ctrlKey || e.metaKey || e.shiftKey || e.button === 1) return;

            const a = e.target.closest && e.target.closest('a[href]');
            if (!a) return;

            const href = a.getAttribute('href') || '';
            // 忽略锚点、javascript:、下载链接
            if (!href || href.startsWith('#') || href.startsWith('javascript:') || a.hasAttribute('download')) return;

            const target = (a.getAttribute('target') || '').toLowerCase();
            if (target && target !== '_self') {
                e.preventDefault();
                // 在当前页跳转(保留历史)
                location.assign(a.href);
            }
        } catch(err) {
            // ignore
        }
    }, true);

    // 覆盖 window.open(处理通过 JS 强制打开新窗口的场景)
    (function() {
        const nativeOpen = window.open;
        window.open = function(url, target, features) {
            try {
                if (!url) return nativeOpen.call(window, url, target, features);
                if (userRequestedNewTab()) {
                    return nativeOpen.call(window, url, target || '_blank', features);
                } else {
                    // 在当前窗口打开(替代新窗口)
                    location.assign(String(url));
                    return null;
                }
            } catch (err) {
                return nativeOpen.call(window, url, target, features);
            }
        };
    })();

    // 防止 <base target="_blank"> 导致所有链接新开标签
    const fixBase = () => {
        const bases = document.getElementsByTagName('base');
        for (let b of bases) {
            if (b.target && b.target.toLowerCase() === '_blank') {
                b.target = '_self';
            }
        }
    };
    fixBase();
    const mo = new MutationObserver(fixBase);
    mo.observe(document.documentElement || document, { childList: true, subtree: true });

})();