Greasy Fork

Greasy Fork is available in English.

解除复制限制

解除网页复制限制并提供可视化控制

当前为 2025-02-01 提交的版本,查看 最新版本

// ==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
// ==/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();

})();