// ==UserScript== // @name 解除复制限制 // @name:zh 解除复制限制 // @name:en Unlock Copy Restrictions // @name:ja コピー制限解除 // @name:ko 복사 제한 해제 // @name:es Desbloquear restricciones de copia // @namespace gura8390/copy/2 // @version 1.4 // @license MIT // @icon https://img.icons8.com/nolan/64/password1.png // @description 解除网页复制限制并提供可视化控制 // @description:zh 解除网页复制限制并提供可视化控制 // @description:en Unlock web copy restrictions with visual control // @description:ja ウェブのコピー制限を解除し、視覚的な制御を提供 // @description:ko 웹 페이지의 복사 제한을 해제하고 시각적 제어를 제공합니다 // @description:es Desbloquea restricciones de copia en la web y proporciona control visual // @author lbihhe // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-end // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 本地化字典,支持中文、英文、日文、韩文、西班牙语 const locales = { en: { menu_toggle_script: "🔄 Toggle Script State", menu_toggle_button: "👁️ Toggle Button Display", btn_unlock: "🔓 Unlock Restrictions", btn_lock: "🔒 Restore Defaults", toast_unlocked: "✔️ Copy restrictions unlocked!", toast_locked: "✔️ Restrictions restored!" }, zh: { menu_toggle_script: "🔄 切换脚本状态", menu_toggle_button: "👁️ 切换按钮显示", btn_unlock: "🔓 解除限制", btn_lock: "🔒 恢复原状", toast_unlocked: "✔️ 复制限制已解除!", toast_locked: "✔️ 限制已恢复!" }, ja: { menu_toggle_script: "🔄 スクリプト状態を切り替え", menu_toggle_button: "👁️ ボタン表示を切り替え", btn_unlock: "🔓 制限解除", btn_lock: "🔒 デフォルトに戻す", toast_unlocked: "✔️ コピー制限が解除されました!", toast_locked: "✔️ 制限が復元されました!" }, ko: { menu_toggle_script: "🔄 스크립트 상태 전환", menu_toggle_button: "👁️ 버튼 표시 전환", btn_unlock: "🔓 제한 해제", btn_lock: "🔒 기본값 복원", toast_unlocked: "✔️ 복사 제한이 해제되었습니다!", toast_locked: "✔️ 제한이 복원되었습니다!" }, es: { menu_toggle_script: "🔄 Cambiar estado del script", menu_toggle_button: "👁️ Cambiar visualización del botón", btn_unlock: "🔓 Desbloquear restricciones", btn_lock: "🔒 Restaurar valores predeterminados", toast_unlocked: "✔️ ¡Restricciones de copia desbloqueadas!", toast_locked: "✔️ ¡Restricciones restauradas!" } }; // 根据浏览器语言决定使用哪种语言环境 const lang = navigator.language.toLowerCase(); let userLang = 'en'; // 默认使用英文 if (lang.startsWith('zh')) { userLang = 'zh'; } else if (lang.startsWith('ja')) { userLang = 'ja'; } else if (lang.startsWith('ko')) { userLang = 'ko'; } else if (lang.startsWith('es')) { userLang = 'es'; } const t = locales[userLang]; // 配置存储键名 const CONFIG = { ENABLED: 'copy_enabled', // true:解除限制,false:恢复原状 SHOW_BUTTON: 'show_button' }; // 全局变量,用于记录注入的样式元素 let unlockStyle = null; // 定义统一的事件处理函数,方便后续移除 const stopPropagation = function(e) { e.stopPropagation(); }; // 需要拦截的事件列表 const eventsList = ['contextmenu', 'copy', 'selectstart']; // 初始化配置:如果没有设置,则默认解除限制为开启、按钮显示为开启 const initConfig = () => { if (GM_getValue(CONFIG.ENABLED, null) === null) { GM_setValue(CONFIG.ENABLED, true); } if (GM_getValue(CONFIG.SHOW_BUTTON, null) === null) { GM_setValue(CONFIG.SHOW_BUTTON, true); } }; // 注册控制面板菜单(与按钮状态同步) const registerMenu = () => { GM_registerMenuCommand(t.menu_toggle_script, () => { const current = GM_getValue(CONFIG.ENABLED); GM_setValue(CONFIG.ENABLED, !current); location.reload(); }); GM_registerMenuCommand(t.menu_toggle_button, () => { GM_setValue(CONFIG.SHOW_BUTTON, !GM_getValue(CONFIG.SHOW_BUTTON)); location.reload(); }); }; // 解除限制:添加覆盖 CSS 和事件拦截 const unlockCopy = () => { // 注入覆盖样式 if (!unlockStyle) { unlockStyle = document.createElement('style'); unlockStyle.id = 'copy-unlocker-style'; unlockStyle.innerHTML = ` * { user-select: auto !important; -webkit-user-select: auto !important; -moz-user-select: auto !important; -ms-user-select: auto !important; } `; document.head.appendChild(unlockStyle); } // 添加事件监听,阻止其它脚本阻止复制等事件 eventsList.forEach(event => { document.body.addEventListener(event, stopPropagation, true); }); }; // 恢复原状:移除覆盖样式和解除事件监听 const restoreCopy = () => { // 移除覆盖样式 if (unlockStyle) { unlockStyle.remove(); unlockStyle = null; } // 移除事件监听 eventsList.forEach(event => { document.body.removeEventListener(event, stopPropagation, true); }); }; // 优化后的提示动画(toast) // 参数:msg 提示信息,bgColor 背景色 const showSuccessToast = (msg, bgColor = '#4CAF50') => { const toast = document.createElement('div'); toast.innerHTML = msg; toast.style.cssText = ` position: fixed; bottom: 80px; right: 20px; background: ${bgColor}; color: white; padding: 12px 24px; border-radius: 8px; z-index: 9999; opacity: 0; animation: fadeSlideIn 0.6s forwards, fadeOut 0.6s 2.5s forwards; box-shadow: 0 4px 12px rgba(0,0,0,0.15); `; // 添加动画样式(仅添加一次) if (!document.getElementById('toast-animations')) { const style = document.createElement('style'); style.id = 'toast-animations'; style.innerHTML = ` @keyframes fadeSlideIn { 0% { transform: translateY(100%); opacity: 0; } 60% { transform: translateY(-10px); opacity: 1; } 100% { transform: translateY(0); opacity: 1; } } @keyframes fadeOut { to { opacity: 0; } } `; document.head.appendChild(style); } document.body.appendChild(toast); // 提示框在动画结束后自动移除 setTimeout(() => { toast.remove(); }, 3200); }; // 创建浮动按钮,支持切换:解除限制 / 恢复原状 const createFloatButton = () => { const btn = document.createElement('button'); btn.id = 'copy-unlocker-btn'; updateButtonLabel(); // 调整按钮配色,采用渐变背景和柔和阴影 btn.style.cssText = ` position: fixed; bottom: 20px; right: 20px; z-index: 9999; padding: 12px 24px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; border: none; border-radius: 30px; cursor: pointer; box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2); transition: background 0.3s, transform 0.2s; font-size: 14px; `; // 鼠标悬停效果 btn.addEventListener('mouseenter', () => { btn.style.transform = 'scale(1.05)'; }); btn.addEventListener('mouseleave', () => { btn.style.transform = 'scale(1)'; }); // 点击事件:根据当前状态切换 btn.addEventListener('click', () => { const enabled = GM_getValue(CONFIG.ENABLED); if (enabled) { // 当前处于解除限制状态,执行恢复 restoreCopy(); GM_setValue(CONFIG.ENABLED, false); showSuccessToast(t.toast_locked, '#F44336'); } else { // 当前为限制状态,执行解除限制 unlockCopy(); GM_setValue(CONFIG.ENABLED, true); showSuccessToast(t.toast_unlocked, '#4CAF50'); } updateButtonLabel(); }); // 根据当前状态更新按钮显示文字 function updateButtonLabel(){ if (GM_getValue(CONFIG.ENABLED)) { btn.innerHTML = t.btn_lock; } else { btn.innerHTML = t.btn_unlock; } } return btn; }; // 主执行函数:根据配置决定是否解除限制,并添加按钮 const main = () => { initConfig(); registerMenu(); // 根据当前状态执行解除或恢复操作 if (GM_getValue(CONFIG.ENABLED)) { unlockCopy(); } else { restoreCopy(); } // 如果配置允许,则添加浮动按钮 if (GM_getValue(CONFIG.SHOW_BUTTON)) { document.body.appendChild(createFloatButton()); } }; // 启动脚本 main(); })();