Linux.do Base64工具

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

目前為 2025-03-31 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Linux.do Base64工具
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Base64编解码工具,支持位置记忆、夜间模式和拖动功能
  6. // @author Xavier
  7. // @match https://linux.do/*
  8. // @grant GM_setClipboard
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // 创建主容器
  17. const container = document.createElement('div');
  18. container.id = 'b64Container';
  19. Object.assign(container.style, {
  20. position: 'fixed',
  21. zIndex: 9999,
  22. borderRadius: '8px',
  23. fontFamily: 'inherit',
  24. cursor: 'move'
  25. });
  26.  
  27. // 初始化位置
  28. const savedPosition = GM_getValue('b64Position', null);
  29. if (savedPosition) {
  30. // 应用保存的位置时清除默认定位
  31. container.style.left = `${savedPosition.x}px`;
  32. container.style.top = `${savedPosition.y}px`;
  33. container.style.bottom = 'auto';
  34. container.style.right = 'auto';
  35. } else {
  36. // 默认位置时清除可能存在的绝对定位
  37. container.style.bottom = '20px';
  38. container.style.right = '20px';
  39. container.style.left = 'auto';
  40. container.style.top = 'auto';
  41. }
  42.  
  43. // 主触发器
  44. const trigger = document.createElement('div');
  45. trigger.innerHTML = 'Base64';
  46. Object.assign(trigger.style, {
  47. padding: '8px 16px',
  48. borderRadius: '6px',
  49. fontSize: '14px',
  50. cursor: 'pointer',
  51. transition: 'all 0.2s',
  52. backgroundColor: 'var(--primary)',
  53. color: 'var(--secondary)',
  54. boxShadow: '0 2px 5px rgba(0,0,0,0.2)'
  55. });
  56.  
  57. // 拖动功能
  58. let isDragging = false;
  59. let offsetX = 0;
  60. let offsetY = 0;
  61.  
  62. container.addEventListener('mousedown', (e) => {
  63. isDragging = true;
  64. const rect = container.getBoundingClientRect();
  65. offsetX = e.clientX - rect.left;
  66. offsetY = e.clientY - rect.top;
  67. });
  68.  
  69. document.addEventListener('mousemove', (e) => {
  70. if (isDragging) {
  71. const x = e.clientX - offsetX;
  72. const y = e.clientY - offsetY;
  73. container.style.left = `${x}px`;
  74. container.style.top = `${y}px`;
  75. container.style.right = 'auto';
  76. container.style.bottom = 'auto';
  77. }
  78. });
  79.  
  80. document.addEventListener('mouseup', () => {
  81. if (isDragging) {
  82. const rect = container.getBoundingClientRect();
  83. GM_setValue('b64Position', {
  84. x: rect.left,
  85. y: rect.top
  86. });
  87. }
  88. isDragging = false;
  89. });
  90.  
  91. // 下拉菜单容器
  92. const menu = document.createElement('div');
  93. menu.style.cssText = `
  94. position: absolute;
  95. bottom: 100%;
  96. right: 0;
  97. width: 160px;
  98. background: var(--secondary);
  99. border-radius: 6px;
  100. box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  101. margin-bottom: 8px;
  102. opacity: 0;
  103. transform: translateY(10px);
  104. transition: all 0.25s ease;
  105. pointer-events: none;
  106. `;
  107.  
  108. // 创建菜单项
  109. const createMenuItem = (text) => {
  110. const item = document.createElement('div');
  111. item.textContent = text;
  112. item.style.cssText = `
  113. padding: 10px 16px;
  114. font-size: 13px;
  115. cursor: pointer;
  116. color: var(--primary);
  117. transition: background 0.2s;
  118. text-align: left;
  119. line-height: 1.4;
  120. `;
  121. item.onmouseenter = () => item.style.background = 'rgba(0,0,0,0.08)';
  122. item.onmouseleave = () => item.style.background = '';
  123. return item;
  124. };
  125.  
  126. const decodeItem = createMenuItem('解析本页base64文本');
  127. const encodeItem = createMenuItem('文本转base64');
  128.  
  129. menu.append(decodeItem, encodeItem);
  130. container.append(trigger, menu);
  131.  
  132. // 菜单切换逻辑
  133. const toggleMenu = (show) => {
  134. menu.style.opacity = show ? 1 : 0;
  135. menu.style.transform = show ? 'translateY(0)' : 'translateY(10px)';
  136. menu.style.pointerEvents = show ? 'all' : 'none';
  137. };
  138.  
  139. // 主题适配
  140. const updateTheme = () => {
  141. const isDark = document.body.getAttribute('data-theme') === 'dark';
  142. trigger.style.backgroundColor = isDark ? 'var(--primary)' : 'var(--secondary)';
  143. trigger.style.color = isDark ? 'var(--secondary)' : 'var(--primary)';
  144. };
  145.  
  146. // 解码功能
  147. const decodeBase64 = () => {
  148. const base64Regex = /([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?/g;
  149.  
  150. document.querySelectorAll('div.post, div.cooked').forEach(container => {
  151. const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT);
  152.  
  153. while (walker.nextNode()) {
  154. const node = walker.currentNode;
  155. if (node.textContent.length > 20) {
  156. const decodedContent = node.textContent.replace(base64Regex, match => {
  157. try {
  158. const decoded = decodeURIComponent(escape(atob(match)));
  159. return decoded !== match ?
  160. `<span class="b64-decoded" style="
  161. color: var(--highlight);
  162. cursor: pointer;
  163. transition: all 0.2s;
  164. border-bottom: 1px dotted currentColor;
  165. ">${decoded}</span>` :
  166. match;
  167. } catch {
  168. return match;
  169. }
  170. });
  171.  
  172. if (decodedContent !== node.textContent) {
  173. const wrapper = document.createElement('span');
  174. wrapper.innerHTML = decodedContent;
  175.  
  176. wrapper.querySelectorAll('.b64-decoded').forEach(span => {
  177. span.onclick = (e) => {
  178. GM_setClipboard(span.textContent);
  179. e.target.style.opacity = '0.5';
  180. setTimeout(() => e.target.style.opacity = '', 500);
  181. };
  182. });
  183.  
  184. node.replaceWith(wrapper);
  185. }
  186. }
  187. }
  188. });
  189. };
  190.  
  191. // 编码功能
  192. const encodeBase64 = () => {
  193. const text = prompt('请输入要编码的文本:');
  194. if (text) {
  195. const encoded = btoa(unescape(encodeURIComponent(text)));
  196. GM_setClipboard(encoded);
  197. alert('已复制到剪贴板: ' + encoded);
  198. }
  199. };
  200.  
  201. // 事件绑定
  202. trigger.addEventListener('click', (e) => {
  203. e.stopPropagation();
  204. toggleMenu(menu.style.opacity === '0');
  205. });
  206.  
  207. decodeItem.addEventListener('click', () => {
  208. decodeBase64();
  209. toggleMenu(false);
  210. });
  211.  
  212. encodeItem.addEventListener('click', () => {
  213. encodeBase64();
  214. toggleMenu(false);
  215. });
  216.  
  217. // 初始化
  218. document.body.appendChild(container);
  219. updateTheme();
  220.  
  221. // 主题变化监听
  222. new MutationObserver(updateTheme).observe(document.body, {
  223. attributes: true,
  224. attributeFilter: ['data-theme']
  225. });
  226.  
  227. // 点击外部关闭
  228. document.addEventListener('click', (e) => {
  229. if (!container.contains(e.target)) toggleMenu(false);
  230. });
  231.  
  232. // 全局样式
  233. const style = document.createElement('style');
  234. style.textContent = `
  235. .b64-decoded:hover {
  236. opacity: 0.8 !important;
  237. }
  238. `;
  239. document.head.appendChild(style);
  240. })();

QingJ © 2025

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