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