Web Test Bypass Copy&Paste

Universal script to enable copy/paste on any webpage and local files

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name              Web Test Bypass Copy&Paste
// @name:en           Web Test Bypass Copy&Paste
// @name:ja           webテストコピペバイパス

// @namespace         https://educational-tools.example.com
// @version           2.0.1-universal
// @description       Universal script to enable copy/paste on any webpage and local files
// @description:en    Universal script to enable copy/paste on any webpage and local files
// @description:zh    通用脚本,可在任何网页和本地文件上启用复制/粘贴
// @description:ja    あらゆるウェブページとローカルファイルでコピー/ペーストを有効にする汎用スクリプト

// @author            Educational Tools
// @license           MIT

// @match             *://*/*
// @match             file://*/*
// @include           *
// @include           file://*

// @grant             GM_addStyle
// @grant             unsafeWindow
// @run-at            document-start

// ==/UserScript==

(function() {
    'use strict';

    // Configuration
    const config = {
        debug: false,
        aggressive: true,
        includeFrames: true,
        continuousMode: true
    };

    // Log function for debugging
    function log(...args) {
        if (config.debug) {
            console.log('[Universal Copy Enable]', ...args);
        }
    }

    // Main initialization
    function initialize() {
        log('Initializing Universal Copy Enable');
        
        // Apply to main window
        applyToContext(window);
        
        // Apply to unsafeWindow if available (for Greasemonkey/Tampermonkey compatibility)
        if (typeof unsafeWindow !== 'undefined' && unsafeWindow !== window) {
            applyToContext(unsafeWindow);
        }
        
        // Set up continuous monitoring
        if (config.continuousMode) {
            setInterval(() => {
                removeAllRestrictions();
                if (config.includeFrames) {
                    processFrames();
                }
            }, 3000);
        }
        
        // Apply CSS globally
        injectGlobalCSS();
        
        // Set up mutation observer
        setupMutationObserver();
        
        // Handle special cases
        handleSpecialCases();
    }

    // Apply copy-enabling to a specific context (window/frame)
    function applyToContext(ctx) {
        try {
            // Override restrictive functions
            overrideRestrictiveFunctions(ctx);
            
            // Enable event handlers
            enableEventHandlers(ctx);
            
            // Clear existing restrictions
            clearContextRestrictions(ctx);
            
            log('Applied to context:', ctx);
        } catch (e) {
            log('Error applying to context:', e);
        }
    }

    // Override functions that might prevent copying
    function overrideRestrictiveFunctions(ctx) {
        // Save original functions
        const originals = {
            addEventListener: ctx.EventTarget?.prototype?.addEventListener,
            preventDefault: ctx.Event?.prototype?.preventDefault,
            stopPropagation: ctx.Event?.prototype?.stopPropagation
        };
        
        // List of events to intercept
        const restrictedEvents = [
            'copy', 'cut', 'paste', 'select', 'selectstart',
            'contextmenu', 'dragstart', 'dragend', 'drag',
            'beforecopy', 'beforecut', 'beforepaste'
        ];
        
        // Override addEventListener
        if (ctx.EventTarget?.prototype) {
            ctx.EventTarget.prototype.addEventListener = function(type, listener, options) {
                if (restrictedEvents.includes(type)) {
                    log('Blocked addEventListener for:', type);
                    return;
                }
                return originals.addEventListener?.call(this, type, listener, options);
            };
        }
        
        // Override preventDefault for specific events
        if (ctx.Event?.prototype) {
            ctx.Event.prototype.preventDefault = function() {
                if (restrictedEvents.includes(this.type)) {
                    log('Blocked preventDefault for:', this.type);
                    return;
                }
                return originals.preventDefault?.call(this);
            };
            
            ctx.Event.prototype.stopPropagation = function() {
                if (restrictedEvents.includes(this.type)) {
                    log('Blocked stopPropagation for:', this.type);
                    return;
                }
                return originals.stopPropagation?.call(this);
            };
        }
        
        // Override document.write protection
        if (ctx.document) {
            const originalWrite = ctx.document.write;
            ctx.document.write = function(...args) {
                log('document.write intercepted');
                return originalWrite.apply(this, args);
            };
        }
    }

    // Enable copy/paste event handlers
    function enableEventHandlers(ctx) {
        const doc = ctx.document;
        if (!doc) return;
        
        // Event handler that allows the event
        const allowEvent = (e) => {
            e.stopImmediatePropagation();
            return true;
        };
        
        // Events to enable
        const events = [
            'copy', 'cut', 'paste', 'select', 'selectstart',
            'contextmenu', 'dragstart', 'beforecopy', 'beforecut'
        ];
        
        // Add listeners with maximum priority
        events.forEach(eventName => {
            doc.addEventListener(eventName, allowEvent, true);
            ctx.addEventListener(eventName, allowEvent, true);
        });
        
        // Special handling for keyboard shortcuts
        doc.addEventListener('keydown', (e) => {
            // Allow all Ctrl/Cmd combinations
            if (e.ctrlKey || e.metaKey) {
                const key = e.key.toLowerCase();
                if (['a', 'c', 'x', 'v'].includes(key)) {
                    e.stopImmediatePropagation();
                    return true;
                }
            }
        }, true);
    }

    // Clear restrictions from context
    function clearContextRestrictions(ctx) {
        const doc = ctx.document;
        if (!doc) return;
        
        // Events to clear
        const events = [
            'copy', 'cut', 'paste', 'select', 'selectstart',
            'contextmenu', 'mousedown', 'mouseup', 'selectend',
            'dragstart', 'dragend', 'drag', 'beforecopy',
            'beforecut', 'beforepaste', 'keydown', 'keyup'
        ];
        
        // Clear document and window handlers
        events.forEach(evt => {
            doc['on' + evt] = null;
            ctx['on' + evt] = null;
            
            // Remove attributes
            if (doc.documentElement?.removeAttribute) {
                doc.documentElement.removeAttribute('on' + evt);
            }
            if (doc.body?.removeAttribute) {
                doc.body.removeAttribute('on' + evt);
            }
        });
        
        // Process all elements
        const elements = doc.querySelectorAll('*');
        elements.forEach(el => {
            events.forEach(evt => {
                el['on' + evt] = null;
                if (el.removeAttribute) {
                    el.removeAttribute('on' + evt);
                }
            });
            
            // Force selection styles
            if (el.style) {
                el.style.userSelect = 'text';
                el.style.webkitUserSelect = 'text';
                el.style.MozUserSelect = 'text';
                el.style.msUserSelect = 'text';
                el.style.KhtmlUserSelect = 'text';
                el.style.pointerEvents = 'auto';
            }
        });
    }

    // Inject global CSS
    function injectGlobalCSS() {
        const css = `
            /* Universal text selection enabler */
            *, *::before, *::after {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
                user-select: text !important;
                -webkit-user-drag: auto !important;
                -webkit-touch-callout: default !important;
            }
            
            /* Specific elements that often have selection disabled */
            body, div, span, p, a, h1, h2, h3, h4, h5, h6,
            pre, code, blockquote, em, strong, i, b, u,
            table, tr, td, th, tbody, thead, tfoot,
            ul, ol, li, dl, dt, dd,
            article, section, nav, aside, header, footer, main,
            form, input, textarea, select, option, label, button,
            img, video, audio, canvas, svg {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
                user-select: text !important;
            }
            
            /* Selection highlighting */
            ::selection {
                background-color: #338FFF !important;
                color: white !important;
            }
            
            ::-moz-selection {
                background-color: #338FFF !important;
                color: white !important;
            }
            
            /* Override inline styles */
            [style*="user-select: none" i],
            [style*="user-select:none" i] {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                user-select: text !important;
            }
            
            /* Ensure pointer events */
            [style*="pointer-events: none" i],
            [style*="pointer-events:none" i] {
                pointer-events: auto !important;
            }
            
            /* Remove overlays that might block selection */
            [class*="overlay" i]:not(input):not(textarea),
            [id*="overlay" i]:not(input):not(textarea),
            [class*="modal-backdrop" i],
            [class*="cover" i]:not(input):not(textarea) {
                pointer-events: none !important;
            }
            
            /* Special handling for code blocks */
            pre, code, .hljs, [class*="highlight" i] {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                user-select: text !important;
            }
            
            /* Disable any custom cursors that might interfere */
            * {
                cursor: auto !important;
            }
            
            *:hover {
                cursor: auto !important;
            }
            
            /* For view-source pages */
            .line-number, .source-line {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                user-select: text !important;
            }
        `;
        
        if (GM_addStyle) {
            GM_addStyle(css);
        } else {
            const style = document.createElement('style');
            style.textContent = css;
            (document.head || document.documentElement).appendChild(style);
        }
    }

    // Remove all restrictions (called periodically)
    function removeAllRestrictions() {
        applyToContext(window);
        clearContextRestrictions(window);
    }

    // Process iframes
    function processFrames() {
        try {
            const frames = document.querySelectorAll('iframe, frame');
            frames.forEach(frame => {
                try {
                    if (frame.contentWindow) {
                        applyToContext(frame.contentWindow);
                    }
                } catch (e) {
                    // Cross-origin frame, skip
                }
            });
        } catch (e) {
            log('Error processing frames:', e);
        }
    }

    // Set up mutation observer
    function setupMutationObserver() {
        if (!window.MutationObserver) return;
        
        const observer = new MutationObserver((mutations) => {
            let shouldReapply = false;
            
            mutations.forEach(mutation => {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    shouldReapply = true;
                }
                if (mutation.type === 'attributes' && 
                    (mutation.attributeName === 'style' || 
                     mutation.attributeName?.startsWith('on'))) {
                    shouldReapply = true;
                }
            });
            
            if (shouldReapply) {
                removeAllRestrictions();
            }
        });
        
        const startObserving = () => {
            const target = document.body || document.documentElement;
            if (target) {
                observer.observe(target, {
                    childList: true,
                    subtree: true,
                    attributes: true,
                    attributeFilter: ['style', 'oncontextmenu', 'onselectstart', 'oncopy']
                });
            } else {
                setTimeout(startObserving, 100);
            }
        };
        
        startObserving();
    }

    // Handle special cases for specific types of pages
    function handleSpecialCases() {
        // For view-source pages
        if (window.location.href.startsWith('view-source:')) {
            document.addEventListener('DOMContentLoaded', () => {
                const preElements = document.querySelectorAll('pre');
                preElements.forEach(pre => {
                    pre.style.userSelect = 'text';
                    pre.style.webkitUserSelect = 'text';
                });
            });
        }
        
        // For PDF viewers
        if (document.querySelector('embed[type="application/pdf"]')) {
            log('PDF detected, applying special handling');
        }
        
        // For code highlighting libraries
        setTimeout(() => {
            const codeBlocks = document.querySelectorAll('pre code, .hljs, .highlight');
            codeBlocks.forEach(block => {
                block.style.userSelect = 'text';
                block.style.webkitUserSelect = 'text';
            });
        }, 1000);
    }

    // Initialize immediately and on various load events
    initialize();
    
    // Reinitialize on different load stages
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    }
    window.addEventListener('load', initialize);
    
    // For single-page applications
    let lastUrl = location.href;
    new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            setTimeout(initialize, 100);
        }
    }).observe(document, {subtree: true, childList: true});

    log('Universal Copy Enable loaded successfully');
})();