Linux.do Base64 script

Base64编解码工具,支持位置记忆、夜间模式和拖动功能

目前為 2025-04-01 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Linux.do Base64 script
// @namespace    http://tampermonkey.net/
// @version      1.5.1
// @description  Base64编解码工具,支持位置记忆、夜间模式和拖动功能
// @author       Xavier
// @match        https://linux.do/*
// @grant        GM_setClipboard
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==


(function() {
    'use strict';

    // 创建主容器
    const container = document.createElement('div');
    container.id = 'b64Container';
    Object.assign(container.style, {
        position: 'fixed',
        zIndex: 9999,
        borderRadius: '8px',
        fontFamily: 'inherit',
        cursor: 'move'
    });

    // 初始化位置
    const savedPosition = GM_getValue('b64Position', null);
    if (savedPosition) {
        container.style.left = `${savedPosition.x}px`;
        container.style.top = `${savedPosition.y}px`;
        container.style.bottom = 'auto';
        container.style.right = 'auto';
    } else {
        container.style.bottom = '20px';
        container.style.right = '20px';
        container.style.left = 'auto';
        container.style.top = 'auto';
    }

    // 主触发器
    const trigger = document.createElement('div');
    trigger.innerHTML = 'Base64';
    Object.assign(trigger.style, {
        padding: '8px 16px',
        borderRadius: '6px',
        fontSize: '14px',
        cursor: 'pointer',
        transition: 'all 0.2s',
        backgroundColor: 'var(--primary)',
        color: 'var(--secondary)',
        boxShadow: '0 2px 5px rgba(0,0,0,0.2)',
        opacity: '0.5', // 设置默认透明度
    });

    // 添加鼠标悬停效果
    trigger.onmouseover = () => {
        trigger.style.opacity = '1'; // 悬停时变为不透明
    };

    trigger.onmouseout = () => {
        trigger.style.opacity = '0.5'; // 鼠标移出后恢复透明度
    };

    // 拖动功能
    let isDragging = false;
    let offsetX = 0;
    let offsetY = 0;

    container.addEventListener('mousedown', (e) => {
        isDragging = true;
        const rect = container.getBoundingClientRect();
        offsetX = e.clientX - rect.left;
        offsetY = e.clientY - rect.top;
    });

    document.addEventListener('mousemove', (e) => {
        if (isDragging) {
            const x = e.clientX - offsetX;
            const y = e.clientY - offsetY;
            container.style.left = `${x}px`;
            container.style.top = `${y}px`;
            container.style.right = 'auto';
            container.style.bottom = 'auto';
        }
    });

    document.addEventListener('mouseup', () => {
        if (isDragging) {
            const rect = container.getBoundingClientRect();
            GM_setValue('b64Position', {
                x: rect.left,
                y: rect.top
            });
        }
        isDragging = false;
    });

    // 下拉菜单容器
    const menu = document.createElement('div');
    menu.style.cssText = `
        position: absolute;
        bottom: 100%;
        right: 0;
        width: 140px;
        background: var(--secondary);
        border-radius: 6px;
        box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        margin-bottom: 8px;
        opacity: 0;
        transform: translateY(10px);
        transition: all 0.25s ease;
        pointer-events: none;
        overflow: hidden;
    `;

    // 创建菜单项
    const createMenuItem = (text) => {
        const item = document.createElement('div');
        item.textContent = text;
        item.style.cssText = `
            padding: 10px 16px;
            font-size: 13px;
            cursor: pointer;
            color: var(--primary);
            transition: all 0.2s;
            text-align: left;
            line-height: 1.4;
        `;

        // 根据按钮文本设置不同圆角
        if (text === '解析本页 Base64') {
            item.style.borderRadius = '6px 6px 0 0'; // 上边圆角
        } else if (text === '文本转 Base64') {
            item.style.borderRadius = '0 0 6px 6px'; // 下边圆角
        }

        item.onmouseenter = () => {
            item.style.background = 'rgba(0,0,0,0.08)';
        };

        item.onmouseleave = () => {
            item.style.background = '';
        };

        return item;
    };

    const decodeItem = createMenuItem('解析本页 Base64');
    const encodeItem = createMenuItem('文本转 Base64');

    menu.append(decodeItem, encodeItem);
    container.append(trigger, menu);

    // 菜单切换逻辑
    const toggleMenu = (show) => {
        menu.style.opacity = show ? 1 : 0;
        menu.style.transform = show ? 'translateY(0)' : 'translateY(10px)';
        menu.style.pointerEvents = show ? 'all' : 'none';
    };

    // 主题适配
    const updateTheme = () => {
        const isDark = document.body.getAttribute('data-theme') === 'dark';
        trigger.style.backgroundColor = isDark ? 'var(--primary)' : 'var(--secondary)';
        trigger.style.color = isDark ? 'var(--secondary)' : 'var(--primary)';
        trigger.style.opacity = '0.5'; // 确保透明度在主题切换时更新
    };

    // 统一提示函数
    function showNotification(message, isSuccess) {
        const notification = document.createElement('div');
        notification.className = 'b64-copy-notification';
        notification.textContent = message;
        notification.style.background = isSuccess ? 'rgba(0, 200, 0, 0.9)' : 'rgba(200, 0, 0, 0.9)';
        document.body.appendChild(notification);

        setTimeout(() => {
            notification.remove();
        }, 2500);
    }

    // 复制提示样式
    const notificationStyle = document.createElement('style');
    notificationStyle.textContent = `
        .b64-copy-notification {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 200, 0, 0.9);
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            font-size: 14px;
            z-index: 10000;
            animation: fadeOut 2s forwards;
            animation-delay: 0.5s;
        }

        @keyframes fadeOut {
            from { opacity: 1; }
            to { opacity: 0; }
        }
    `;
    document.head.appendChild(notificationStyle);

    // 解码功能
    let isParsed = false;
    let originalTexts = {};

    const decodeBase64 = () => {
        const base64Regex = /([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?/g;

        if (isParsed) {
            Object.entries(originalTexts).forEach(([wrapperId, text]) => {
                const wrapper = document.getElementById(wrapperId);
                if (wrapper) {
                    const textNode = document.createTextNode(text);
                    wrapper.replaceWith(textNode);
                }
            });
            originalTexts = {};
            isParsed = false;
            showNotification('已恢复原始内容', true);
            return;
        }

        let decodeSuccess = false;

        document.querySelectorAll('div.post, div.cooked').forEach(container => {
            const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);

            while (walker.nextNode()) {
                const node = walker.currentNode;
                if (node.textContent.length > 20) {
                    const decodedContent = node.textContent.replace(base64Regex, match => {
                        try {
                            const decoded = decodeURIComponent(escape(atob(match)));
                            if (decoded !== match) {
                                decodeSuccess = true;
                                return `<span class="b64-decoded" style="color: #FF7F28; background: rgba(255, 127, 40, 0.1); cursor: pointer; transition: all 0.2s; padding: 1px 2px; border-radius: 3px;">${decoded}</span>`;
                            }
                            return match;
                        } catch (e) {
                            return match;
                        }
                    });

                    if (decodedContent !== node.textContent) {
                        const wrapper = document.createElement('span');
                        const wrapperId = `b64-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
                        wrapper.id = wrapperId;
                        wrapper.innerHTML = decodedContent;

                        wrapper.querySelectorAll('.b64-decoded').forEach(span => {
                            span.onclick = (e) => {
                                GM_setClipboard(span.textContent);
                                showNotification('已复制到剪贴板', true);
                                e.target.style.opacity = '0.7';
                                setTimeout(() => e.target.style.opacity = '', 500);
                            };
                        });

                        originalTexts[wrapperId] = node.textContent;
                        node.replaceWith(wrapper);
                    }
                }
            }
        });

        if (decodeSuccess) {
            isParsed = true;
            showNotification('解析成功', true);
        } else {
            showNotification('解析失败,未发现有效Base64', false);
        }
    };

    // 编码功能
    const encodeBase64 = () => {
        const text = prompt('请输入要编码的文本:');
        if (text) {
            try {
                const encoded = btoa(unescape(encodeURIComponent(text)));
                GM_setClipboard(encoded);
                showNotification('编码成功已复制', true);
            } catch (e) {
                showNotification('编码失败:无效字符', false);
            }
        } else {
            showNotification('编码已取消', false);
        }
    };

    // 事件绑定
    trigger.addEventListener('click', (e) => {
        e.stopPropagation();
        toggleMenu(menu.style.opacity === '0');
    });

    decodeItem.addEventListener('click', () => {
        decodeBase64();
        toggleMenu(false);
    });

    encodeItem.addEventListener('click', () => {
        encodeBase64();
        toggleMenu(false);
    });

    // 初始化
    document.body.appendChild(container);
    updateTheme();

    // 主题变化监听
    new MutationObserver(updateTheme).observe(document.body, {
        attributes: true,
        attributeFilter: ['data-theme']
    });

    // 点击外部关闭
    document.addEventListener('click', (e) => {
        if (!container.contains(e.target)) toggleMenu(false);
    });

    // 监听前进和后退事件,重置解析状态
    function resetParseState() {
        isParsed = false;
        originalTexts = {};
    }
    window.addEventListener('popstate', resetParseState);

    // 全局样式
    const style = document.createElement('style');
    style.textContent = `
        .b64-decoded:hover {
            background: rgba(255, 127, 40, 0.2) !important;
        }
        .b64-decoded:active {
            background: rgba(255, 127, 40, 0.3) !important;
        }
    `;
    document.head.appendChild(style);

    // 确保原有复制提示兼容性
    function showCopyNotification() {
        showNotification('已复制到剪贴板', true);
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址