您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
选中文本时自动检测URL并定位到指定行和字符位置
// ==UserScript== // @name URL定位器 // @license MIT // @namespace http://tampermonkey.net/ // @version 1.0 // @description 选中文本时自动检测URL并定位到指定行和字符位置 // @author You // @match https://apps-staging.razer.com/html-tools/anne-log-viewer.html // @grant GM_openInTab // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // ==/UserScript== // *://*/* (function() { 'use strict'; // 创建浮动窗口 function createFloatingWindow() { const floatingDiv = document.createElement('div'); floatingDiv.id = 'url-locator-window'; floatingDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 800px; max-height: 80vh; background: #fff; border: 2px solid #007bff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 10000; font-family: Arial, sans-serif; font-size: 14px; display: none; `; const header = document.createElement('div'); header.style.cssText = ` background: #007bff; color: white; padding: 10px; border-radius: 6px 6px 0 0; font-weight: bold; display: flex; justify-content: space-between; align-items: center; `; header.innerHTML = ` <span>URL定位器</span> <button id="close-locator" style="background: none; border: none; color: white; font-size: 18px; cursor: pointer;">×</button> `; const content = document.createElement('div'); content.id = 'locator-content'; content.style.cssText = ` padding: 15px; max-height: calc(80vh - 60px); overflow-y: auto; `; floatingDiv.appendChild(header); floatingDiv.appendChild(content); document.body.appendChild(floatingDiv); // 关闭按钮事件 document.getElementById('close-locator').addEventListener('click', () => { floatingDiv.style.display = 'none'; }); return floatingDiv; } // 解析URL和位置信息 function parseUrlWithLocation(text) { // 匹配URL:行号:字符位置的模式 const pattern = /(https?:\/\/[^\s:]+\.[^\s:]+)(?::(\d+))?(?::(\d+))?/g; const matches = []; let match; while ((match = pattern.exec(text)) !== null) { matches.push({ url: match[1], line: match[2] ? parseInt(match[2]) : null, char: match[3] ? parseInt(match[3]) : null, fullMatch: match[0] }); } return matches; } // 获取文件内容并定位 function fetchAndLocate(urlInfo) { return new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: urlInfo.url, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' }, timeout: 10000, // 10秒超时 onload: function(response) { try { if (response.status !== 200) { resolve({ success: false, url: urlInfo.url, error: `HTTP ${response.status}: ${response.statusText}` }); return; } const text = response.responseText; const lines = text.split('\n'); let targetLine = ''; let targetPosition = ''; let context = ''; if (urlInfo.line && urlInfo.line <= lines.length) { targetLine = lines[urlInfo.line - 1]; // 行号从1开始,数组从0开始 if (urlInfo.char && urlInfo.char <= targetLine.length) { // 获取字符位置前后的上下文 const start = Math.max(0, urlInfo.char - 200); const end = Math.min(targetLine.length, urlInfo.char + 200); const beforeChar = targetLine.substring(start, urlInfo.char - 1); const targetChar = targetLine.charAt(urlInfo.char - 1); const afterChar = targetLine.substring(urlInfo.char, end); // HTML转义函数 const escapeHtml = (text) => { return text .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; context = `${escapeHtml(beforeChar)}<span style="background: yellow; color: red; font-weight: bold;">${escapeHtml(targetChar)}</span>${escapeHtml(afterChar)}`; targetPosition = `字符位置: ${urlInfo.char}`; } else { context = targetLine.length > 1000 ? targetLine.substring(0, 1000) + '...' : targetLine; targetPosition = urlInfo.char ? `字符位置超出范围 (${urlInfo.char} > ${targetLine.length})` : ''; } } else { targetLine = urlInfo.line ? `行号超出范围 (${urlInfo.line} > ${lines.length})` : '未指定行号'; } resolve({ success: true, url: urlInfo.url, line: urlInfo.line, char: urlInfo.char, targetLine, targetPosition, context, totalLines: lines.length, fileSize: text.length }); } catch (error) { resolve({ success: false, url: urlInfo.url, error: '解析响应时出错: ' + error.message }); } }, onerror: function(error) { resolve({ success: false, url: urlInfo.url, error: '网络请求失败: ' + (error.error || '未知错误') }); }, ontimeout: function() { resolve({ success: false, url: urlInfo.url, error: '请求超时 (10秒)' }); } }); }); } // 更新浮动窗口内容 async function updateFloatingWindow(urlInfos) { const floatingWindow = document.getElementById('url-locator-window') || createFloatingWindow(); const content = document.getElementById('locator-content'); content.innerHTML = '<div style="text-align: center; padding: 20px;">加载中...</div>'; floatingWindow.style.display = 'block'; let html = ''; for (let i = 0; i < urlInfos.length; i++) { const urlInfo = urlInfos[i]; html += `<div style="margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; border-radius: 4px;">`; html += `<div style="font-weight: bold; color: #007bff; margin-bottom: 8px;">URL ${i + 1}:</div>`; html += `<div style="word-break: break-all; margin-bottom: 5px;"><a href="${urlInfo.url}" target="_blank">${urlInfo.url}</a></div>`; if (urlInfo.line) html += `<div><strong>行号:</strong> ${urlInfo.line}</div>`; if (urlInfo.char) html += `<div><strong>字符位置:</strong> ${urlInfo.char}</div>`; html += `<div style="margin-top: 10px;"><strong>结果:</strong></div>`; html += `<div id="result-${i}" style="margin-top: 5px; padding: 8px; background: #f8f9fa; border-radius: 4px;">加载中...</div>`; html += `</div>`; } content.innerHTML = html; // 异步加载每个URL的内容 for (let i = 0; i < urlInfos.length; i++) { const result = await fetchAndLocate(urlInfos[i]); const resultDiv = document.getElementById(`result-${i}`); if (result.success) { resultDiv.innerHTML = ` <div><strong>文件信息:</strong> ${result.totalLines} 行, ${result.fileSize} 字符</div> ${result.line ? `<div><strong>第 ${result.line} 行内容:</strong></div>` : ''} ${result.targetPosition ? `<div><strong>${result.targetPosition}</strong></div>` : ''} <div style="margin-top: 8px; padding: 12px; background: white; border: 1px solid #ddd; border-radius: 4px; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 13px; max-height: 300px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; line-height: 1.4;"> ${result.context || result.targetLine} </div> `; } else { resultDiv.innerHTML = `<div style="color: red;"><strong>错误:</strong> ${result.error}</div>`; } } } // 监听文本选择事件 document.addEventListener('mouseup', function(e) { const selection = window.getSelection(); const selectedText = selection.toString().trim(); if (selectedText.length > 0) { const urlInfos = parseUrlWithLocation(selectedText); if (urlInfos.length > 0) { updateFloatingWindow(urlInfos); } } }); // 监听键盘事件,按ESC关闭窗口 document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { const floatingWindow = document.getElementById('url-locator-window'); if (floatingWindow) { floatingWindow.style.display = 'none'; } } }); // 添加拖拽功能 let isDragging = false; let dragOffset = { x: 0, y: 0 }; document.addEventListener('mousedown', function(e) { const floatingWindow = document.getElementById('url-locator-window'); const locatorContent = document.getElementById('locator-content'); if (floatingWindow && floatingWindow.contains(e.target) && !locatorContent.contains(e.target) && e.target.tagName !== 'BUTTON' && e.target.tagName !== 'A') { isDragging = true; const rect = floatingWindow.getBoundingClientRect(); dragOffset.x = e.clientX - rect.left; dragOffset.y = e.clientY - rect.top; floatingWindow.style.cursor = 'move'; } }); document.addEventListener('mousemove', function(e) { if (isDragging) { const floatingWindow = document.getElementById('url-locator-window'); if (floatingWindow) { floatingWindow.style.left = (e.clientX - dragOffset.x) + 'px'; floatingWindow.style.top = (e.clientY - dragOffset.y) + 'px'; floatingWindow.style.right = 'auto'; } } }); document.addEventListener('mouseup', function() { if (isDragging) { isDragging = false; const floatingWindow = document.getElementById('url-locator-window'); if (floatingWindow) { floatingWindow.style.cursor = 'default'; } } }); })();