// ==UserScript== // @name 事件屏蔽器 // @namespace http://tampermonkey.net/ // @version 1.10 // @description 屏蔽浏览器中的鼠标、键盘等事件,可配置 // @author Tales // @license MIT // @git https://github.com/Talesxx/event-blocker // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // ==/UserScript== (function () { 'use strict'; // 事件列表 const events = { mouseEvents: [ // 'click', // 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'contextmenu' ], keyboardEvents: [ 'keydown', 'keyup', 'keypress' ], touchEvents: [ 'touchstart', 'touchmove', 'touchend', 'touchcancel' ], wheelEvents: [ 'wheel', 'mousewheel', 'DOMMouseScroll' ], focusEvents: [ 'focus', 'blur', 'focusin', 'focusout' ], formEvents: [ 'submit', 'change', 'input', 'reset' ], clipboardEvents: [ 'copy', 'cut', 'paste' ], dragEvents: [ 'drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop' ] }; // 多语言翻译 const translations = { zh: { title: '⚙️ 事件屏蔽配置', mouseEvents: '鼠标事件', keyboardEvents: '键盘事件', touchEvents: '触摸事件', wheelEvents: '滚轮事件', focusEvents: '焦点事件', formEvents: '表单事件', clipboardEvents: '剪贴板事件', dragEvents: '拖拽事件', cancel: '取消', save: '💾 保存配置', mousedown: '鼠标按下', mouseup: '鼠标抬起', mousemove: '鼠标移动', mouseover: '鼠标悬停', mouseout: '鼠标移出', mouseenter: '鼠标进入', mouseleave: '鼠标离开', contextmenu: '右键菜单', keydown: '按键按下', keyup: '按键抬起', keypress: '按键输入', touchstart: '触摸开始', touchmove: '触摸移动', touchend: '触摸结束', touchcancel: '触摸取消', wheel: '滚轮滚动', mousewheel: '鼠标滚轮', DOMMouseScroll: 'DOM滚轮', focus: '获得焦点', blur: '失去焦点', focusin: '焦点进入', focusout: '焦点离开', submit: '表单提交', change: '内容改变', input: '输入内容', reset: '表单重置', copy: '复制', cut: '剪切', paste: '粘贴', drag: '拖拽中', dragstart: '开始拖拽', dragend: '结束拖拽', dragover: '拖拽经过', dragenter: '拖拽进入', dragleave: '拖拽离开', drop: '放置', menuCommand: '配置事件屏蔽', enhancedMode: '增强模式', enhancedModeDescription: '通过重写 addEventListener 提供更强大的事件阻止能力', enhancedModeEnabled: '增强模式已启用', enhancedModeDisabled: '增强模式已禁用', saveGlobal: '💾 保存全局配置', savePage: '🌐 保存网页配置', domainConfig: '域名配置', regexConfig: '正则配置', currentDomain: '当前域名', domainInput: '域名(如:example.com)', regexInput: '正则表达式(如:.*\\.example\\.com)', configType: '配置类型', domainType: '域名匹配', regexType: '正则匹配', deleteConfig: '删除配置', configPriority: '配置优先级:完整域名 > 正则配置 > 全局配置', currentConfig: '当前配置', currentType: '当前生效类型', currentUrl: '当前网址', globalConfig: '全局配置', domainTypeConfig: '域名配置', regexTypeConfig: '正则配置' }, en: { title: '⚙️ Event Blocker Config', mouseEvents: 'Mouse Events', keyboardEvents: 'Keyboard Events', touchEvents: 'Touch Events', wheelEvents: 'Wheel Events', focusEvents: 'Focus Events', formEvents: 'Form Events', clipboardEvents: 'Clipboard Events', dragEvents: 'Drag Events', cancel: 'Cancel', save: '💾 Save Config', mousedown: 'Mouse Down', mouseup: 'Mouse Up', mousemove: 'Mouse Move', mouseover: 'Mouse Over', mouseout: 'Mouse Out', mouseenter: 'Mouse Enter', mouseleave: 'Mouse Leave', contextmenu: 'Context Menu', keydown: 'Key Down', keyup: 'Key Up', keypress: 'Key Press', touchstart: 'Touch Start', touchmove: 'Touch Move', touchend: 'Touch End', touchcancel: 'Touch Cancel', wheel: 'Wheel', mousewheel: 'Mouse Wheel', DOMMouseScroll: 'DOM Mouse Scroll', focus: 'Focus', blur: 'Blur', focusin: 'Focus In', focusout: 'Focus Out', submit: 'Submit', change: 'Change', input: 'Input', reset: 'Reset', copy: 'Copy', cut: 'Cut', paste: 'Paste', drag: 'Drag', dragstart: 'Drag Start', dragend: 'Drag End', dragover: 'Drag Over', dragenter: 'Drag Enter', dragleave: 'Drag Leave', drop: 'Drop', menuCommand: 'Configure Event Blocker', enhancedMode: 'Enhanced Mode', enhancedModeDescription: 'Provide stronger event blocking by overriding addEventListener', enhancedModeEnabled: 'Enhanced Mode Enabled', enhancedModeDisabled: 'Enhanced Mode Disabled', saveGlobal: '💾 Save Global Config', savePage: '🌐 Save Page Config', domainConfig: 'Domain Config', regexConfig: 'Regex Config', currentDomain: 'Current Domain', domainInput: 'Domain (e.g., example.com)', regexInput: 'Regex (e.g., .*\\.example\\.com)', configType: 'Config Type', domainType: 'Domain Match', regexType: 'Regex Match', deleteConfig: 'Delete Config', configPriority: 'Priority: Full Domain > Regex > Global', currentConfig: 'Current Config', currentType: 'Current Type', currentUrl: 'Current URL', globalConfig: 'Global Config', domainTypeConfig: 'Domain Config', regexTypeConfig: 'Regex Config' } }; // 事件类型图标 const eventTypeIcons = { mouseEvents: '🖱️', keyboardEvents: '⌨️', touchEvents: '👆', wheelEvents: '🔄', focusEvents: '👁️', formEvents: '📝', clipboardEvents: '📋', dragEvents: '🎯' }; // 生成默认配置 function generateDefaultConfig() { const defaultConfig = {}; for (const [eventType, eventList] of Object.entries(events)) { defaultConfig[eventType] = {}; eventList.forEach(eventName => { defaultConfig[eventType][eventName] = false; }); } defaultConfig.enhancedMode = false; return defaultConfig; } function generateDefaultAllConfigs() { return { global: generateDefaultConfig(), domains: {} }; } function getCurrentDomain() { try { return window.location.hostname; } catch (e) { return ''; } } function getAllConfigs() { const oldConfig = GM_getValue('eventBlockerConfig', null); let allConfigs = GM_getValue('eventBlockerAllConfigs', null); if (!allConfigs) { allConfigs = generateDefaultAllConfigs(); if (oldConfig) { allConfigs.global = oldConfig; GM_setValue('eventBlockerAllConfigs', allConfigs); } } return allConfigs; } function getEffectiveConfig() { const allConfigs = getAllConfigs(); const currentDomain = getCurrentDomain(); let matchedDomain = null; let matchedRegex = null; const fullDomain = currentDomain; for (const [domain, domainConfig] of Object.entries(allConfigs.domains)) { if (domainConfig.type === 'domain') { if (domain === fullDomain) { matchedDomain = domain; break; } } else if (domainConfig.type === 'regex') { try { const regex = new RegExp(domain); if (regex.test(currentDomain)) { matchedRegex = domain; } } catch (e) { console.warn('Invalid regex pattern:', domain); } } } if (matchedDomain) { return allConfigs.domains[matchedDomain].config; } if (matchedRegex) { return allConfigs.domains[matchedRegex].config; } return allConfigs.global; } function saveGlobalConfig(config) { const allConfigs = getAllConfigs(); allConfigs.global = config; GM_setValue('eventBlockerAllConfigs', allConfigs); } function saveDomainConfig(domain, config, type = 'domain') { const allConfigs = getAllConfigs(); allConfigs.domains[domain] = { config, type }; GM_setValue('eventBlockerAllConfigs', allConfigs); } function deleteDomainConfig(domain) { const allConfigs = getAllConfigs(); delete allConfigs.domains[domain]; GM_setValue('eventBlockerAllConfigs', allConfigs); } // 保存配置 function saveConfig(config) { for (const [eventType, eventList] of Object.entries(events)) { if (!config[eventType]) { config[eventType] = {}; } eventList.forEach(eventName => { const checkbox = document.getElementById(`event-${eventType}-${eventName}`); if (checkbox) { config[eventType][eventName] = checkbox.checked; } }); } const enhancedModeCheckbox = document.getElementById('enhanced-mode-toggle'); if (enhancedModeCheckbox) { config.enhancedMode = enhancedModeCheckbox.checked; } } // 切换语言 function toggleLanguage(currentLanguage) { currentLanguage = currentLanguage === 'zh' ? 'en' : 'zh'; GM_setValue('eventBlockerLanguage', currentLanguage); // 更新菜单命令 const menuCommand = document.querySelector('.event-blocker-menu-command'); if (menuCommand) { menuCommand.textContent = t(currentLanguage, 'menuCommand'); } return currentLanguage; } function getCurrentLanguage() { return GM_getValue('eventBlockerLanguage', 'zh'); } // 获取翻译文本 function t(language, key) { return translations[language][key] || key; } // 配置窗口元素 let configWindow$2 = null; let overlay$2 = null; // 屏蔽事件的函数 function blockEvent(e) { // 检查事件目标是否是配置窗口或其子元素 let target = e.target; while (target) { if (target === configWindow$2 || target === overlay$2) { return false; } target = target.parentElement; } // e.preventDefault(); // 不阻止默认行为 e.stopPropagation(); return false; } let initStatus$1 = false; // 屏蔽事件的函数 function generalBlockEvents(config) { if (initStatus$1) return; initStatus$1 = true; // 遍历所有事件类型 for (const [eventType, eventList] of Object.entries(events)) { // 遍历该类型下的所有事件 eventList.forEach(eventName => { // 检查该事件是否被配置为屏蔽 if (config[eventType] && config[eventType][eventName]) { // 在捕获阶段添加监听器 document.addEventListener(eventName, blockEvent, true); } }); } } // 取消屏蔽事件的函数 function generalUnblockEvents() { if (!initStatus$1) return; initStatus$1 = false; // 遍历所有事件类型 for (const [eventType, eventList] of Object.entries(events)) { // 遍历该类型下的所有事件 eventList.forEach(eventName => { // 检查该事件是否被配置为屏蔽 // if (config[eventType] && config[eventType][eventName]) { // 移除捕获阶段的监听器 document.removeEventListener(eventName, blockEvent, true); // } }); } } // 设置配置窗口元素 function setConfigWindowElements(window, overlayElement) { configWindow$2 = window; overlay$2 = overlayElement; } // 增强模式事件阻止器 // 增强模式状态 let enhancedModeEnabled = false; let blockedEventTypes = new Set(); let configWindow$1 = null; let overlay$1 = null; // 保存原始的 addEventListener 方法 const originalAddEventListener = window.EventTarget.prototype.addEventListener; const originalRemoveEventListener = window.EventTarget.prototype.removeEventListener; // 存储被拦截的事件监听器 const interceptedListeners = new WeakMap(); // 检查元素是否在配置窗口内 function isInsideConfigWindow(target) { if (!configWindow$1 && !overlay$1 || !target) return false; let element = target; while (element) { if (element === configWindow$1 || element === overlay$1) { return true; } element = element.parentElement; } return false; } // 创建拦截的事件处理器 function createInterceptedHandler(originalHandler, eventType) { if (typeof originalHandler !== 'function') { console.log("传入的监听器不是函数,无法创建拦截处理器。", originalHandler, eventType); return originalHandler; } return function interceptedHandler(event) { // 是否拦截 该事件类型 let isBlocked = blockedEventTypes.has(eventType); // 如果增强模式未启用,直接执行原始处理器 if (!enhancedModeEnabled) { isBlocked = false; } // 检查事件目标是否在配置窗口内 if (isInsideConfigWindow(event.target)) { isBlocked = false; } // 检查事件类型是否被阻止 if (isBlocked) { // event.preventDefault(); event.stopPropagation(); event.stopImmediatePropagation(); return false; } // 执行原始处理器 return originalHandler.call(this, event); }; } // 重写 addEventListener function overrideAddEventListener() { window.EventTarget.prototype.addEventListener = function (type, listener, options) { var _a; // 如果监听器是 null 或 undefined,直接调用原始方法 if (!listener) { // console.log("监听器是 null 或 undefined。未拦截:", this, type, listener, options); return originalAddEventListener.call(this, type, listener, options); } // 检查 this 是否是对象,如果不是,直接调用原始方法 if (typeof this !== 'object' || this === null) { // console.log("this 不是对象或 null,增强模式无法完美处理。未拦截:", this, type, listener, options); return originalAddEventListener.call(this, type, listener, options); } // 创建拦截的处理器 const interceptedHandler = createInterceptedHandler(listener, type); // 存储映射关系 if (!interceptedListeners.has(this)) { interceptedListeners.set(this, new Map()); } const elementListeners = interceptedListeners.get(this); if (!(elementListeners === null || elementListeners === void 0 ? void 0 : elementListeners.has(type))) { elementListeners === null || elementListeners === void 0 ? void 0 : elementListeners.set(type, new Map()); } (_a = elementListeners === null || elementListeners === void 0 ? void 0 : elementListeners.get(type)) === null || _a === void 0 ? void 0 : _a.set(listener, interceptedHandler); if (typeof listener === 'function' && typeof this === 'object' && this !== null) { // 调用原始方法,但使用拦截的处理器 return originalAddEventListener.call(this, type, interceptedHandler, options); } else { // console.log("警告,增强模式无法完美处理的事件。", this, type, listener, options); return originalAddEventListener.call(this, type, listener, options); } }; } // 重写 removeEventListener function overrideRemoveEventListener() { window.EventTarget.prototype.removeEventListener = function (type, listener, options) { // 检查 this 是否是对象,如果不是,直接调用原始方法 if (typeof this !== 'object' || this === null) { return originalRemoveEventListener.call(this, type, listener, options); } // 获取存储的拦截处理器 const elementListeners = interceptedListeners.get(this); if (elementListeners && elementListeners.has(type) && listener) { const typeListeners = elementListeners.get(type); const interceptedHandler = typeListeners === null || typeListeners === void 0 ? void 0 : typeListeners.get(listener); if (interceptedHandler) { // 使用拦截的处理器移除 const result = originalRemoveEventListener.call(this, type, interceptedHandler, options); typeListeners === null || typeListeners === void 0 ? void 0 : typeListeners.delete(listener); return result; } } // 如果没有找到映射,直接调用原始方法 return originalRemoveEventListener.call(this, type, listener, options); }; } let initStatus = false; // 初始化增强模式事件阻止器 function initEnhancedEventBlocker() { if (initStatus) return; initStatus = true; overrideAddEventListener(); overrideRemoveEventListener(); } // 启用增强模式 function enableEnhancedMode(eventTypes) { if (enhancedModeEnabled) return; blockedEventTypes = new Set(eventTypes); enhancedModeEnabled = true; } // 禁用增强模式 function disableEnhancedMode() { if (!enhancedModeEnabled) return; enhancedModeEnabled = false; blockedEventTypes.clear(); } // 设置配置窗口元素 function setEnhancedModeConfigWindowElements(window, overlayElement) { configWindow$1 = window; overlay$1 = overlayElement; } // 配置窗口元素 let configWindow = null; let overlay = null; // 关闭配置窗口的函数 function closeConfigWindow() { if (configWindow && overlay) { document.body.removeChild(configWindow); document.body.removeChild(overlay); configWindow = null; overlay = null; } } // 应用增强模式 function applyEnhancedMode(config) { const effectiveConfig = getEffectiveConfig(); if (effectiveConfig.enhancedMode) { const enabledTypes = []; for (const [eventType, eventList] of Object.entries(events)) { eventList.forEach(eventName => { if (effectiveConfig[eventType] && effectiveConfig[eventType][eventName]) { enabledTypes.push(eventName); } }); } generalUnblockEvents(); generalBlockEvents(effectiveConfig); enableEnhancedMode(enabledTypes); } else { disableEnhancedMode(); generalUnblockEvents(); generalBlockEvents(effectiveConfig); } } // 移除禁用事件 function removeDisabledEvents() { disableEnhancedMode(); generalUnblockEvents(); } // 创建配置窗口 function createConfigWindow(config) { const currentLanguage = getCurrentLanguage(); const effectiveConfig = getEffectiveConfig(); config = effectiveConfig; // 检查配置窗口是否已经存在 if (configWindow && overlay) { // 窗口已存在,直接返回 return; } // 检查页面上是否已经存在配置窗口元素 const existingWindow = document.getElementById('event-blocker-config-window'); const existingOverlay = document.getElementById('event-blocker-overlay'); if (existingWindow || existingOverlay) { // 清理已存在的元素 if (existingWindow) { document.body.removeChild(existingWindow); } if (existingOverlay) { document.body.removeChild(existingOverlay); } } // 创建配置窗口 configWindow = document.createElement('div'); configWindow.id = 'event-blocker-config-window'; configWindow.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0.95); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 16px; padding: 0; z-index: 9999; box-shadow: 0 20px 60px rgba(0,0,0,0.3); min-width: 700px; max-width: 90vw; max-height: 90vh; overflow: hidden; opacity: 0; transition: all 0.3s ease; `; // 创建标题栏 const header = document.createElement('div'); header.style.cssText = ` background: rgba(255,255,255,0.1); padding: 20px 25px; border-bottom: 1px solid rgba(255,255,255,0.2); `; const title = document.createElement('h2'); title.textContent = t(currentLanguage, 'title'); title.style.cssText = ` margin: 0; color: white; font-size: 24px; font-weight: 600; text-shadow: 0 2px 4px rgba(0,0,0,0.2); text-align: center; `; header.appendChild(title); // 创建增强模式开关 const enhancedModeContainer = document.createElement('div'); enhancedModeContainer.style.cssText = ` position: absolute; left: 25px; top: 50%; transform: translateY(-50%); display: flex; align-items: center; gap: 8px; `; const enhancedModeLabel = document.createElement('span'); enhancedModeLabel.textContent = t(currentLanguage, 'enhancedMode'); enhancedModeLabel.style.cssText = ` color: white; font-size: 14px; font-weight: 500; `; const enhancedModeSwitch = document.createElement('label'); enhancedModeSwitch.style.cssText = ` position: relative; display: inline-block; width: 44px; height: 24px; cursor: pointer; `; const enhancedModeInput = document.createElement('input'); enhancedModeInput.type = 'checkbox'; enhancedModeInput.id = 'enhanced-mode-toggle'; enhancedModeInput.checked = effectiveConfig.enhancedMode || false; enhancedModeInput.style.cssText = ` opacity: 0; width: 0; height: 0; `; const enhancedModeSlider = document.createElement('span'); enhancedModeSlider.style.cssText = ` position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: ${effectiveConfig.enhancedMode ? '#4CAF50' : '#ccc'}; transition: .3s; border-radius: 24px; `; const enhancedModeKnob = document.createElement('span'); enhancedModeKnob.style.cssText = ` position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .3s; border-radius: 50%; transform: ${effectiveConfig.enhancedMode ? 'translateX(20px)' : 'translateX(0)'}; box-shadow: 0 1px 3px rgba(0,0,0,0.3); `; enhancedModeSlider.appendChild(enhancedModeKnob); enhancedModeSwitch.appendChild(enhancedModeInput); enhancedModeSwitch.appendChild(enhancedModeSlider); // 切换事件 enhancedModeInput.addEventListener('change', function (e) { const isEnabled = e.target.checked; config.enhancedMode = isEnabled; // 更新UI enhancedModeSlider.style.backgroundColor = isEnabled ? '#4CAF50' : '#ccc'; enhancedModeKnob.style.transform = isEnabled ? 'translateX(20px)' : 'translateX(0)'; }); enhancedModeContainer.appendChild(enhancedModeLabel); enhancedModeContainer.appendChild(enhancedModeSwitch); header.appendChild(enhancedModeContainer); // 创建语言切换按钮 const langButton = document.createElement('button'); langButton.textContent = currentLanguage === 'zh' ? '🇺🇸 EN' : '🇨🇳 中文'; langButton.style.cssText = ` position: absolute; right: 25px; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,0.2); color: white; border: 2px solid rgba(255,255,255,0.3); padding: 8px 16px; border-radius: 20px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease; `; langButton.onmouseover = function () { this.style.background = 'rgba(255,255,255,0.3)'; this.style.borderColor = 'rgba(255,255,255,0.5)'; }; langButton.onmouseout = function () { this.style.background = 'rgba(255,255,255,0.2)'; this.style.borderColor = 'rgba(255,255,255,0.3)'; }; header.style.position = 'relative'; header.appendChild(langButton); // 创建内容容器 const content = document.createElement('div'); content.style.cssText = ` background: white; padding: 25px; max-height: 60vh; overflow-y: auto; `; const currentDomain = getCurrentDomain(); const allConfigs = getAllConfigs(); let configType = 'global'; let configSource = t(currentLanguage, 'globalConfig'); const existingDomainConfig = Object.keys(allConfigs.domains).find(key => { const domainConfig = allConfigs.domains[key]; if (domainConfig.type === 'domain') { if (key === currentDomain) { configType = 'domain'; configSource = `${t(currentLanguage, 'domainTypeConfig')}: ${key}`; return true; } } else { try { const regex = new RegExp(key); if (regex.test(currentDomain)) { configType = 'regex'; configSource = `${t(currentLanguage, 'regexTypeConfig')}: ${key}`; return true; } } catch (e) { return false; } } return false; }); const domainSection = document.createElement('div'); domainSection.style.cssText = ` background: #f8f9fa; border-radius: 12px; padding: 20px; margin-bottom: 20px; border: 1px solid #e9ecef; `; const domainTitle = document.createElement('h3'); domainTitle.textContent = t(currentLanguage, 'currentConfig'); domainTitle.style.cssText = ` margin: 0 0 15px 0; color: #495057; font-size: 16px; font-weight: 600; `; domainSection.appendChild(domainTitle); const infoContainer = document.createElement('div'); infoContainer.style.cssText = ` display: flex; flex-direction: column; gap: 8px; `; const currentTypeInfo = document.createElement('div'); currentTypeInfo.style.cssText = ` font-size: 14px; color: #495057; `; currentTypeInfo.innerHTML = `${t(currentLanguage, 'currentType')}: ${configSource}`; infoContainer.appendChild(currentTypeInfo); const currentUrlInfo = document.createElement('div'); currentUrlInfo.style.cssText = ` font-size: 14px; color: #495057; `; currentUrlInfo.innerHTML = `${t(currentLanguage, 'currentUrl')}: ${currentDomain}`; infoContainer.appendChild(currentUrlInfo); domainSection.appendChild(infoContainer); const priorityInfo = document.createElement('div'); priorityInfo.textContent = t(currentLanguage, 'configPriority'); priorityInfo.style.cssText = ` margin-top: 12px; padding: 8px 12px; background: #e7f3ff; border-left: 3px solid #007bff; border-radius: 4px; font-size: 12px; color: #0056b3; `; domainSection.appendChild(priorityInfo); content.appendChild(domainSection); // 创建配置选项 for (const [eventType, eventList] of Object.entries(events)) { // 创建事件类型卡片 const typeCard = document.createElement('div'); typeCard.style.cssText = ` background: #f8f9fa; border-radius: 12px; padding: 15px; margin-bottom: 15px; border: 1px solid #e9ecef; transition: all 0.2s ease; `; typeCard.onmouseover = function () { this.style.boxShadow = '0 4px 12px rgba(0,0,0,0.08)'; this.style.transform = 'translateY(-2px)'; }; typeCard.onmouseout = function () { this.style.boxShadow = 'none'; this.style.transform = 'translateY(0)'; }; // 创建事件类型标题 const typeTitle = document.createElement('h3'); typeTitle.textContent = t(currentLanguage, eventType); typeTitle.style.cssText = ` margin: 0 0 12px 0; color: #495057; font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; `; // 添加图标 typeTitle.innerHTML = `${eventTypeIcons[eventType] || '•'} ${typeTitle.textContent}`; typeCard.appendChild(typeTitle); // 创建事件列表容器 const eventContainer = document.createElement('div'); eventContainer.style.cssText = ` display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; `; // 为每个事件创建复选框 eventList.forEach(eventName => { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = `event-${eventType}-${eventName}`; checkbox.checked = effectiveConfig[eventType] && effectiveConfig[eventType][eventName] || false; checkbox.style.cssText = ` display: none; `; const label = document.createElement('label'); label.htmlFor = `event-${eventType}-${eventName}`; label.textContent = t(currentLanguage, eventName); label.style.cssText = ` display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: white; border: 2px solid #dee2e6; border-radius: 8px; cursor: pointer; font-size: 13px; color: #495057; transition: all 0.2s ease; user-select: none; `; label.onmouseover = function () { this.style.borderColor = '#667eea'; }; label.onmouseout = function () { if (!checkbox.checked) { this.style.borderColor = '#dee2e6'; } }; // 创建自定义复选框 const customCheckbox = document.createElement('span'); customCheckbox.style.cssText = ` width: 18px; height: 18px; border: 2px solid #667eea; border-radius: 4px; background: white; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; flex-shrink: 0; `; // 添加选中标记 const checkmark = document.createElement('span'); checkmark.textContent = '✓'; checkmark.style.cssText = ` color: white; font-size: 14px; font-weight: bold; opacity: 0; transition: opacity 0.2s ease; `; customCheckbox.appendChild(checkmark); // 更新复选框状态 function updateCheckboxState() { if (checkbox.checked) { label.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; label.style.borderColor = '#667eea'; label.style.color = 'white'; customCheckbox.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'; customCheckbox.style.borderColor = 'rgba(255,255,255,0.3)'; checkmark.style.opacity = '1'; } else { label.style.background = 'white'; label.style.borderColor = '#dee2e6'; label.style.color = '#495057'; customCheckbox.style.background = 'white'; customCheckbox.style.borderColor = '#667eea'; checkmark.style.opacity = '0'; } } updateCheckboxState(); checkbox.addEventListener('change', updateCheckboxState); label.insertBefore(customCheckbox, label.firstChild); eventContainer.appendChild(checkbox); eventContainer.appendChild(label); }); typeCard.appendChild(eventContainer); content.appendChild(typeCard); } // 创建按钮容器 const buttonContainer = document.createElement('div'); buttonContainer.style.cssText = ` background: white; padding: 20px 25px; border-top: 1px solid #e9ecef; display: flex; justify-content: flex-end; gap: 12px; `; // 创建取消按钮 const cancelButton = document.createElement('button'); cancelButton.textContent = t(currentLanguage, 'cancel'); cancelButton.style.cssText = ` background: #f8f9fa; color: #6c757d; border: 2px solid #dee2e6; padding: 12px 28px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease; `; cancelButton.onmouseover = function () { this.style.background = '#e9ecef'; this.style.borderColor = '#ced4da'; }; cancelButton.onmouseout = function () { this.style.background = '#f8f9fa'; this.style.borderColor = '#dee2e6'; }; cancelButton.onclick = closeConfigWindow; const saveGlobalButton = document.createElement('button'); saveGlobalButton.textContent = t(currentLanguage, 'saveGlobal'); saveGlobalButton.style.cssText = ` background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 28px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); transition: all 0.2s ease; `; saveGlobalButton.onmouseover = function () { this.style.transform = 'translateY(-2px)'; this.style.boxShadow = '0 6px 16px rgba(102, 126, 234, 0.5)'; }; saveGlobalButton.onmouseout = function () { this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 4px 12px rgba(102, 126, 234, 0.4)'; }; const savePageButton = document.createElement('button'); savePageButton.textContent = t(currentLanguage, 'savePage'); savePageButton.style.cssText = ` background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; border: none; padding: 12px 28px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(40, 167, 69, 0.4); transition: all 0.2s ease; `; savePageButton.onmouseover = function () { this.style.transform = 'translateY(-2px)'; this.style.boxShadow = '0 6px 16px rgba(40, 167, 69, 0.5)'; }; savePageButton.onmouseout = function () { this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 4px 12px rgba(40, 167, 69, 0.4)'; }; buttonContainer.appendChild(cancelButton); buttonContainer.appendChild(saveGlobalButton); buttonContainer.appendChild(savePageButton); if (existingDomainConfig) { const deleteButton = document.createElement('button'); deleteButton.textContent = t(currentLanguage, 'deleteConfig'); deleteButton.style.cssText = ` background: #dc3545; color: white; border: none; padding: 12px 28px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(220, 53, 69, 0.4); transition: all 0.2s ease; `; deleteButton.onmouseover = function () { this.style.transform = 'translateY(-2px)'; this.style.boxShadow = '0 6px 16px rgba(220, 53, 69, 0.5)'; }; deleteButton.onmouseout = function () { this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 4px 12px rgba(220, 53, 69, 0.4)'; }; deleteButton.onclick = function () { if (confirm('确定要删除此配置吗?')) { deleteDomainConfig(existingDomainConfig); closeConfigWindow(); } }; buttonContainer.appendChild(deleteButton); } // 组装窗口 configWindow.appendChild(header); configWindow.appendChild(content); configWindow.appendChild(buttonContainer); // 创建遮罩层 overlay = document.createElement('div'); overlay.id = 'event-blocker-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); backdrop-filter: blur(4px); z-index: 9998; opacity: 0; transition: opacity 0.3s ease; `; overlay.onclick = closeConfigWindow; // 添加到页面 document.body.appendChild(overlay); document.body.appendChild(configWindow); // 触发动画 requestAnimationFrame(function () { if (configWindow && overlay) { configWindow.style.opacity = '1'; configWindow.style.transform = 'translate(-50%, -50%) scale(1)'; overlay.style.opacity = '1'; } }); // 添加滚动条样式 const style = document.createElement('style'); style.textContent = ` #event-blocker-config-window > div:nth-child(2)::-webkit-scrollbar { width: 8px; } #event-blocker-config-window > div:nth-child(2)::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } #event-blocker-config-window > div:nth-child(2)::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 4px; } #event-blocker-config-window > div:nth-child(2)::-webkit-scrollbar-thumb:hover { background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); } `; document.head.appendChild(style); // 设置配置窗口元素到事件处理模块 if (configWindow && overlay) { setConfigWindowElements(configWindow, overlay); // 设置配置窗口元素到增强模式模块 setEnhancedModeConfigWindowElements(configWindow, overlay); } // 绑定语言切换事件 langButton.onclick = function () { toggleLanguage(currentLanguage); closeConfigWindow(); createConfigWindow(effectiveConfig); }; saveGlobalButton.onclick = function () { removeDisabledEvents(); saveConfig(config); saveGlobalConfig(config); applyEnhancedMode(); closeConfigWindow(); }; savePageButton.onclick = function () { const domainKey = currentDomain; const configType = 'domain'; removeDisabledEvents(); saveConfig(config); saveDomainConfig(domainKey, config, configType); applyEnhancedMode(); closeConfigWindow(); }; if (configWindow && overlay) { return { configWindow, overlay }; } } (function () { // 确保GM_*函数存在(用于开发测试) if (typeof GM_getValue === 'undefined') { // 模拟GM_getValue函数 window.GM_getValue = function (key, defaultValue) { const value = localStorage.getItem(key); return value ? JSON.parse(value) : defaultValue; }; } if (typeof GM_setValue === 'undefined') { // 模拟GM_setValue函数 window.GM_setValue = function (key, value) { localStorage.setItem(key, JSON.stringify(value)); }; } if (typeof GM_registerMenuCommand === 'undefined') { // 模拟GM_registerMenuCommand函数 window.GM_registerMenuCommand = function (name, callback) { console.log('GM_registerMenuCommand:', name); }; } let config = getEffectiveConfig(); const currentLanguage = getCurrentLanguage(); function applyEnhancedMode() { config = getEffectiveConfig(); if (config.enhancedMode) { const enabledTypes = []; for (const [eventType, eventList] of Object.entries(events)) { eventList.forEach(eventName => { if (config[eventType] && config[eventType][eventName]) { enabledTypes.push(eventName); } }); } generalBlockEvents(config); initEnhancedEventBlocker(); enableEnhancedMode(enabledTypes); } else { disableEnhancedMode(); generalBlockEvents(config); } } // 注册菜单命令 GM_registerMenuCommand(t(currentLanguage, 'menuCommand'), function () { createConfigWindow(config); }); applyEnhancedMode(); // 监听DOMContentLoaded事件,确保在DOM加载完成后再次执行 document.addEventListener('DOMContentLoaded', function () { applyEnhancedMode(); }); // 监听load事件,确保在所有资源加载完成后再次执行 window.addEventListener('load', function () { applyEnhancedMode(); }); // 暴露全局变量,用于测试页面调用 window.createConfigWindow = createConfigWindow; window.currentLanguage = currentLanguage; window.config = config; })(); })();