411 lines
12 KiB
JavaScript
411 lines
12 KiB
JavaScript
// content.js - 核心功能实现
|
||
(function() {
|
||
'use strict';
|
||
|
||
console.log('[BlurText] Content script loaded');
|
||
|
||
let isBlurMode = false;
|
||
let blurMode = 'element'; // 'element' or 'selection'
|
||
let blurIntensity = 10;
|
||
let blurredElements = new Set();
|
||
let hintElement = null;
|
||
let blurButton = null; // 浮动模糊按钮
|
||
|
||
// 初始化:从存储中加载配置
|
||
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);
|
||
|
||
const modeText = blurMode === 'selection' ? '文本选择模式 - 拖动选择文字后点击模糊按钮' : '元素模式 - 点击元素进行模糊';
|
||
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);
|
||
}
|
||
|
||
// 键盘事件对两种模式都需要
|
||
document.addEventListener('keydown', handleKeydown, true);
|
||
|
||
console.log('[BlurText] Event listeners attached');
|
||
}
|
||
|
||
// 禁用模糊模式
|
||
function disableBlurMode() {
|
||
document.body.classList.remove('blurtext-mode');
|
||
hideHint();
|
||
hideBlurButton();
|
||
|
||
// 移除所有事件监听
|
||
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);
|
||
|
||
// 清除所有高亮和预览
|
||
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);
|
||
|
||
// 清除旧模式的UI元素
|
||
hideBlurButton();
|
||
removeAllHighlights();
|
||
|
||
// 根据新模式添加事件监听和样式
|
||
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);
|
||
}
|
||
|
||
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 blurSelectedText() {
|
||
const selection = window.getSelection();
|
||
if (!selection.rangeCount) return;
|
||
|
||
try {
|
||
const range = selection.getRangeAt(0);
|
||
|
||
// 创建 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.title = '点击恢复此段文本';
|
||
|
||
// 添加点击事件,允许单独恢复
|
||
span.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
unblurSelectionSpan(span);
|
||
});
|
||
|
||
// 包裹选中的内容
|
||
range.surroundContents(span);
|
||
|
||
// 添加到模糊元素集合
|
||
blurredElements.add(span);
|
||
|
||
console.log('[BlurText] Text selection blurred, total elements:', blurredElements.size);
|
||
|
||
// 清除选择
|
||
selection.removeAllRanges();
|
||
|
||
// 隐藏按钮
|
||
hideBlurButton();
|
||
|
||
// 显示提示
|
||
showHint('文本已模糊(点击可单独恢复)', 2000);
|
||
} catch (error) {
|
||
console.error('[BlurText] Error blurring selection:', error);
|
||
showHint('无法模糊该选区(可能包含复杂的HTML结构)', 3000);
|
||
hideBlurButton();
|
||
}
|
||
}
|
||
|
||
// 恢复单个选择模式的模糊段落
|
||
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);
|
||
|
||
// 显示提示
|
||
showHint('已恢复此段文本', 1500);
|
||
|
||
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') ||
|
||
target.classList.contains('blurtext-blurred')) {
|
||
return;
|
||
}
|
||
|
||
// 移除之前的高亮
|
||
removeAllHighlights();
|
||
|
||
// 直接高亮元素
|
||
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;
|
||
|
||
// 延迟移除,避免在元素间移动时闪烁
|
||
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);
|
||
} 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();
|
||
|
||
// 如果在模糊模式下,显示提示
|
||
if (isBlurMode) {
|
||
showHint('已清除所有模糊效果', 2000);
|
||
}
|
||
}
|
||
|
||
// 更新所有已模糊元素的强度
|
||
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;
|
||
}
|
||
}
|
||
|
||
// 页面加载完成后,恢复之前的模糊状态(未来功能)
|
||
// 这里可以添加持久化存储功能
|
||
|
||
})();
|