Easy Copy Selected Text - Enhanced

Automatically copies selected text to clipboard with additional features

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Easy Copy Selected Text - Enhanced
// @namespace    http://tampermonkey.net/
// @version      0.8
// @description  Automatically copies selected text to clipboard with additional features
// @author       diogoodev
// @match        *://*/*
// @grant        GM_setClipboard
// @grant        GM_addStyle
// @run-at       document-start
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const config = {
        enabledByDefault: true,
        copyDelay: 10,             
        selectionDelay: 500,       
        notificationDuration: 2000, 
        toggleKey: 'Alt',          
        middleClickPaste: true     
    };

    let state = {
        enabled: config.enabledByDefault,
        oldSelectedText: "",
        copyInProgress: false,
        clipboard: ""
    };

    function addStyles() {
        const styles = `
            #easy-copy-snackbar {
                visibility: hidden;
                min-width: 250px;
                margin-left: -125px;
                text-align: center;
                border-radius: 8px;
                padding: 12px;
                position: fixed;
                z-index: 999999;
                left: 50%;
                top: 30px;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                font-size: 14px;
                box-shadow: 0 4px 8px rgba(0,0,0,0.2);
                opacity: 0;
                transition: opacity 0.3s, top 0.3s;
            }

            #easy-copy-snackbar.success {
                background-color: #4CAF50;
                color: white;
            }

            #easy-copy-snackbar.error {
                background-color: #F44336;
                color: white;
            }

            #easy-copy-snackbar.show {
                visibility: visible;
                opacity: 0.9;
                top: 30px;
            }

            .easy-copy-textarea {
                position: fixed;
                top: 0;
                left: 0;
                width: 2em;
                height: 2em;
                padding: 0;
                border: none;
                outline: none;
                box-shadow: none;
                background: transparent;
                opacity: 0;
            }
        `;

        if (typeof GM_addStyle !== "undefined") {
            GM_addStyle(styles);
        } else {
            try {
                const styleElement = document.createElement("style");
                styleElement.textContent = styles;
                if (document.head || document.documentElement) {
                    (document.head || document.documentElement).appendChild(styleElement);
                } else {
                    document.addEventListener('DOMContentLoaded', function() {
                        (document.head || document.documentElement).appendChild(styleElement);
                    });
                }
            } catch (e) {
            }
        }
    }

    function initializeUI() {
        addStyles();

        if (!document.getElementById("easy-copy-snackbar")) {
            const divSnackbar = document.createElement("div");
            divSnackbar.id = "easy-copy-snackbar";
            (document.body || document.documentElement).appendChild(divSnackbar);
        }
    }

    function getSelectionText() {
        let text = "";

        const activeEl = document.activeElement;
        const activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null;

        if (
            activeElTagName === "textarea" ||
            (activeElTagName === "input" && /^(?:text|search|password|tel|url)$/i.test(activeEl.type)) &&
            (typeof activeEl.selectionStart === "number")
        ) {
            text = activeEl.value.slice(activeEl.selectionStart, activeEl.selectionEnd);
        }
        else if (window.getSelection) {
            text = window.getSelection().toString();
        }

        text = text.trim();

        if (text === state.oldSelectedText || text === "") {
            return "";
        }

        state.oldSelectedText = text;
        return text;
    }

    function showSnackbar(message, isSuccess = true) {
        const snackbar = document.getElementById("easy-copy-snackbar");
        if (!snackbar) return;

        snackbar.textContent = message;
        snackbar.className = isSuccess ? "show success" : "show error";

        setTimeout(() => {
            snackbar.className = snackbar.className.replace("show", "");
        }, config.notificationDuration);
    }

    function copyTextToClipboard(text) {
        if (state.copyInProgress || !text) return;

        state.copyInProgress = true;

        try {
            if (typeof GM_setClipboard !== "undefined") {
                GM_setClipboard(text, "text");
                state.clipboard = text;
                showSnackbar("Text copied");
                state.copyInProgress = false;
                return;
            }

            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(text)
                    .then(() => {
                        state.clipboard = text;
                        showSnackbar("Text copied");
                    })
                    .catch(err => {
                        fallbackCopyMethod(text);
                    })
                    .finally(() => {
                        state.copyInProgress = false;
                    });
                return;
            }

            fallbackCopyMethod(text);

        } catch (err) {
            state.copyInProgress = false;
        }
    }

    function fallbackCopyMethod(text) {
        const textArea = document.createElement("textarea");
        textArea.value = text;

        textArea.setAttribute("readonly", "");
        textArea.setAttribute("class", "easy-copy-textarea");

        document.body.appendChild(textArea);
        textArea.focus();
        textArea.select();

        try {
            const successful = document.execCommand('copy');
            if (successful) {
                state.clipboard = text;
                showSnackbar("Text copied");
            } else {
                showSnackbar("Failed to copy text", false);
            }
        } catch (err) {
            showSnackbar("Failed to copy text", false);
        }

        document.body.removeChild(textArea);
        state.copyInProgress = false;
    }

    function pasteText() {
        if (!state.clipboard) return;

        const activeEl = document.activeElement;
        const activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null;

        if (
            activeElTagName === "textarea" ||
            (activeElTagName === "input" && /^(?:text|search|password|tel|url)$/i.test(activeEl.type))
        ) {
            const start = activeEl.selectionStart || 0;
            const end = activeEl.selectionEnd || 0;
            const textBefore = activeEl.value.substring(0, start);
            const textAfter = activeEl.value.substring(end);

            activeEl.value = textBefore + state.clipboard + textAfter;
            activeEl.selectionStart = activeEl.selectionEnd = start + state.clipboard.length;

            const event = new Event('input', { bubbles: true });
            activeEl.dispatchEvent(event);

            showSnackbar("Text pasted");
        } else if (document.execCommand) {
            try {
                document.execCommand('insertText', false, state.clipboard);
                showSnackbar("Text pasted");
            } catch (err) {
                showSnackbar("Failed to paste text", false);
            }
        }
    }

    function toggleScriptState() {
        state.enabled = !state.enabled;
        showSnackbar(state.enabled ? "Auto copy enabled" : "Auto copy disabled", state.enabled);
    }

    function initializeEvents() {
        document.addEventListener('keydown', function(e) {
            if (e.key === config.toggleKey) {
                toggleScriptState();
            }
        });

        document.addEventListener('mouseup', function(e) {
            if (!state.enabled) return;

            if (e.button === 1 && config.middleClickPaste) {
                e.preventDefault();
                pasteText();
                return;
            }

            setTimeout(() => {
                const textSelected = getSelectionText();
                if (textSelected) {
                    copyTextToClipboard(textSelected);
                }
            }, config.copyDelay);
        });

        document.addEventListener('selectionchange', function() {
            if (!state.enabled) return;

            clearTimeout(this.selectionTimer);
            this.selectionTimer = setTimeout(() => {
                if (!state.copyInProgress) {
                    const textSelected = getSelectionText();
                    if (textSelected) {
                        copyTextToClipboard(textSelected);
                    }
                }
            }, config.selectionDelay);
        });
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", function() {
            initializeUI();
            initializeEvents();
        });
    } else {
        initializeUI();
        initializeEvents();
    }
})();