Linux.do Base64工具

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

Устаревшая версия за 31.03.2025. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Linux.do Base64工具
// @namespace    http://tampermonkey.net/
// @version      1.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)'
    });

    // 拖动功能
    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: 160px;
        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;
    `;

    // 创建菜单项
    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: background 0.2s;
            text-align: left;
            line-height: 1.4;
        `;
        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)';
    };

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

        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)));
                            return decoded !== match ?
                                `<span class="b64-decoded" style="
                                    color: var(--highlight);
                                    cursor: pointer;
                                    transition: all 0.2s;
                                    border-bottom: 1px dotted currentColor;
                                ">${decoded}</span>` :
                                match;
                        } catch {
                            return match;
                        }
                    });

                    if (decodedContent !== node.textContent) {
                        const wrapper = document.createElement('span');
                        wrapper.innerHTML = decodedContent;

                        wrapper.querySelectorAll('.b64-decoded').forEach(span => {
                            span.onclick = (e) => {
                                GM_setClipboard(span.textContent);
                                e.target.style.opacity = '0.5';
                                setTimeout(() => e.target.style.opacity = '', 500);
                            };
                        });

                        node.replaceWith(wrapper);
                    }
                }
            }
        });
    };

    // 编码功能
    const encodeBase64 = () => {
        const text = prompt('请输入要编码的文本:');
        if (text) {
            const encoded = btoa(unescape(encodeURIComponent(text)));
            GM_setClipboard(encoded);
            alert('已复制到剪贴板: ' + encoded);
        }
    };

    // 事件绑定
    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);
    });

    // 全局样式
    const style = document.createElement('style');
    style.textContent = `
        .b64-decoded:hover {
            opacity: 0.8 !important;
        }
    `;
    document.head.appendChild(style);
})();