解锁PTA复制粘贴限制

这个用户脚本旨在解除PTA(拼题A、Pintia)平台上的复制和粘贴限制,使用户能够自由复制和粘贴文本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         解锁PTA复制粘贴限制
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  这个用户脚本旨在解除PTA(拼题A、Pintia)平台上的复制和粘贴限制,使用户能够自由复制和粘贴文本
// @author       MadelineCarter
// @match        https://pintia.cn/problem-sets/*/exam/problems/*
// @match        https://pintia.cn/problem-sets/*/exam/*
// @grant        none
// @license      All Rights Reserved
// ==/UserScript==

(function() {
    'use strict';

    // 阻止弹窗
    window.alert = function() {};

    // 模拟真实键盘输入
    async function simulateRealTyping(element, text) {
        element.focus();

        for (let i = 0; i < text.length; i++) {
            const char = text[i];

            element.dispatchEvent(new KeyboardEvent('keydown', {
                key: char,
                code: 'Key' + char.toUpperCase(),
                charCode: char.charCodeAt(0),
                keyCode: char.charCodeAt(0),
                which: char.charCodeAt(0),
                bubbles: true,
                cancelable: true,
                composed: true
            }));

            element.dispatchEvent(new KeyboardEvent('keypress', {
                key: char,
                charCode: char.charCodeAt(0),
                keyCode: char.charCodeAt(0),
                which: char.charCodeAt(0),
                bubbles: true,
                cancelable: true
            }));

            const currentValue = element.value || '';
            const start = element.selectionStart || currentValue.length;
            const end = element.selectionEnd || currentValue.length;
            const newValue = currentValue.substring(0, start) + char + currentValue.substring(end);

            const nativeSetter = Object.getOwnPropertyDescriptor(
                element.tagName === 'TEXTAREA' ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype,
                'value'
            ).set;
            nativeSetter.call(element, newValue);

            element.selectionStart = element.selectionEnd = start + 1;

            element.dispatchEvent(new InputEvent('input', {
                data: char,
                inputType: 'insertText',
                bubbles: true,
                cancelable: false,
                composed: true
            }));

            element.dispatchEvent(new KeyboardEvent('keyup', {
                key: char,
                code: 'Key' + char.toUpperCase(),
                charCode: char.charCodeAt(0),
                keyCode: char.charCodeAt(0),
                which: char.charCodeAt(0),
                bubbles: true,
                cancelable: true
            }));

            await new Promise(resolve => setTimeout(resolve, Math.random() * 10));
        }

        element.dispatchEvent(new Event('change', { bubbles: true }));
    }

    // CodeMirror编辑器
    async function simulateCodeMirrorTyping(cm, text) {
        if (cm.setValue) {
            cm.setValue('');
        } else if (cm.dispatch) {
            cm.dispatch({
                changes: {from: 0, to: cm.state.doc.length, insert: ''}
            });
        }

        for (let i = 0; i < text.length; i++) {
            const char = text[i];

            if (cm.replaceSelection) {
                cm.replaceSelection(char);
            } else if (cm.dispatch) {
                const state = cm.state;
                const pos = state.selection.main.head;
                cm.dispatch({
                    changes: {from: pos, insert: char}
                });
            }

            await new Promise(resolve => setTimeout(resolve, Math.random() * 10));
        }
    }

    // 监听粘贴事件 - 捕获阶段优先处理
    document.addEventListener('paste', async function(e) {
        const target = e.target;

        // 只处理输入框
        if (target.tagName !== 'TEXTAREA' &&
            target.tagName !== 'INPUT' &&
            !target.classList.contains('CodeMirror') &&
            target.contentEditable !== 'true') {
            return;
        }

        // 阻止网站的粘贴拦截
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();

        try {
            const text = await navigator.clipboard.readText();
            if (!text) return;

            // 模拟输入
            if (target.classList.contains('CodeMirror') && target.CodeMirror) {
                await simulateCodeMirrorTyping(target.CodeMirror, text);
            } else if (target.tagName === 'TEXTAREA' || target.tagName === 'INPUT') {
                await simulateRealTyping(target, text);
            } else if (target.contentEditable === 'true') {
                for (let char of text) {
                    document.execCommand('insertText', false, char);
                    await new Promise(r => setTimeout(r, Math.random() * 10));
                }
            }
        } catch(err) {
            console.error(err);
        }
    }, true);

    // 监听 Ctrl+V
    document.addEventListener('keydown', async function(e) {
        if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
            const target = e.target;

            // 只处理输入框
            if (target.tagName !== 'TEXTAREA' &&
                target.tagName !== 'INPUT' &&
                !target.classList.contains('CodeMirror') &&
                target.contentEditable !== 'true') {
                return;
            }

            // 阻止默认行为
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();

            try {
                const text = await navigator.clipboard.readText();
                if (!text) return;

                // 模拟输入
                if (target.classList.contains('CodeMirror') && target.CodeMirror) {
                    await simulateCodeMirrorTyping(target.CodeMirror, text);
                } else if (target.tagName === 'TEXTAREA' || target.tagName === 'INPUT') {
                    await simulateRealTyping(target, text);
                } else if (target.contentEditable === 'true') {
                    for (let char of text) {
                        document.execCommand('insertText', false, char);
                        await new Promise(r => setTimeout(r, Math.random() * 10));
                    }
                }
            } catch(err) {
                console.error(err);
            }
        }
    }, true);

})();
/*
    This script is not licensed for use, modification, or distribution.
    All rights are reserved by the author.
*/