// content.js - 核心功能实现 (function() { 'use strict'; console.log('[BlurText] Content script loaded'); let isBlurMode = false; let blurMode = 'element'; // 'element', 'selection', or 'area' let blurIntensity = 10; let blurredElements = new Set(); let hintElement = null; let blurButton = null; // 浮动模糊按钮 let elementTooltip = null; // 元素模式悬停提示 // 区域模式相关变量 let isDrawing = false; let startX = 0; let startY = 0; let drawingBox = null; let areaOverlays = []; // 存储所有区域覆盖层 // 初始化:从存储中加载配置 chrome.storage.local.get(['blurIntensity'], (result) => { if (result.blurIntensity) { blurIntensity = result.blurIntensity; console.log('[BlurText] Loaded blur intensity:', blurIntensity); } }); // 监听来自 popup 的消息 chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { console.log('[BlurText] Received message:', request); if (request.action === 'toggleBlurMode') { isBlurMode = request.enabled; blurMode = request.mode || 'element'; blurIntensity = request.intensity || 10; console.log('[BlurText] Toggle blur mode:', isBlurMode, 'mode:', blurMode, 'intensity:', blurIntensity); if (isBlurMode) { enableBlurMode(); } else { disableBlurMode(); } } else if (request.action === 'clearAll') { console.log('[BlurText] Clear all blurs'); clearAllBlurs(); } else if (request.action === 'updateIntensity') { blurIntensity = request.intensity; console.log('[BlurText] Update intensity:', blurIntensity); updateAllBlurIntensity(); } else if (request.action === 'switchMode') { blurMode = request.mode; console.log('[BlurText] Switch mode:', blurMode); updateModeUI(); } }); // 启用模糊模式 function enableBlurMode() { console.log('[BlurText] Enabling blur mode, mode:', blurMode); let modeText = '元素模式 - 点击元素进行模糊'; if (blurMode === 'selection') { modeText = '文本选择模式 - 拖动选择文字后点击模糊按钮'; } else if (blurMode === 'area') { modeText = '区域模式 - 拖动鼠标绘制模糊区域'; } showHint(`模糊模式已开启 - ${modeText},按 ESC 退出`); // 根据模式添加对应的样式和事件 if (blurMode === 'element') { document.body.classList.add('blurtext-mode'); document.addEventListener('click', handleClick, true); document.addEventListener('mouseover', handleMouseOver, true); document.addEventListener('mouseout', handleMouseOut, true); } else if (blurMode === 'selection') { // 文本选择模式不需要 crosshair 光标 document.addEventListener('mouseup', handleTextSelection, true); } else if (blurMode === 'area') { // 区域模式:使用 crosshair 光标并添加绘制事件 document.body.classList.add('blurtext-mode'); document.addEventListener('mousedown', handleAreaMouseDown, true); document.addEventListener('mousemove', handleAreaMouseMove, true); document.addEventListener('mouseup', handleAreaMouseUp, true); } // 键盘事件对所有模式都需要 document.addEventListener('keydown', handleKeydown, true); console.log('[BlurText] Event listeners attached'); } // 禁用模糊模式 function disableBlurMode() { document.body.classList.remove('blurtext-mode'); hideHint(); hideBlurButton(); hideDrawingBox(); hideElementTooltip(); // 移除所有事件监听 document.removeEventListener('click', handleClick, true); document.removeEventListener('keydown', handleKeydown, true); document.removeEventListener('mouseover', handleMouseOver, true); document.removeEventListener('mouseout', handleMouseOut, true); document.removeEventListener('mouseup', handleTextSelection, true); document.removeEventListener('mousedown', handleAreaMouseDown, true); document.removeEventListener('mousemove', handleAreaMouseMove, true); document.removeEventListener('mouseup', handleAreaMouseUp, true); // 清除所有高亮和预览 removeAllHighlights(); } // 更新模式UI function updateModeUI() { if (!isBlurMode) return; console.log('[BlurText] Switching mode to:', blurMode); // 先移除所有旧的事件监听和样式 document.body.classList.remove('blurtext-mode'); document.removeEventListener('click', handleClick, true); document.removeEventListener('mouseover', handleMouseOver, true); document.removeEventListener('mouseout', handleMouseOut, true); document.removeEventListener('mouseup', handleTextSelection, true); document.removeEventListener('mousedown', handleAreaMouseDown, true); document.removeEventListener('mousemove', handleAreaMouseMove, true); document.removeEventListener('mouseup', handleAreaMouseUp, true); // 清除旧模式的UI元素 hideBlurButton(); removeAllHighlights(); hideDrawingBox(); hideElementTooltip(); // 根据新模式添加事件监听和样式 if (blurMode === 'element') { document.body.classList.add('blurtext-mode'); document.addEventListener('click', handleClick, true); document.addEventListener('mouseover', handleMouseOver, true); document.addEventListener('mouseout', handleMouseOut, true); showHint('已切换到元素模式 - 点击元素进行模糊', 3000); } else if (blurMode === 'selection') { // 文本选择模式不需要 crosshair 光标 document.addEventListener('mouseup', handleTextSelection, true); showHint('已切换到文本选择模式 - 拖动选择文字后点击模糊按钮', 3000); } else if (blurMode === 'area') { // 区域模式 document.body.classList.add('blurtext-mode'); document.addEventListener('mousedown', handleAreaMouseDown, true); document.addEventListener('mousemove', handleAreaMouseMove, true); document.addEventListener('mouseup', handleAreaMouseUp, true); showHint('已切换到区域模式 - 拖动鼠标绘制模糊区域', 3000); } console.log('[BlurText] Mode switched successfully'); } // 处理点击事件 function handleClick(e) { console.log('[BlurText] Click detected, isBlurMode:', isBlurMode, 'blurMode:', blurMode); if (!isBlurMode || blurMode !== 'element') return; // 阻止默认行为 e.preventDefault(); e.stopPropagation(); let target = e.target; console.log('[BlurText] Target element:', target.tagName, target.className, 'text:', target.textContent?.substring(0, 20)); // 如果是提示元素,忽略 if (target.classList.contains('blurtext-hint')) { console.log('[BlurText] Ignored hint element'); return; } // 切换模糊状态 toggleBlur(target); } // 处理键盘事件(ESC 退出) function handleKeydown(e) { if (e.key === 'Escape' && isBlurMode) { isBlurMode = false; disableBlurMode(); // 通知 popup chrome.runtime.sendMessage({ action: 'blurModeDisabled' }); } } // 处理文本选择(文本选择模式) function handleTextSelection(e) { if (!isBlurMode || blurMode !== 'selection') return; // 避免点击模糊按钮时触发 if (e.target.classList.contains('blurtext-blur-button')) { return; } // 避免点击已模糊的文本段落时触发(这些段落有自己的点击事件) if (e.target.classList.contains('blurtext-selection-wrapped')) { return; } const selection = window.getSelection(); const selectedText = selection.toString().trim(); console.log('[BlurText] Text selection:', selectedText); if (selectedText.length > 0) { // 显示模糊按钮 showBlurButton(selection); } else { // 没有选中文本,隐藏按钮 hideBlurButton(); } } // 显示模糊按钮 function showBlurButton(selection) { // 移除旧按钮 hideBlurButton(); // 获取选区的位置 const range = selection.getRangeAt(0); const rect = range.getBoundingClientRect(); // 创建模糊按钮 blurButton = document.createElement('div'); blurButton.className = 'blurtext-blur-button'; blurButton.textContent = '🔒 模糊选中文本'; blurButton.style.position = 'fixed'; blurButton.style.left = `${rect.left + rect.width / 2}px`; blurButton.style.top = `${rect.bottom + 10}px`; blurButton.style.transform = 'translateX(-50%)'; blurButton.style.zIndex = '2147483647'; // 点击按钮模糊文本 blurButton.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); blurSelectedText(); }); document.body.appendChild(blurButton); } // 隐藏模糊按钮 function hideBlurButton() { if (blurButton && blurButton.parentNode) { blurButton.parentNode.removeChild(blurButton); blurButton = null; } } // 显示元素悬停提示 function showElementTooltip(element, text) { // 移除旧提示 hideElementTooltip(); // 获取元素位置 const rect = element.getBoundingClientRect(); // 创建提示元素 elementTooltip = document.createElement('div'); elementTooltip.className = 'blurtext-element-tooltip'; elementTooltip.textContent = text; elementTooltip.style.left = `${rect.left + rect.width / 2}px`; elementTooltip.style.top = `${rect.top + rect.height / 2}px`; elementTooltip.style.transform = 'translate(-50%, -50%)'; document.body.appendChild(elementTooltip); } // 隐藏元素悬停提示 function hideElementTooltip() { if (elementTooltip && elementTooltip.parentNode) { elementTooltip.parentNode.removeChild(elementTooltip); elementTooltip = null; } } // 模糊选中的文本 function blurSelectedText() { const selection = window.getSelection(); if (!selection.rangeCount) return; try { const range = selection.getRangeAt(0); // 获取选中的文本内容 const selectedText = range.toString(); // 验证选区是否符合要求 if (!selectedText.trim()) { console.log('[BlurText] No text selected'); hideBlurButton(); return; } // 限制:不允许包含换行 if (selectedText.includes('\n') || selectedText.includes('\r')) { console.log('[BlurText] Selection contains line breaks'); showSelectionError(selection, '不支持跨行选择'); hideBlurButton(); return; } // 限制:不允许包含多个空格(允许单个空格) if (/\s{2,}/.test(selectedText)) { console.log('[BlurText] Selection contains multiple spaces'); showSelectionError(selection, '不支持多个连续空格'); hideBlurButton(); return; } // 限制:检查是否跨越多个元素 const startContainer = range.startContainer; const endContainer = range.endContainer; // 如果起始和结束容器不同,则跨越了多个节点 if (startContainer !== endContainer) { console.log('[BlurText] Selection spans multiple elements'); showSelectionError(selection, '不支持跨元素选择'); hideBlurButton(); return; } // 限制:只能选择文本节点 if (startContainer.nodeType !== Node.TEXT_NODE) { console.log('[BlurText] Selection is not in a text node'); showSelectionError(selection, '请选择纯文本内容'); hideBlurButton(); return; } // 创建 span 包裹选中的文本 const span = document.createElement('span'); span.className = 'blurtext-blurred blurtext-selection-wrapped'; span.style.setProperty('--blur-intensity', `${blurIntensity}px`); span.style.cursor = 'pointer'; // 添加鼠标悬停事件,显示恢复提示 span.addEventListener('mouseenter', () => { showElementTooltip(span, '点击恢复此文本'); }); span.addEventListener('mouseleave', () => { hideElementTooltip(); }); // 添加点击事件,允许单独恢复 span.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); hideElementTooltip(); // 点击后立即隐藏提示 unblurSelectionSpan(span); }); // 使用 surroundContents(在严格限制下应该不会失败) range.surroundContents(span); // 添加到模糊元素集合 blurredElements.add(span); console.log('[BlurText] Text selection blurred, total elements:', blurredElements.size); // 清除选择 selection.removeAllRanges(); // 隐藏按钮 hideBlurButton(); } catch (error) { console.error('[BlurText] Error blurring selection:', error); showSelectionError(selection, '无法模糊该选区'); hideBlurButton(); } } // 在选区位置显示错误提示 function showSelectionError(selection, message) { if (!selection.rangeCount) return; const range = selection.getRangeAt(0); const rect = range.getBoundingClientRect(); // 创建错误提示元素 const errorTooltip = document.createElement('div'); errorTooltip.className = 'blurtext-selection-error'; errorTooltip.textContent = message; errorTooltip.style.position = 'fixed'; errorTooltip.style.left = `${rect.left + rect.width / 2}px`; errorTooltip.style.top = `${rect.bottom + 10}px`; errorTooltip.style.transform = 'translateX(-50%)'; errorTooltip.style.zIndex = '2147483647'; document.body.appendChild(errorTooltip); // 清除选择 selection.removeAllRanges(); // 2秒后自动移除 setTimeout(() => { if (errorTooltip.parentNode) { errorTooltip.parentNode.removeChild(errorTooltip); } }, 2000); } // 恢复单个选择模式的模糊段落 function unblurSelectionSpan(span) { if (!span || !span.parentNode) return; console.log('[BlurText] Unblurring selection span'); // 从集合中移除 blurredElements.delete(span); // 获取 span 的内容(保持子节点结构) const fragment = document.createDocumentFragment(); while (span.firstChild) { fragment.appendChild(span.firstChild); } // 用原内容替换 span span.parentNode.replaceChild(fragment, span); console.log('[BlurText] Selection span removed, remaining elements:', blurredElements.size); } // 鼠标悬停高亮 function handleMouseOver(e) { if (!isBlurMode || blurMode !== 'element') return; const target = e.target; if (target.classList.contains('blurtext-hint')) { return; } // 如果是已模糊的元素,显示恢复提示(不添加高亮) if (target.classList.contains('blurtext-blurred')) { removeAllHighlights(); // 移除其他高亮 showElementTooltip(target, '点击取消模糊'); return; } // 移除之前的高亮和提示 removeAllHighlights(); hideElementTooltip(); // 直接高亮元素 target.classList.add('blurtext-highlight'); } // 移除所有高亮 function removeAllHighlights() { const highlighted = document.querySelectorAll('.blurtext-highlight'); highlighted.forEach(el => { el.classList.remove('blurtext-highlight'); }); } // 鼠标移出取消高亮 function handleMouseOut(e) { if (!isBlurMode || blurMode !== 'element') return; // 隐藏提示 hideElementTooltip(); // 延迟移除,避免在元素间移动时闪烁 setTimeout(() => { // 检查鼠标是否还在模糊模式下的元素上 const hoveredElement = document.elementFromPoint(e.clientX, e.clientY); if (!hoveredElement || hoveredElement.classList.contains('blurtext-hint') || hoveredElement.classList.contains('blurtext-blurred')) { return; } // 如果鼠标不在任何高亮元素上,清除所有高亮 const highlighted = document.querySelector('.blurtext-highlight:hover'); if (!highlighted) { removeAllHighlights(); } }, 100); } // 切换元素模糊状态 function toggleBlur(element) { if (blurredElements.has(element)) { // 取消模糊 console.log('[BlurText] Removing blur from element'); element.classList.remove('blurtext-blurred'); element.style.removeProperty('--blur-intensity'); blurredElements.delete(element); // 隐藏提示气泡(因为元素已不再模糊) hideElementTooltip(); } else { // 添加模糊 console.log('[BlurText] Adding blur to element, intensity:', blurIntensity); element.classList.add('blurtext-blurred'); element.style.setProperty('--blur-intensity', `${blurIntensity}px`); blurredElements.add(element); } console.log('[BlurText] Total blurred elements:', blurredElements.size); } // 清除所有模糊 function clearAllBlurs() { blurredElements.forEach(element => { element.classList.remove('blurtext-blurred'); element.style.removeProperty('--blur-intensity'); }); blurredElements.clear(); // 清除所有区域覆盖层 areaOverlays.forEach(overlay => { if (overlay.parentNode) { overlay.parentNode.removeChild(overlay); } }); areaOverlays = []; // 如果在模糊模式下,显示提示 if (isBlurMode) { showHint('已清除所有模糊效果', 2000); } } // ========== 区域模式相关函数 ========== // 处理区域模式鼠标按下 function handleAreaMouseDown(e) { if (!isBlurMode || blurMode !== 'area') return; // 忽略提示元素和已有的区域覆盖层 if (e.target.classList.contains('blurtext-hint') || e.target.classList.contains('blurtext-area-overlay') || e.target.classList.contains('blurtext-area-close')) { return; } // 检查是否点击了关闭按钮 if (e.target.classList.contains('blurtext-area-close')) { const overlay = e.target.parentElement; removeAreaOverlay(overlay); e.preventDefault(); e.stopPropagation(); return; } isDrawing = true; startX = e.pageX; startY = e.pageY; // 创建绘制框 drawingBox = document.createElement('div'); drawingBox.className = 'blurtext-drawing-box'; drawingBox.style.position = 'absolute'; drawingBox.style.left = startX + 'px'; drawingBox.style.top = startY + 'px'; drawingBox.style.width = '0px'; drawingBox.style.height = '0px'; drawingBox.style.zIndex = '2147483646'; document.body.appendChild(drawingBox); e.preventDefault(); e.stopPropagation(); } // 处理区域模式鼠标移动 function handleAreaMouseMove(e) { if (!isBlurMode || blurMode !== 'area' || !isDrawing || !drawingBox) return; const currentX = e.pageX; const currentY = e.pageY; const width = Math.abs(currentX - startX); const height = Math.abs(currentY - startY); const left = Math.min(currentX, startX); const top = Math.min(currentY, startY); drawingBox.style.left = left + 'px'; drawingBox.style.top = top + 'px'; drawingBox.style.width = width + 'px'; drawingBox.style.height = height + 'px'; } // 处理区域模式鼠标释放 function handleAreaMouseUp(e) { if (!isBlurMode || blurMode !== 'area' || !isDrawing || !drawingBox) return; isDrawing = false; const width = parseInt(drawingBox.style.width); const height = parseInt(drawingBox.style.height); // 只有当区域足够大时才创建模糊覆盖层(至少 20x20 像素) if (width > 20 && height > 20) { createAreaOverlay( parseInt(drawingBox.style.left), parseInt(drawingBox.style.top), width, height ); } // 移除绘制框 if (drawingBox.parentNode) { drawingBox.parentNode.removeChild(drawingBox); } drawingBox = null; e.preventDefault(); e.stopPropagation(); } // 创建区域覆盖层 function createAreaOverlay(left, top, width, height) { const overlay = document.createElement('div'); overlay.className = 'blurtext-area-overlay'; overlay.style.position = 'absolute'; overlay.style.left = left + 'px'; overlay.style.top = top + 'px'; overlay.style.width = width + 'px'; overlay.style.height = height + 'px'; overlay.style.setProperty('--blur-intensity', `${blurIntensity}px`); overlay.style.zIndex = '2147483645'; // 点击覆盖层恢复区域 overlay.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); removeAreaOverlay(overlay); }); document.body.appendChild(overlay); areaOverlays.push(overlay); // 添加到模糊元素集合(用于统一管理强度) blurredElements.add(overlay); console.log('[BlurText] Area overlay created, total overlays:', areaOverlays.length); } // 移除区域覆盖层 function removeAreaOverlay(overlay) { const index = areaOverlays.indexOf(overlay); if (index > -1) { areaOverlays.splice(index, 1); } blurredElements.delete(overlay); if (overlay.parentNode) { overlay.parentNode.removeChild(overlay); } console.log('[BlurText] Area overlay removed, remaining:', areaOverlays.length); } // 隐藏绘制框 function hideDrawingBox() { if (drawingBox && drawingBox.parentNode) { drawingBox.parentNode.removeChild(drawingBox); drawingBox = null; } isDrawing = false; } // ========== 区域模式函数结束 ========== // 更新所有已模糊元素的强度 function updateAllBlurIntensity() { blurredElements.forEach(element => { element.style.setProperty('--blur-intensity', `${blurIntensity}px`); }); } // 显示提示 function showHint(message, duration = null) { // 移除旧提示 hideHint(); // 创建新提示 hintElement = document.createElement('div'); hintElement.className = 'blurtext-hint'; hintElement.textContent = message; document.body.appendChild(hintElement); // 如果指定了持续时间,自动隐藏 if (duration) { setTimeout(() => { hideHint(); }, duration); } } // 隐藏提示 function hideHint() { if (hintElement && hintElement.parentNode) { hintElement.parentNode.removeChild(hintElement); hintElement = null; } } // 页面加载完成后,恢复之前的模糊状态(未来功能) // 这里可以添加持久化存储功能 })();