// ==UserScript== // @name 某OJ平台长代码导入 // @namespace http://tampermonkey.net/ // @version 3.0 // @description 在使用 Ace 编辑器的 OJ 页面:解除长文本粘贴限制 + 文件导入按钮 // @author haoduoyv // @match *://*/* // @grant none // @run-at document-idle // @tag 学习工具 // @license MIT // ==/UserScript== (function () { 'use strict'; const w = window; const _alert = typeof window.alert === 'function' ? window.alert.bind(window) : Window.prototype.alert.bind(window); const _confirm = typeof window.confirm === 'function' ? window.confirm.bind(window) : Window.prototype.confirm.bind(window); // ------------------------- // 把文本写入 Ace 编辑器 // ------------------------- function writeToEditor(text) { const editor = w.ace.edit('source'); editor.setValue(text, -1); editor.clearSelection(); editor.focus(); } // ------------------------- // 拦截粘贴:capture 阶段最先拿到事件, // 读出文本后直接 setValue,再 preventDefault // 阻止 Ace / 页面自己的限制逻辑继续执行。 // ------------------------- let pasteHooked = false; function hookPaste() { if (pasteHooked) return; pasteHooked = true; document.addEventListener('paste', (e) => { // 只在 Ace 编辑器页面生效 if (!isAcePage()) return; const text = e.clipboardData && e.clipboardData.getData('text/plain'); if (!text) return; e.preventDefault(); e.stopImmediatePropagation(); writeToEditor(text); }, true); // capture 阶段,最早执行 } // ------------------------- // 文件导入按钮 // ------------------------- function installUI() { if (!document.body) return; if (!isAcePage()) return; if (document.getElementById('oj-import-toolbar')) return; const _alert_local = _alert; const _confirm_local = _confirm; const toolbar = document.createElement('div'); toolbar.id = 'oj-import-toolbar'; toolbar.style.cssText = [ 'position:fixed', 'right:20px', 'bottom:20px', 'z-index:2147483647', 'display:flex', 'gap:8px' ].join(';'); const btnStyle = [ 'padding:11px 18px', 'color:#fff', 'border:0', 'border-radius:7px', 'box-shadow:0 2px 10px rgba(0,0,0,.2)', 'cursor:pointer', 'font-size:14px', 'font-family:Arial,sans-serif' ].join(';'); // 文件导入按钮 const fileBtn = document.createElement('button'); fileBtn.type = 'button'; fileBtn.textContent = '📂 导入文件'; fileBtn.style.cssText = btnStyle + ';background:#1677ff'; const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.c,.cpp,.cc,.cxx,.h,.hpp,.java,.py,.txt,.js,.cs,.go,.rs'; fileInput.style.display = 'none'; fileInput.addEventListener('change', function () { const file = fileInput.files && fileInput.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function () { try { const content = String(reader.result || ''); const ok = _confirm_local( '导入文件:' + file.name + '\n字符数:' + content.length + '\n\n将替换编辑器全部内容,继续?' ); if (!ok) return; writeToEditor(content); _alert_local('导入成功!字符数:' + content.length); } catch (err) { _alert_local('导入失败:' + err.message); } finally { fileInput.value = ''; } }; reader.onerror = function () { _alert_local('文件读取失败。'); fileInput.value = ''; }; reader.readAsText(file, 'UTF-8'); }); fileBtn.addEventListener('click', () => fileInput.click()); toolbar.appendChild(fileInput); toolbar.appendChild(fileBtn); document.body.appendChild(toolbar); } // ------------------------- // 工具 // ------------------------- function isAcePage() { const source = document.getElementById('source'); return !!( source && source.classList.contains('ace_editor') && w.ace && typeof w.ace.edit === 'function' ); } // ------------------------- // 监听 DOM / URL 变化(SPA) // ------------------------- function observeChanges() { const domOb = new MutationObserver(() => { installUI(); }); const start = () => { if (document.body) { domOb.observe(document.body, { childList: true, subtree: true }); installUI(); } else { setTimeout(start, 100); } }; start(); let lastUrl = location.href; const urlOb = new MutationObserver(() => { const cur = location.href; if (cur !== lastUrl) { lastUrl = cur; setTimeout(installUI, 500); } }); urlOb.observe(document, { subtree: true, childList: true }); w.addEventListener('popstate', () => setTimeout(installUI, 500)); w.addEventListener('hashchange', () => setTimeout(installUI, 500)); } // ------------------------- // 初始化 // ------------------------- function init() { hookPaste(); // 全局只注册一次,越早越好 installUI(); observeChanges(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();