// ==UserScript== // @name 🛠️网页限制解除 // @name:en 🛠️Remove web limits // @name:zh 🛠️网页限制解除 // @name:zh-CN 🛠️网页限制解除 // @name:zh-TW 🛠️網頁限制解除 // @name:ja 🛠️ウェブの規制緩和 // @description 通杀大部分网站,可以解除禁止复制、剪切、选择文本、右键菜单的限制。 // @description:en Pass to kill most of the site, you can lift the restrictions prohibited to copy, cut, select the text, right-click menu. // @description:zh 通杀大部分网站,可以解除禁止复制、剪切、选择文本、右键菜单的限制。 // @description:zh-CN 通杀大部分网站,可以解除禁止复制、剪切、选择文本、右键菜单的限制。 // @description:zh-TW 通殺大部分網站,可以解除禁止復制、剪切、選擇文本、右鍵菜單的限制。 // @description:ja サイトのほとんどを殺すために渡し、あなたは、コピー切り取り、テキスト、右クリックメニューを選択することは禁止の制限を解除することができます // @author 六斤八两 // @version 1.0.1 // @license LGPLv3 // @match *://*/* // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @require https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.js // @run-at document-start // @namespace https://greasyfork.org/users/1446006 // @downloadURL https://update.greasyfork.icu/scripts/535746/%F0%9F%9B%A0%EF%B8%8F%E7%BD%91%E9%A1%B5%E9%99%90%E5%88%B6%E8%A7%A3%E9%99%A4.user.js // @updateURL https://update.greasyfork.icu/scripts/535746/%F0%9F%9B%A0%EF%B8%8F%E7%BD%91%E9%A1%B5%E9%99%90%E5%88%B6%E8%A7%A3%E9%99%A4.meta.js // ==/UserScript== (function() { 'use strict'; // 域名规则列表 var rules = { black_rule: { name: "black", hook_eventNames: "", unhook_eventNames: "" }, default_rule: { name: "default", hook_eventNames: "contextmenu|select|selectstart|copy|cut|dragstart", unhook_eventNames: "keydown|keyup", dom0: true, hook_addEventListener: true, hook_preventDefault: true, hook_set_returnValue: true, add_css: true } }; // 站点模式与存储 var MODES = { standard: 'standard', light: 'light', friendly: 'friendly', disabled: 'disabled' }; var MODE_LABELS = { standard: '标准模式', light: '轻量模式', friendly: '友好模式', disabled: '禁用' }; var siteModes = GM_getValue('site_modes', {}); function getSiteMode(host) { return siteModes[host] || ''; } function setSiteMode(host, mode) { siteModes[host] = mode; GM_setValue('site_modes', siteModes); } // 预设友好模式的常见视频网站后缀 var FRIENDLY_SUFFIXES = [ 'bilibili.com', 'iqiyi.com', 'youku.com', 'v.qq.com', 'video.qq.com', 'mgtv.com', 'acfun.cn', 'sohu.com', 'tv.sohu.com', 'pptv.com', 'le.com', 'tudou.com', 'youtube.com' ]; function getDefaultModeForHost(host) { for (var i = 0; i < FRIENDLY_SUFFIXES.length; i++) { var suf = FRIENDLY_SUFFIXES[i]; if (host === suf || host.slice(-suf.length - 1) === '.' + suf || host.slice(-suf.length) === suf) { return MODES.friendly; } } return ''; } // 域名列表(增加用户排除列表存储) var lists = { // 用户自定义排除列表 // 基础黑名单(不可删除) + 用户自定义排除列表 base_blacklist: GM_getValue('base_blacklist', [ 'youtube.com', 'wikipedia.org', 'mail.qq.com', 'translate.google.com' ]), // 保留给规则匹配的黑名单(正则表达式),默认为空 black_list: [], // 合并后的排除列表(仅用于显示) exclude_list: function() { return this.base_blacklist.concat(GM_getValue('exclude_list', [])) .filter((v, i, a) => a.indexOf(v) === i); } }; // 要处理的 event 列表 var hook_eventNames, unhook_eventNames, eventNames; // 全局状态(供弹窗展示) var g_currentMode = 'standard'; var g_rule = null; // 储存名称 var storageName = getRandStr('qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM', parseInt(Math.random() * 12 + 8)); // 储存被 Hook 的函数 var EventTarget_addEventListener = EventTarget.prototype.addEventListener; var document_addEventListener = document.addEventListener; var Event_preventDefault = Event.prototype.preventDefault; var originalReturnValueDescriptor = Object.getOwnPropertyDescriptor(Event.prototype, 'returnValue'); // Hook addEventListener proc function addEventListener(type, func, useCapture) { var _addEventListener = this === document ? document_addEventListener : EventTarget_addEventListener; if(hook_eventNames.indexOf(type) >= 0) { _addEventListener.apply(this, [type, returnTrue, useCapture]); } else if(this && unhook_eventNames.indexOf(type) >= 0) { var funcsName = storageName + type + (useCapture ? 't' : 'f'); if(this[funcsName] === undefined) { this[funcsName] = []; _addEventListener.apply(this, [type, useCapture ? unhook_t : unhook_f, useCapture]); } this[funcsName].push(func); } else { _addEventListener.apply(this, arguments); } } // 清理循环 function clearLoop() { var elements = getElements(); for(var i in elements) { for(var j in eventNames) { var name = 'on' + eventNames[j]; if(elements[i][name] !== null && elements[i][name] !== onxxx) { if(unhook_eventNames.indexOf(eventNames[j]) >= 0) { elements[i][storageName + name] = elements[i][name]; elements[i][name] = onxxx; } else { elements[i][name] = null; } } } } } // 使用 MutationObserver 增量清理 DOM0 事件 function cleanseElement(root) { if(!root) return; var nodes = [root]; if(root.querySelectorAll) { nodes = nodes.concat(Array.prototype.slice.call(root.querySelectorAll('*'))); } for(var i = 0; i < nodes.length; i++) { var el = nodes[i]; for(var j in eventNames) { var evt = eventNames[j]; var name = 'on' + evt; if(el[name] !== null && el[name] !== onxxx) { if(unhook_eventNames.indexOf(evt) >= 0) { el[storageName + name] = el[name]; el[name] = onxxx; } else { el[name] = null; } } } } } function setupDom0Cleaner() { cleanseElement(document); try { var observer = new MutationObserver(function(mutations) { for(var i = 0; i < mutations.length; i++) { var m = mutations[i]; for(var k = 0; k < m.addedNodes.length; k++) { var node = m.addedNodes[k]; if(node && node.nodeType === 1) { cleanseElement(node); } } } }); observer.observe(document.documentElement, { childList: true, subtree: true }); window.addEventListener('load', function() { cleanseElement(document); }, true); } catch(e) { // 降级到原循环 setInterval(clearLoop, 30 * 1000); setTimeout(clearLoop, 2500); window.addEventListener('load', clearLoop, true); clearLoop(); } } // 轻量模式:仅在捕获阶段阻断常见拦截事件的传播 function enableLightModeCapture() { var captureTypes = ['contextmenu', 'copy', 'cut', 'selectstart', 'dragstart']; function stopAll(e) { e.stopImmediatePropagation(); e.stopPropagation(); } for(var i = 0; i < captureTypes.length; i++) { var t = captureTypes[i]; window.addEventListener(t, stopAll, true); document.addEventListener(t, stopAll, true); } } // 返回true的函数 function returnTrue(e) { return true; } function unhook_t(e) { return unhook(e, this, storageName + e.type + 't'); } function unhook_f(e) { return unhook(e, this, storageName + e.type + 'f'); } function unhook(e, self, funcsName) { var list = self[funcsName]; for(var i in list) { list[i](e); } e.returnValue = true; return true; } function onxxx(e) { var name = storageName + 'on' + e.type; this[name](e); e.returnValue = true; return true; } // 获取随机字符串 function getRandStr(chs, len) { var str = ''; while(len--) { str += chs[parseInt(Math.random() * chs.length)]; } return str; } // 获取所有元素 包括document function getElements() { var elements = Array.prototype.slice.call(document.getElementsByTagName('*')); elements.push(document); return elements; } // 添加css function addStyle(css) { var style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); } // 获取目标域名应该使用的规则 function getRule(url) { function testUrl(list, url) { for(var i in list) { if(list[i].test(url)) { return true; } } return false; } if(testUrl(lists.black_list, url)) { return rules.black_rule; } return rules.default_rule; } // 初始化(始终注册菜单) function init() { // 注册菜单项 const isExcluded = lists.exclude_list().includes(location.host); // 计算当前模式(即使被排除也计算,用于菜单显示) var host = location.hostname; var defaultMode = getDefaultModeForHost(host) || MODES.light; var currentModeForMenu = isExcluded ? MODES.disabled : (getSiteMode(host) || defaultMode); g_currentMode = currentModeForMenu; g_rule = rules.default_rule; GM_registerMenuCommand(`当前网站:${isExcluded ? '❌' : '✔️'}`, () => { const currentList = lists.exclude_list(); const newList = isExcluded ? currentList.filter(h => h !== location.host) : [...currentList, location.host]; GM_setValue('exclude_list', newList); window.location.reload(); }); // 状态显示弹窗 GM_registerMenuCommand('📜 当前状态', () => { createPopup(); }); // 菜单:显示/切换模式(打开面板),始终注册,并使用中文标签 GM_registerMenuCommand(`站点模式:${MODE_LABELS[currentModeForMenu] || currentModeForMenu}`, () => { createPopup(); }); // 如果当前网站在排除列表中则不执行后续逻辑 if (isExcluded) { g_currentMode = MODES.disabled; g_rule = rules.default_rule; return; } // 获取当前域名的规则 var url = window.location.host + window.location.pathname; var rule = getRule(url); // host/defaultMode 已在上面计算 var currentMode = getSiteMode(host) || defaultMode; g_currentMode = currentMode; g_rule = rule; // 按模式调整策略 if (currentMode === MODES.disabled) { return; // 完全不影响页面 } else if (currentMode === MODES.friendly) { rule.unhook_eventNames = ""; rule.hook_addEventListener = false; rule.hook_preventDefault = false; rule.hook_set_returnValue = false; rule.dom0 = false; rule.hook_eventNames = ""; // 仅保留 CSS 放行 } else if (currentMode === MODES.light) { rule.unhook_eventNames = ""; rule.hook_addEventListener = false; rule.hook_preventDefault = false; rule.hook_set_returnValue = false; rule.dom0 = false; enableLightModeCapture(); } // 设置 event 列表 hook_eventNames = rule.hook_eventNames.split("|").filter(Boolean); // TODO Allowed to return value unhook_eventNames = rule.unhook_eventNames.split("|").filter(Boolean); eventNames = hook_eventNames.concat(unhook_eventNames); // 调用清理 DOM0 event 方法的循环 if(rule.dom0) { setupDom0Cleaner(); } // hook addEventListener if(rule.hook_addEventListener) { EventTarget.prototype.addEventListener = addEventListener; document.addEventListener = addEventListener; } // hook preventDefault if(rule.hook_preventDefault) { Event.prototype.preventDefault = function() { if(eventNames.indexOf(this.type) < 0) { Event_preventDefault.apply(this, arguments); } }; } // Hook set returnValue if(rule.hook_set_returnValue) { try { Object.defineProperty(Event.prototype, 'returnValue', { configurable: true, enumerable: false, set: function(v) { if(eventNames.indexOf(this.type) >= 0 && v !== true) { return; // 忽略将其设为 false 的尝试 } if(originalReturnValueDescriptor && originalReturnValueDescriptor.set) { originalReturnValueDescriptor.set.call(this, v); } }, get: function() { if(originalReturnValueDescriptor && originalReturnValueDescriptor.get) { return originalReturnValueDescriptor.get.call(this); } return true; } }); } catch(e) { // 退回旧方案 Event.prototype.__defineSetter__('returnValue', function() { if(this.returnValue !== true && eventNames.indexOf(this.type) >= 0) { this.returnValue = true; } }); } } console.debug('url: ' + url, 'storageName:' + storageName, 'rule: ' + rule.name); // 添加CSS if(rule.add_css) { addStyle(` html, * { -webkit-user-select:text!important; -moz-user-select:text!important; user-select:text!important; -ms-user-select:text!important; -khtml-user-select:text!important; } /* 悬浮窗样式 */ #rml-popup { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 480px; height: 318px; min-height: 340px; padding-bottom: 40px; background: #2d2d2d; border: 1px solid #444; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 9999; font-family: "微软雅黑", sans-serif; padding: 20px; cursor: grab; user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; backdrop-filter: blur(4px); touch-action: none; overflow: auto; color: #fff; } #rml-popup * { user-select: none !important; -webkit-user-select: none !important; -moz-user-select: none !important; -ms-user-select: none !important; } #rml-popup:active { cursor: grabbing; box-shadow: 0 8px 30px rgba(0,0,0,0.3); } .window-controls { position: absolute; top: 5px; right: 5px; display: flex; gap: 8px; } .control-btn { width: 24px; height: 24px; border-radius: 50%; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 14px; color: white; transition: transform 0.2s ease, opacity 0.2s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.2); } .close-btn { background: #ff5f57; } .min-btn { background: #ffbd2e; } .max-btn { background: #28c940; } .control-btn:hover { filter: brightness(0.9); } .collapsed { height: 60px !important; overflow: hidden; opacity: 0.8; } .collapsed .input-group, .collapsed .platform-buttons, .collapsed .donate-section, .collapsed .warning { display: none; } .collapsed .window-controls { top: 18px; } `); } // 创建控制弹窗 function createPopup() { // 创建容器并添加控制按钮 const container = document.createElement('div'); container.id = 'rml-popup'; const modeLabelMap = { standard: '标准模式', light: '轻量模式', friendly: '友好模式', disabled: '禁用' }; const modeLabel = modeLabelMap[g_currentMode] || g_currentMode || '标准模式'; const captureList = ['contextmenu','copy','cut','selectstart','dragstart']; const EVENT_LABELS = { contextmenu: '右键菜单', copy: '复制', cut: '剪切', select: '文本选择', selectstart: '选择开始', dragstart: '拖拽开始', keydown: '按键按下', keyup: '按键抬起', mousedown: '鼠标按下', mouseup: '鼠标抬起' }; function labelEvent(name){ return EVENT_LABELS[name] || name; } function mapEvents(list){ return (list && list.length) ? list.map(labelEvent).join(',') : '无'; } let processedEventsText = ''; if (g_currentMode === MODES.light) { processedEventsText = mapEvents(captureList); } else if (g_currentMode === MODES.friendly) { processedEventsText = '无(仅 CSS 放行)'; } else if (g_currentMode === MODES.disabled) { processedEventsText = '无(已禁用)'; } else { processedEventsText = mapEvents(eventNames || []); } let featureList = []; if (g_currentMode === MODES.disabled) { featureList = ['已禁用']; } else if (g_currentMode === MODES.friendly) { if (g_rule && g_rule.add_css) featureList.push('CSS 放行选择'); featureList.push('最小侵入(不 Hook 事件)'); } else if (g_currentMode === MODES.light) { if (g_rule && g_rule.add_css) featureList.push('CSS 放行选择'); featureList.push('捕获期阻断常见拦截事件'); } else { // 标准模式 if (g_rule && g_rule.add_css) featureList.push('CSS 放行选择'); if (g_rule && g_rule.hook_addEventListener) featureList.push('Hook addEventListener'); if (g_rule && g_rule.hook_preventDefault) featureList.push('过滤 preventDefault'); if (g_rule && g_rule.hook_set_returnValue) featureList.push('保护 returnValue'); if (g_rule && g_rule.dom0) featureList.push('清理内联 on* 事件'); } const featuresText = featureList.join('、'); container.innerHTML = `