Greasy Fork

网页限制解除器

深度解除网页复制内容限制 智能恢复右键菜单/文本选择/剪贴板操作/拖拽功能

目前为 2025-04-06 提交的版本。查看 最新版本

// ==UserScript==
// @name         网页限制解除器
// @description  深度解除网页复制内容限制 智能恢复右键菜单/文本选择/剪贴板操作/拖拽功能
// @version      1.1.0
// @author       念柚
// @namespace    https://github.com/MiPoNianYou/UserScripts
// @license      GPL-3.0
// @match        *://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

class EnhancedLiberator {
    constructor() {
        this.InitializeObserver();
        this.ExecuteCoreFeatures();
        this.HijackEventListeners();
    }

    ExecuteCoreFeatures() {
        this.RemoveEventListeners(document);
        this.RemoveEventListeners(document.body);
        this.InjectLiberationStyles();
        this.BindGlobalEventHandlers();
        this.ProcessExistingElements();
    }

    BindGlobalEventHandlers() {
        const events = [
            ['contextmenu', e => e.stopImmediatePropagation()],
            ['selectstart', e => e.stopImmediatePropagation()],
            ['copy', e => e.stopImmediatePropagation()],
            ['cut', e => e.stopImmediatePropagation()],
            ['paste', e => e.stopImmediatePropagation()]
        ];
        events.forEach(([type, handler]) => {
            document.addEventListener(type, handler, {capture: true, passive: true});
        });
    }

    InjectLiberationStyles() {
        const css = `*,*::before,*::after{
            -webkit-user-select:text!important;
            user-select:text!important;
            -webkit-user-drag:auto!important;
            user-drag:auto!important;
        }`;
        if (!document.getElementById('liberator-style')) {
            const style = document.createElement('style');
            style.id = 'liberator-style';
            style.textContent = css;
            (document.head || document.documentElement).append(style);
        }
    }

    ProcessExistingElements() {
        try {
            const walker = document.createTreeWalker(
                document.documentElement,
                NodeFilter.SHOW_ELEMENT
            );
            let node;
            while ((node = walker.nextNode())) {
                this.RemoveEventListeners(node);
            }
        } catch (e) { console.debug('Liberator:', e); }
    }

    RemoveEventListeners(element) {
        if (!element || !element.addEventListener) return;

        // 清除属性事件
        ['oncontextmenu','onselectstart','oncopy','oncut','onpaste','ondrag','ondragstart']
            .forEach(prop => { element[prop] = null; });

        // 劫持addEventListener
        const nativeAdd = EventTarget.prototype.addEventListener;
        element.addEventListener = function(type, listener, options) {
            if (['contextmenu','selectstart','copy','cut','paste','drag','dragstart']
                .includes(type)) return;
            nativeAdd.call(this, type, listener, options);
        };
    }

    HijackEventListeners() {
        const nativeAdd = EventTarget.prototype.addEventListener;
        EventTarget.prototype.addEventListener = function(type, listener, options) {
            if (['contextmenu','selectstart','copy','cut','paste','drag','dragstart']
                .includes(type)) return;
            nativeAdd.call(this, type, listener, options);
        };
    }

    InitializeObserver() {
        new MutationObserver(mutations => {
            mutations.forEach(mutation => {
                mutation.addedNodes.forEach(node => {
                    if (node.nodeType === 1) {
                        this.RemoveEventListeners(node);
                        this.ProcessExistingElements();
                    }
                });
            });
        }).observe(document, {
            childList: true,
            subtree: true
        });
    }
}

new EnhancedLiberator();