// ==UserScript== // @name 自动复制选中文本和解除复制限制 // @name:zh-CN 自动复制选中文本和解除复制限制 // @name:zh-TW 自動複製選中文本和解除複製限制 // @name:en Auto Copy Selected Text and Remove Copy Restrictions // @name:ja 選択したテキストを自動コピーし、コピー制限を解除 // @name:ko 선택한 텍스트 자동 복사 및 복사 제한 해제 // @description 在任意网站选中任意文本时自动复制, 并提供设置选项以启用/禁用解除网站的复制限制和自动复制功能, 以及显示/隐藏按钮 // @description:zh-CN 在任意网站选中任意文本时自动复制, 并提供设置选项以启用/禁用解除网站的复制限制和自动复制功能, 以及显示/隐藏按钮 // @description:zh-TW 在任何網站選中任意文本時自動複製, 並提供設置選項以啟用/禁用解除網站的複製限制和自動複製功能, 以及顯示/隱藏按鈕 // @description:en Automatically copy selected text on any website and provide options to enable/disable removal of copy restrictions and auto copy functionality, as well as show/hide button // @description:ja 任意のウェブサイトで選択したテキストを自動的にコピーし、コピー制限の解除と自動コピー機能の有効/無効、表示/非表示ボタンの設定オプションを提供します // @description:ko 모든 웹사이트에서 선택한 텍스트를 자동 복사하며, 웹사이트의 복사 제한 해제 및 자동 복사 기능 활성화/비활성화, 버튼 표시/숨기기 설정 옵션을 제공합니다 // @author lbihhe // @license MIT // @icon https://img.icons8.com/nolan/64/password1.png // @version 3.2.2 // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // @run-at document-end // @namespace http://tampermonkey.net/ // @downloadURL none // ==/UserScript== (() => { 'use strict'; /*** 国际化部分 ***/ const userLang = (navigator.language || navigator.userLanguage || 'en').toLowerCase(); const language = userLang.startsWith('zh') ? 'zh' : userLang.startsWith('ja') ? 'ja' : userLang.startsWith('es') ? 'es' : 'en'; const messages = { zh: { enableCopy: '🔓解除复制限制并启用自动复制', disableCopy: '🔒禁用复制功能及复制限制', copyTextAlert: '选中文本已复制到剪贴板', copyHTMLAlert: '选中的 HTML 已复制到剪贴板', copyFailureAlert: '复制失败', copyExceptionAlert: '复制过程中出现异常: ', invalidCopyFormatAlert: '无效的复制格式,保留当前设置。', copyFormatPrompt: '请选择复制格式(text: 纯文本, html: HTML, link: 链接和文本):', toggleShowButton: '设置显示/隐藏解除复制限制按钮', setCopyFormat: '设置复制格式' }, en: { enableCopy: '🔓Enable Copy Restrictions and Auto Copy', disableCopy: '🔒Disable Copy Restrictions and Auto Copy', copyTextAlert: 'Selected text copied to clipboard', copyHTMLAlert: 'Selected HTML copied to clipboard', copyFailureAlert: 'Copy failed', copyExceptionAlert: 'Exception during copy: ', invalidCopyFormatAlert: 'Invalid copy format, retaining current settings.', copyFormatPrompt: 'Please select copy format (text: Plain text, html: HTML, link: Link and text):', toggleShowButton: 'Toggle Show/Hide Copy Restrictions Button', setCopyFormat: 'Set Copy Format' }, ja: { enableCopy: '🔓コピー制限を解除し、自動コピーを有効化', disableCopy: '🔒コピー制限と自動コピーを無効にする', copyTextAlert: '選択したテキストがクリップボードにコピーされました', copyHTMLAlert: '選択したHTMLがクリップボードにコピーされました', copyFailureAlert: 'コピー失敗', copyExceptionAlert: 'コピー中に例外が発生しました: ', invalidCopyFormatAlert: '無効なコピー形式です。現在の設定を保持します。', copyFormatPrompt: 'コピー形式を選択してください(text: プレーンテキスト, html: HTML, link: リンクとテキスト):', toggleShowButton: 'コピー制限ボタンの表示/非表示切替', setCopyFormat: 'コピー形式を設定' }, es: { enableCopy: '🔓Habilitar restricciones de copia y auto-copia', disableCopy: '🔒Deshabilitar restricciones de copia y auto-copia', copyTextAlert: 'Texto seleccionado copiado al portapapeles', copyHTMLAlert: 'HTML seleccionado copiado al portapapeles', copyFailureAlert: 'Error al copiar', copyExceptionAlert: 'Excepción durante la copia: ', invalidCopyFormatAlert: 'Formato de copia no válido, se mantienen los ajustes actuales.', copyFormatPrompt: 'Seleccione el formato de copia (text: Texto plano, html: HTML, link: Enlace y texto):', toggleShowButton: 'Alternar mostrar/ocultar botón de restricciones de copia', setCopyFormat: 'Establecer formato de copia' } }; const t = key => (messages[language] && messages[language][key]) || messages.en[key]; console.log(`当前语言: ${language}, 显示文本:`, t('enableCopy')); /*** 针对特定网站(doc88.com)的处理 ***/ let doc88Path = ""; const websiteRuleDoc88 = { regexp: /.*doc88\.com\/.+/, init: () => { const style = document.createElement('style'); style.id = "copy-element-hide"; style.textContent = "#left-menu{display: none !important;}"; document.head.appendChild(style); GM_xmlhttpRequest({ method: "GET", url: "https://res3.doc88.com/resources/js/modules/main-v2.min.js?v=2.56", onload: response => { const result = /\("#cp_textarea"\).val\(([\S]*?)\);/.exec(response.responseText); if (result) { doc88Path = result[1]; } } }); window.addEventListener("load", () => { const cpFn = unsafeWindow.copyText?.toString(); const fnResult = cpFn && /'\+([\S]*?)\+"<\/textarea>/.exec(cpFn); if (fnResult) { doc88Path = fnResult[1]; } }); }, getSelectedText: () => { let select = unsafeWindow; doc88Path.split(".").forEach(v => { select = select?.[v]; }); if (!select) { unsafeWindow.Config.vip = 1; unsafeWindow.Config.logined = 1; document.getElementById("copy-element-hide")?.remove(); } return select; } }; if (websiteRuleDoc88.regexp.test(window.location.href)) { websiteRuleDoc88.init(); } /*** 读取存储的设置 ***/ const copyState = { enabled: GM_getValue('enabled', false), showButton: GM_getValue('showButton', true), copyFormat: GM_getValue('copyFormat', 'text'), showAlert: GM_getValue('showAlert', true) }; /******** 全局变量 ********/ let button = null; // 提前声明按钮变量 /******** 定义切换复制状态的函数 ********/ function toggleCopyState() { if (copyState.enabled) { disableCopy(); if (button) button.innerHTML = t('enableCopy'); } else { enableCopy(); if (button) button.innerHTML = t('disableCopy'); } copyState.enabled = !copyState.enabled; GM_setValue('enabled', copyState.enabled); } /******** 定义其他函数 ********/ const stopPropagation = e => e.stopPropagation(); const autoCopyHandler = () => { let selectedText; if (websiteRuleDoc88.regexp.test(window.location.href)) { selectedText = websiteRuleDoc88.getSelectedText(); } else { selectedText = window.getSelection().toString().trim(); } if (selectedText) { switch (copyState.copyFormat) { case 'text': copyTextToClipboard(selectedText); break; case 'html': copyHTMLToClipboard(selectedText); break; case 'link': copyTextToClipboard(`${selectedText}\n${window.location.href}`); break; default: copyTextToClipboard(selectedText); break; } } }; const copyTextToClipboard = textContent => { const tempTextarea = document.createElement('textarea'); Object.assign(tempTextarea.style, { position: 'fixed', top: '0', left: '0', width: '2em', height: '2em', padding: '0', border: 'none', outline: 'none', boxShadow: 'none', background: 'transparent' }); tempTextarea.value = textContent; document.body.appendChild(tempTextarea); tempTextarea.select(); try { const successful = document.execCommand('copy'); showAlert(successful ? t('copyTextAlert') : t('copyFailureAlert')); } catch (err) { showAlert(t('copyExceptionAlert') + err); } document.body.removeChild(tempTextarea); }; const copyHTMLToClipboard = htmlContent => { const tempDiv = document.createElement('div'); Object.assign(tempDiv.style, { position: 'fixed', top: '0', left: '0', width: '2em', height: '2em', padding: '0', border: 'none', outline: 'none', boxShadow: 'none', background: 'transparent' }); tempDiv.innerHTML = htmlContent; document.body.appendChild(tempDiv); const range = document.createRange(); range.selectNodeContents(tempDiv); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); try { const successful = document.execCommand('copy'); showAlert(successful ? t('copyHTMLAlert') : t('copyFailureAlert')); } catch (err) { showAlert(t('copyExceptionAlert') + err); } document.body.removeChild(tempDiv); }; const showAlert = message => { if (!copyState.showAlert) return; const alertBox = document.createElement('div'); alertBox.innerText = message; Object.assign(alertBox.style, { position: 'fixed', bottom: '70px', right: '20px', backgroundColor: 'rgba(0, 0, 0, 0.7)', color: '#fff', padding: '10px 15px', borderRadius: '10px', boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', fontFamily: '微软雅黑, Arial, sans-serif', fontSize: '14px', zIndex: '9999' }); document.body.appendChild(alertBox); setTimeout(() => alertBox.remove(), 3000); }; const enableCopy = () => { ['copy', 'cut', 'contextmenu', 'selectstart', 'mousedown', 'mouseup', 'keydown', 'keyup', 'keypress', 'oncopy', 'oncut', 'onpaste'] .forEach(event => document.addEventListener(event, stopPropagation, true)); const style = document.createElement('style'); style.type = 'text/css'; style.textContent = ` * { -webkit-user-select: auto !important; -moz-user-select: auto !important; -ms-user-select: auto !important; user-select: auto !important; pointer-events: auto !important; -webkit-touch-callout: default !important; } `; document.head.appendChild(style); if (document.body) { document.body.oncontextmenu = null; } const frames = [ ...document.getElementsByTagName('iframe'), ...document.getElementsByTagName('object'), ...document.getElementsByTagName('embed') ]; frames.forEach(frame => { try { const frameDoc = frame.contentWindow.document; ['copy', 'cut', 'contextmenu', 'selectstart', 'mousedown', 'mouseup', 'keydown', 'keyup', 'keypress'] .forEach(event => frameDoc.addEventListener(event, stopPropagation, true)); } catch (e) { console.error('无法访问框架内容:', e); } }); document.addEventListener('mouseup', autoCopyHandler, true); }; const disableCopy = () => { ['copy', 'cut', 'contextmenu', 'selectstart', 'mousedown', 'mouseup', 'keydown', 'keyup', 'keypress', 'oncopy', 'oncut', 'onpaste'] .forEach(event => document.removeEventListener(event, stopPropagation, true)); document.querySelectorAll('style').forEach(style => { if (style.textContent.includes('-webkit-user-select: auto !important')) { style.remove(); } }); document.removeEventListener('mouseup', autoCopyHandler, true); }; function createButton() { const btn = document.createElement('button'); btn.innerHTML = t('enableCopy'); Object.assign(btn.style, { position: 'fixed', bottom: '20px', right: '20px', zIndex: '9999', padding: '10px 15px', backgroundColor: 'rgba(173, 216, 230, 0.9)', color: '#000', border: 'none', borderRadius: '10px', cursor: 'pointer', fontFamily: '微软雅黑, Arial, sans-serif', fontSize: '14px', boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)', transition: 'background-color 0.3s, transform 0.3s' }); btn.addEventListener('mouseover', () => { btn.style.backgroundColor = 'rgba(135, 206, 235, 0.9)'; btn.style.transform = 'scale(1.05)'; }); btn.addEventListener('mouseout', () => { btn.style.backgroundColor = 'rgba(173, 216, 230, 0.9)'; btn.style.transform = 'scale(1)'; }); btn.addEventListener('click', toggleCopyState); return btn; } /******** 菜单命令注册 ********/ // 切换显示/隐藏按钮 GM_registerMenuCommand(t('toggleShowButton'), () => { copyState.showButton = !copyState.showButton; GM_setValue('showButton', copyState.showButton); if (copyState.showButton) { // 如果按钮不存在则创建,否则将按钮添加到 DOM 中 if (!button) { button = createButton(); copyState.button = button; } else if (!document.contains(button)) { document.body.appendChild(button); } } else { button && button.parentNode && button.parentNode.removeChild(button); } }); // 设置复制格式 GM_registerMenuCommand(t('setCopyFormat'), () => { const options = ['text', 'html', 'link']; const copyFormat = prompt(t('copyFormatPrompt'), copyState.copyFormat); if (options.includes(copyFormat)) { copyState.copyFormat = copyFormat; GM_setValue('copyFormat', copyState.copyFormat); } else { alert(t('invalidCopyFormatAlert')); } }); /******** 脚本初始化 ********/ // 根据存储的设置决定是否创建按钮 if (copyState.showButton) { button = createButton(); copyState.button = button; document.body.appendChild(button); } // 初始状态:禁用自动复制功能 disableCopy(); })();