您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
强大的网页文本格式化工具,支持文本清理、格式转换、编码解码、排序去重等实用功能
// ==UserScript== // @name 网页文本格式化工具 // @namespace http://tampermonkey.net/ // @version 1.0 // @description 强大的网页文本格式化工具,支持文本清理、格式转换、编码解码、排序去重等实用功能 // @author shenfangda // @match *://*/* // @grant GM_addStyle // @grant GM_setClipboard // @grant GM_getResourceText // @license MIT // ==/UserScript== (function() { 'use strict'; // 配置 const config = { tools: [ { id: 'clean', name: '文本清理', icon: '🧹' }, { id: 'format', name: '格式转换', icon: '🔄' }, { id: 'encode', name: '编码解码', icon: '🔐' }, { id: 'sort', name: '排序去重', icon: '📊' }, { id: 'case', name: '大小写转换', icon: '📝' }, { id: 'replace', name: '批量替换', icon: '🔁' }, { id: 'count', name: '文本统计', icon: '📈' }, { id: 'extract', name: '信息提取', icon: '🔍' } ], // 文本处理规则 textRules: { clean: { spaces: { name: '多余空格', desc: '清理多余空格和制表符' }, lines: { name: '空行清理', desc: '删除多余空行' }, html: { name: 'HTML标签', desc: '移除HTML标签' }, special: { name: '特殊字符', desc: '清理不可见字符' }, punctuation: { name: '标点符号', desc: '统一标点符号格式' } }, format: { json: { name: 'JSON格式化', desc: '格式化JSON文本' }, xml: { name: 'XML格式化', desc: '格式化XML文本' }, sql: { name: 'SQL格式化', desc: '格式化SQL语句' }, csv: { name: 'CSV转换', desc: '转换CSV格式' }, markdown: { name: 'Markdown转换', desc: '转换为Markdown格式' } }, encode: { url: { name: 'URL编码/解码', desc: 'URL编码转换' }, base64: { name: 'Base64编码/解码', desc: 'Base64转换' }, html: { name: 'HTML实体编码', desc: 'HTML实体转换' }, unicode: { name: 'Unicode编码', desc: 'Unicode转换' }, md5: { name: 'MD5哈希', desc: '生成MD5哈希值' } } } }; // 主类 class TextFormatterToolkit { constructor() { this.currentTool = null; this.selectedText = ''; this.init(); } init() { console.log('网页文本格式化工具已启动'); this.createUI(); this.bindEvents(); this.addContextMenu(); } // 创建UI界面 createUI() { GM_addStyle(` #text-formatter-panel { position: fixed; top: 50px; right: 50px; width: 400px; max-height: 600px; background: #fff; border: 1px solid #ddd; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.12); z-index: 10000; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-size: 14px; display: none; overflow: hidden; } #text-formatter-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px 20px; cursor: move; display: flex; justify-content: space-between; align-items: center; font-weight: 600; } #text-formatter-title { display: flex; align-items: center; gap: 8px; } #text-formatter-close { background: rgba(255,255,255,0.2); border: none; color: white; font-size: 18px; cursor: pointer; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; transition: background 0.2s; } #text-formatter-close:hover { background: rgba(255,255,255,0.3); } #text-formatter-content { padding: 20px; max-height: 500px; overflow-y: auto; } .tool-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-bottom: 20px; } .tool-item { padding: 15px; background: #f8f9fa; border: 2px solid transparent; border-radius: 8px; cursor: pointer; transition: all 0.2s; text-align: center; position: relative; overflow: hidden; } .tool-item:hover { background: #e9ecef; border-color: #667eea; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15); } .tool-item.active { background: #667eea; color: white; border-color: #667eea; } .tool-icon { font-size: 28px; margin-bottom: 8px; display: block; } .tool-name { font-weight: 500; font-size: 13px; } .tool-panel { display: none; animation: fadeIn 0.3s ease-in-out; } .tool-panel.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .input-section, .output-section { margin-bottom: 15px; } .section-title { font-weight: 600; margin-bottom: 8px; color: #495057; display: flex; align-items: center; gap: 6px; } .text-area { width: 100%; min-height: 120px; padding: 12px; border: 1px solid #ced4da; border-radius: 6px; font-family: 'Consolas', 'Monaco', monospace; font-size: 13px; resize: vertical; transition: border-color 0.2s; } .text-area:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } .button-group { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 15px; } .btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; transition: all 0.2s; display: inline-flex; align-items: center; gap: 6px; } .btn-primary { background: #667eea; color: white; } .btn-primary:hover { background: #5a67d8; transform: translateY(-1px); } .btn-secondary { background: #6c757d; color: white; } .btn-secondary:hover { background: #5a6268; } .btn-success { background: #28a745; color: white; } .btn-success:hover { background: #218838; } .btn-warning { background: #ffc107; color: #212529; } .btn-warning:hover { background: #e0a800; } .checkbox-group { display: flex; flex-direction: column; gap: 8px; margin-bottom: 15px; } .checkbox-item { display: flex; align-items: center; gap: 8px; padding: 8px; background: #f8f9fa; border-radius: 6px; cursor: pointer; transition: background 0.2s; } .checkbox-item:hover { background: #e9ecef; } .checkbox-item input[type="checkbox"] { margin: 0; } .stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 15px; } .stat-item { background: #f8f9fa; padding: 10px; border-radius: 6px; text-align: center; } .stat-value { font-size: 20px; font-weight: bold; color: #667eea; } .stat-label { font-size: 12px; color: #6c757d; margin-top: 2px; } .floating-button { position: fixed; bottom: 30px; right: 30px; width: 60px; height: 60px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; border-radius: 50%; cursor: pointer; font-size: 24px; color: white; box-shadow: 0 4px 20px rgba(102, 126, 234, 0.3); transition: all 0.3s; z-index: 9999; display: flex; align-items: center; justify-content: center; } .floating-button:hover { transform: scale(1.1); box-shadow: 0 6px 25px rgba(102, 126, 234, 0.4); } .replace-pairs { display: flex; flex-direction: column; gap: 8px; margin-bottom: 15px; } .replace-pair { display: flex; gap: 8px; align-items: center; } .replace-input { flex: 1; padding: 8px; border: 1px solid #ced4da; border-radius: 4px; font-size: 13px; } .extract-results { background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 6px; padding: 12px; margin-top: 10px; max-height: 200px; overflow-y: auto; } .extract-item { padding: 6px 8px; margin: 4px 0; background: white; border-radius: 4px; border-left: 3px solid #667eea; font-family: monospace; font-size: 12px; } `); // 创建浮动按钮 const floatingBtn = document.createElement('button'); floatingBtn.className = 'floating-button'; floatingBtn.innerHTML = '📝'; floatingBtn.title = '文本格式化工具'; floatingBtn.onclick = () => this.togglePanel(); document.body.appendChild(floatingBtn); // 创建主面板 const panel = document.createElement('div'); panel.id = 'text-formatter-panel'; panel.innerHTML = ` <div id="text-formatter-header"> <div id="text-formatter-title"> <span>📝</span> <span>文本格式化工具</span> </div> <button id="text-formatter-close">×</button> </div> <div id="text-formatter-content"> <div class="tool-grid"> ${config.tools.map(tool => ` <div class="tool-item" data-tool="${tool.id}"> <span class="tool-icon">${tool.icon}</span> <span class="tool-name">${tool.name}</span> </div> `).join('')} </div> <div id="tool-panels"> ${this.createToolPanels()} </div> </div> `; document.body.appendChild(panel); this.makeDraggable(panel.querySelector('#text-formatter-header'), panel); } // 创建工具面板 createToolPanels() { return ` <div class="tool-panel" id="panel-clean"> <div class="input-section"> <div class="section-title">🧹 文本清理</div> <textarea class="text-area" id="clean-input" placeholder="输入需要清理的文本..."></textarea> </div> <div class="checkbox-group"> ${Object.entries(config.textRules.clean).map(([key, rule]) => ` <label class="checkbox-item"> <input type="checkbox" value="${key}" checked> <strong>${rule.name}</strong> - ${rule.desc} </label> `).join('')} </div> <div class="button-group"> <button class="btn btn-primary" onclick="textFormatterToolkit.processClean()">开始清理</button> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('clean')">清空</button> </div> <div class="output-section"> <div class="section-title">✨ 清理结果</div> <textarea class="text-area" id="clean-output" readonly placeholder="清理结果将显示在这里..."></textarea> </div> </div> <div class="tool-panel" id="panel-format"> <div class="input-section"> <div class="section-title">🔄 格式转换</div> <textarea class="text-area" id="format-input" placeholder="输入需要格式化的文本..."></textarea> </div> <div class="button-group"> ${Object.entries(config.textRules.format).map(([key, rule]) => ` <button class="btn btn-primary" onclick="textFormatterToolkit.processFormat('${key}')">${rule.name}</button> `).join('')} </div> <div class="button-group"> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('format')">清空</button> <button class="btn btn-success" onclick="textFormatterToolkit.copyOutput('format')">复制结果</button> </div> <div class="output-section"> <div class="section-title">📋 转换结果</div> <textarea class="text-area" id="format-output" readonly placeholder="转换结果将显示在这里..."></textarea> </div> </div> <div class="tool-panel" id="panel-encode"> <div class="input-section"> <div class="section-title">🔐 编码解码</div> <textarea class="text-area" id="encode-input" placeholder="输入需要编码/解码的文本..."></textarea> </div> <div class="button-group"> ${Object.entries(config.textRules.encode).map(([key, rule]) => ` <button class="btn btn-primary" onclick="textFormatterToolkit.processEncode('${key}', true)">${rule.name}</button> `).join('')} </div> <div class="button-group"> ${Object.entries(config.textRules.encode).filter(([key]) => key !== 'md5').map(([key, rule]) => ` <button class="btn btn-warning" onclick="textFormatterToolkit.processEncode('${key}', false)">${rule.name}解码</button> `).join('')} </div> <div class="button-group"> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('encode')">清空</button> <button class="btn btn-success" onclick="textFormatterToolkit.copyOutput('encode')">复制结果</button> </div> <div class="output-section"> <div class="section-title">🔑 处理结果</div> <textarea class="text-area" id="encode-output" readonly placeholder="处理结果将显示在这里..."></textarea> </div> </div> <div class="tool-panel" id="panel-sort"> <div class="input-section"> <div class="section-title">📊 排序去重</div> <textarea class="text-area" id="sort-input" placeholder="每行一个项目,输入需要处理的文本..."></textarea> </div> <div class="button-group"> <button class="btn btn-primary" onclick="textFormatterToolkit.processSort('asc')">升序排列</button> <button class="btn btn-primary" onclick="textFormatterToolkit.processSort('desc')">降序排列</button> <button class="btn btn-success" onclick="textFormatterToolkit.processSort('unique')">去重</button> <button class="btn btn-warning" onclick="textFormatterToolkit.processSort('shuffle')">随机排序</button> </div> <div class="button-group"> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('sort')">清空</button> <button class="btn btn-success" onclick="textFormatterToolkit.copyOutput('sort')">复制结果</button> </div> <div class="output-section"> <div class="section-title">📈 处理结果</div> <textarea class="text-area" id="sort-output" readonly placeholder="处理结果将显示在这里..."></textarea> </div> </div> <div class="tool-panel" id="panel-case"> <div class="input-section"> <div class="section-title">📝 大小写转换</div> <textarea class="text-area" id="case-input" placeholder="输入需要转换的文本..."></textarea> </div> <div class="button-group"> <button class="btn btn-primary" onclick="textFormatterToolkit.processCase('upper')">转换大写</button> <button class="btn btn-primary" onclick="textFormatterToolkit.processCase('lower')">转换小写</button> <button class="btn btn-success" onclick="textFormatterToolkit.processCase('title')">标题格式</button> <button class="btn btn-warning" onclick="textFormatterToolkit.processCase('camel')">驼峰命名</button> </div> <div class="button-group"> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('case')">清空</button> <button class="btn btn-success" onclick="textFormatterToolkit.copyOutput('case')">复制结果</button> </div> <div class="output-section"> <div class="section-title">✏️ 转换结果</div> <textarea class="text-area" id="case-output" readonly placeholder="转换结果将显示在这里..."></textarea> </div> </div> <div class="tool-panel" id="panel-replace"> <div class="input-section"> <div class="section-title">🔁 批量替换</div> <textarea class="text-area" id="replace-input" placeholder="输入需要替换的文本..."></textarea> </div> <div class="section-title">替换规则</div> <div class="replace-pairs" id="replace-pairs"> <div class="replace-pair"> <input type="text" class="replace-input" placeholder="查找内容" data-type="find"> <input type="text" class="replace-input" placeholder="替换为" data-type="replace"> <button class="btn btn-warning" onclick="textFormatterToolkit.removeReplacePair(this)">删除</button> </div> </div> <div class="button-group"> <button class="btn btn-primary" onclick="textFormatterToolkit.addReplacePair()">添加规则</button> <button class="btn btn-success" onclick="textFormatterToolkit.processReplace()">执行替换</button> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('replace')">清空</button> </div> <div class="output-section"> <div class="section-title">🔄 替换结果</div> <textarea class="text-area" id="replace-output" readonly placeholder="替换结果将显示在这里..."></textarea> </div> </div> <div class="tool-panel" id="panel-count"> <div class="input-section"> <div class="section-title">📈 文本统计</div> <textarea class="text-area" id="count-input" placeholder="输入需要统计的文本..."></textarea> </div> <div class="button-group"> <button class="btn btn-primary" onclick="textFormatterToolkit.processCount()">开始统计</button> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('count')">清空</button> </div> <div class="stats-grid" id="count-stats" style="display: none;"> <div class="stat-item"> <div class="stat-value" id="char-count">0</div> <div class="stat-label">字符数</div> </div> <div class="stat-item"> <div class="stat-value" id="word-count">0</div> <div class="stat-label">单词数</div> </div> <div class="stat-item"> <div class="stat-value" id="line-count">0</div> <div class="stat-label">行数</div> </div> <div class="stat-item"> <div class="stat-value" id="para-count">0</div> <div class="stat-label">段落数</div> </div> </div> </div> <div class="tool-panel" id="panel-extract"> <div class="input-section"> <div class="section-title">🔍 信息提取</div> <textarea class="text-area" id="extract-input" placeholder="输入需要提取信息的文本..."></textarea> </div> <div class="button-group"> <button class="btn btn-primary" onclick="textFormatterToolkit.processExtract('email')">提取邮箱</button> <button class="btn btn-primary" onclick="textFormatterToolkit.processExtract('url')">提取网址</button> <button class="btn btn-success" onclick="textFormatterToolkit.processExtract('phone')">提取电话</button> <button class="btn btn-warning" onclick="textFormatterToolkit.processExtract('ip')">提取IP</button> </div> <div class="button-group"> <button class="btn btn-secondary" onclick="textFormatterToolkit.clearInput('extract')">清空</button> <button class="btn btn-success" onclick="textFormatterToolkit.copyOutput('extract')">复制结果</button> </div> <div class="extract-results" id="extract-results" style="display: none;"> <div class="section-title">🎯 提取结果</div> <div id="extract-items"></div> </div> </div> `; } // 绑定事件 bindEvents() { // 工具选择 document.querySelectorAll('.tool-item').forEach(item => { item.addEventListener('click', () => { const toolId = item.dataset.tool; this.selectTool(toolId); }); }); // 关闭按钮 document.getElementById('text-formatter-close').addEventListener('click', () => { this.hidePanel(); }); // 点击外部关闭 document.addEventListener('click', (e) => { const panel = document.getElementById('text-formatter-panel'); const button = document.querySelector('.floating-button'); if (!panel.contains(e.target) && !button.contains(e.target)) { this.hidePanel(); } }); } // 添加右键菜单 addContextMenu() { document.addEventListener('contextmenu', (e) => { const selection = window.getSelection().toString().trim(); if (selection) { this.selectedText = selection; } }); // 监听文本选择 document.addEventListener('mouseup', () => { setTimeout(() => { const selection = window.getSelection().toString().trim(); if (selection) { this.selectedText = selection; } }, 100); }); } // 选择工具 selectTool(toolId) { // 更新工具选择状态 document.querySelectorAll('.tool-item').forEach(item => { item.classList.remove('active'); }); document.querySelector(`[data-tool="${toolId}"]`).classList.add('active'); // 显示对应面板 document.querySelectorAll('.tool-panel').forEach(panel => { panel.classList.remove('active'); }); document.getElementById(`panel-${toolId}`).classList.add('active'); this.currentTool = toolId; // 如果有选中文本,自动填充 if (this.selectedText && !document.getElementById(`${toolId}-input`).value) { document.getElementById(`${toolId}-input`).value = this.selectedText; } } // 文本清理 processClean() { const input = document.getElementById('clean-input').value; if (!input) return; let result = input; const checkboxes = document.querySelectorAll('#panel-clean input[type="checkbox"]:checked'); checkboxes.forEach(checkbox => { const rule = checkbox.value; switch (rule) { case 'spaces': result = result.replace(/\s+/g, ' ').trim(); break; case 'lines': result = result.replace(/\n\s*\n\s*\n/g, '\n\n'); break; case 'html': result = result.replace(/<[^>]*>/g, ''); break; case 'special': result = result.replace(/[\x00-\x1F\x7F-\x9F]/g, ''); break; case 'punctuation': result = result.replace(/[\""]/g, '"') .replace(/[\'']/g, "'") .replace(/[---]/g, '-') .replace(/\.\.\./g, '…'); break; } }); document.getElementById('clean-output').value = result; } // 格式转换 processFormat(type) { const input = document.getElementById('format-input').value; if (!input) return; let result = input; try { switch (type) { case 'json': result = JSON.stringify(JSON.parse(input), null, 2); break; case 'xml': result = this.formatXML(input); break; case 'sql': result = this.formatSQL(input); break; case 'csv': result = this.formatCSV(input); break; case 'markdown': result = this.formatMarkdown(input); break; } } catch (error) { result = `格式转换错误: ${error.message}`; } document.getElementById('format-output').value = result; } // 编码解码 processEncode(type, encode) { const input = document.getElementById('encode-input').value; if (!input) return; let result = input; try { switch (type) { case 'url': result = encode ? encodeURIComponent(input) : decodeURIComponent(input); break; case 'base64': result = encode ? btoa(input) : atob(input); break; case 'html': result = encode ? this.htmlEncode(input) : this.htmlDecode(input); break; case 'unicode': result = encode ? this.unicodeEncode(input) : this.unicodeDecode(input); break; case 'md5': result = this.md5(input); break; } } catch (error) { result = `编码解码错误: ${error.message}`; } document.getElementById('encode-output').value = result; } // 排序处理 processSort(type) { const input = document.getElementById('sort-input').value; if (!input) return; let lines = input.split('\n').filter(line => line.trim()); switch (type) { case 'asc': lines.sort((a, b) => a.localeCompare(b)); break; case 'desc': lines.sort((a, b) => b.localeCompare(a)); break; case 'unique': lines = [...new Set(lines)]; break; case 'shuffle': lines = this.shuffleArray(lines); break; } document.getElementById('sort-output').value = lines.join('\n'); } // 大小写转换 processCase(type) { const input = document.getElementById('case-input').value; if (!input) return; let result = input; switch (type) { case 'upper': result = input.toUpperCase(); break; case 'lower': result = input.toLowerCase(); break; case 'title': result = this.toTitleCase(input); break; case 'camel': result = this.toCamelCase(input); break; } document.getElementById('case-output').value = result; } // 批量替换 addReplacePair() { const container = document.getElementById('replace-pairs'); const pair = document.createElement('div'); pair.className = 'replace-pair'; pair.innerHTML = ` <input type="text" class="replace-input" placeholder="查找内容" data-type="find"> <input type="text" class="replace-input" placeholder="替换为" data-type="replace"> <button class="btn btn-warning" onclick="textFormatterToolkit.removeReplacePair(this)">删除</button> `; container.appendChild(pair); } removeReplacePair(button) { const pairs = document.querySelectorAll('.replace-pair'); if (pairs.length > 1) { button.parentElement.remove(); } } processReplace() { const input = document.getElementById('replace-input').value; if (!input) return; let result = input; const pairs = document.querySelectorAll('.replace-pair'); pairs.forEach(pair => { const findInput = pair.querySelector('[data-type="find"]'); const replaceInput = pair.querySelector('[data-type="replace"]'); const find = findInput.value; const replace = replaceInput.value; if (find) { result = result.replace(new RegExp(find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), replace); } }); document.getElementById('replace-output').value = result; } // 文本统计 processCount() { const input = document.getElementById('count-input').value; if (!input) return; const stats = { chars: input.length, words: input.trim() ? input.trim().split(/\s+/).length : 0, lines: input.split('\n').length, paragraphs: input.split(/\n\s*\n/).filter(p => p.trim()).length }; document.getElementById('char-count').textContent = stats.chars; document.getElementById('word-count').textContent = stats.words; document.getElementById('line-count').textContent = stats.lines; document.getElementById('para-count').textContent = stats.paragraphs; document.getElementById('count-stats').style.display = 'grid'; } // 信息提取 processExtract(type) { const input = document.getElementById('extract-input').value; if (!input) return; let results = []; const patterns = { email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, url: /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g, phone: /1[3-9]\d{9}|\d{3,4}-\d{7,8}|\(\d{3,4}\)\d{7,8}/g, ip: /\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/g }; const matches = input.match(patterns[type]) || []; results = [...new Set(matches)]; // 去重 const resultsContainer = document.getElementById('extract-items'); const resultsDiv = document.getElementById('extract-results'); if (results.length > 0) { resultsContainer.innerHTML = results.map(item => ` <div class="extract-item">${item}</div> `).join(''); document.getElementById('extract-output').value = results.join('\n'); } else { resultsContainer.innerHTML = '<div class="extract-item">未找到匹配的内容</div>'; document.getElementById('extract-output').value = ''; } resultsDiv.style.display = 'block'; } // 工具方法 clearInput(type) { document.getElementById(`${type}-input`).value = ''; if (document.getElementById(`${type}-output`)) { document.getElementById(`${type}-output`).value = ''; } if (type === 'count') { document.getElementById('count-stats').style.display = 'none'; } if (type === 'extract') { document.getElementById('extract-results').style.display = 'none'; } } copyOutput(type) { const output = document.getElementById(`${type}-output`); if (output && output.value) { GM_setClipboard(output.value); this.showNotification('已复制到剪贴板!'); } } // 辅助方法 formatXML(xml) { // 简单的XML格式化 return xml.replace(/></g, '>\n<').replace(/(>)(<)(\/)/g, '$1\n$2$3'); } formatSQL(sql) { // 简单的SQL格式化 return sql.replace(/\b(SELECT|FROM|WHERE|JOIN|LEFT|RIGHT|INNER|ORDER|GROUP|HAVING)\b/gi, '\n$1') .replace(/\b(AND|OR)\b/gi, '\n $1'); } formatCSV(input) { // 简单的CSV转换 const lines = input.split('\n').filter(line => line.trim()); return lines.map(line => line.split(/[,\t]/).map(cell => `"${cell.trim()}"`).join(',')).join('\n'); } formatMarkdown(input) { // 简单的Markdown转换 return input.split('\n').map(line => { line = line.trim(); if (line.length === 0) return ''; if (line.length < 50) return `## ${line}`; return line; }).join('\n\n'); } htmlEncode(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } htmlDecode(text) { const div = document.createElement('div'); div.innerHTML = text; return div.textContent; } unicodeEncode(text) { return text.split('').map(char => char.codePointAt(0) > 127 ? '\\u' + char.codePointAt(0).toString(16).padStart(4, '0') : char ).join(''); } unicodeDecode(text) { return text.replace(/\\u([0-9a-fA-F]{4})/g, (match, hex) => String.fromCharCode(parseInt(hex, 16)) ); } md5(text) { // 简化的MD5实现(实际使用需要完整实现) return 'MD5: ' + btoa(text).slice(0, 16); } shuffleArray(array) { const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; } toTitleCase(text) { return text.toLowerCase().replace(/\b\w/g, char => char.toUpperCase()); } toCamelCase(text) { return text.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (match, char) => char.toUpperCase()); } // 界面控制 togglePanel() { const panel = document.getElementById('text-formatter-panel'); if (panel.style.display === 'none' || !panel.style.display) { panel.style.display = 'block'; if (this.selectedText && !this.currentTool) { this.selectTool('clean'); } } else { this.hidePanel(); } } hidePanel() { document.getElementById('text-formatter-panel').style.display = 'none'; } showNotification(message) { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #28a745; color: white; padding: 12px 20px; border-radius: 6px; z-index: 10001; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.15); animation: slideIn 0.3s ease-out; `; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'fadeOut 0.3s ease-in'; setTimeout(() => notification.remove(), 300); }, 2000); } // 拖拽功能 makeDraggable(element, container) { let isDragging = false; let currentX; let currentY; let initialX; let initialY; let xOffset = 0; let yOffset = 0; element.addEventListener('mousedown', dragStart); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); function dragStart(e) { initialX = e.clientX - xOffset; initialY = e.clientY - yOffset; if (e.target === element) { isDragging = true; } } function drag(e) { if (isDragging) { e.preventDefault(); currentX = e.clientX - initialX; currentY = e.clientY - initialY; xOffset = currentX; yOffset = currentY; container.style.transform = `translate(${currentX}px, ${currentY}px)`; } } function dragEnd(e) { initialX = currentX; initialY = currentY; isDragging = false; } } } // 添加动画样式 GM_addStyle(` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } `); // 初始化 const textFormatterToolkit = new TextFormatterToolkit(); window.textFormatterToolkit = textFormatterToolkit; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址