// ==UserScript==
// @name 事件屏蔽器
// @namespace http://tampermonkey.net/
// @version 1.11
// @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;
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);
}
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);
// 组装窗口
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;
})();
})();