// ==UserScript== // @name 解除复制粘贴限制(自用) // @namespace http://tampermonkey.net/ // @version 0.3 // @description 选择性移除页面上的事件监听器,保留编辑器功能(网站无法正常显示时请关闭脚本) // @author You // @match *://*/* // @exclude *://jsbin.com/* // @exclude *://*.scriptcat.*/* // @exclude *://scriptcat.*/* // @exclude *://codepen.*/* // @exclude *://stackblitz.*/* // @exclude *://codesandbox.*/* // @grant none // ==/UserScript== (function() { 'use strict'; const originalAddEventListener = EventTarget.prototype.addEventListener; const originalRemoveEventListener = EventTarget.prototype.removeEventListener; const editorSelectors = [ '.monaco-editor', // VSCode风格编辑器 '.CodeMirror', // CodeMirror编辑器 'textarea', // 普通文本域 '.ace_editor', // Ace编辑器 '.codepen-editor' // CodePen编辑器 ]; //此处添加白名单网站,白名单网站脚本停用,适用于脚本导致的网页显示不正常的情况 const whitelistDomains = [ 'scriptcat.org', 'docs.scriptcat.org', 'jsbin.com', 'codepen.io', 'stackblitz.com', 'codesandbox.io' ]; function isEditorElement(element) { if (!element) return false; if (editorSelectors.some(selector => element.matches(selector))) return true; return isEditorElement(element.parentElement); } function isWhitelistedDomain() { const host = window.location.hostname; return whitelistDomains.some(domain => host.includes(domain)); } const eventListeners = new WeakMap(); EventTarget.prototype.addEventListener = function(type, listener, options) { if (isWhitelistedDomain() || isEditorElement(this)) { return originalAddEventListener.call(this, type, listener, options); } if (!eventListeners.has(this)) { eventListeners.set(this, []); } eventListeners.get(this).push({ type, listener, options }); return originalAddEventListener.call(this, type, listener, options); }; EventTarget.prototype.removeEventListener = function(type, listener, options) { if (isWhitelistedDomain() || isEditorElement(this)) { return originalRemoveEventListener.call(this, type, listener, options); } if (eventListeners.has(this)) { const listeners = eventListeners.get(this); const index = listeners.findIndex(l => l.type === type && l.listener === listener && (l.options === options || (l.options && options && l.options.capture === options.capture)) ); if (index !== -1) { listeners.splice(index, 1); } } return originalRemoveEventListener.call(this, type, listener, options); }; function removeAllEventListeners() { if (isWhitelistedDomain()) { return; } try { const walk = document.createTreeWalker( document.body, NodeFilter.SHOW_ELEMENT, null, false ); const root = document.body; if (!isEditorElement(root) && eventListeners.has(root)) { const listeners = [...eventListeners.get(root)]; listeners.forEach(listener => { originalRemoveEventListener.call(root, listener.type, listener.listener, listener.options); }); eventListeners.delete(root); } while (walk.nextNode()) { const node = walk.currentNode; if (!isEditorElement(node) && eventListeners.has(node)) { const listeners = [...eventListeners.get(node)]; listeners.forEach(listener => { originalRemoveEventListener.call(node, listener.type, listener.listener, listener.options); }); eventListeners.delete(node); } } } catch (error) { } } function removeGlobalEventHandlers() { if (isWhitelistedDomain()) { return; } try { if (!isEditorElement(document)) { document.oncopy = null; document.oncut = null; document.onpaste = null; document.onselectstart = null; document.oncontextmenu = null; } if (!isEditorElement(document.body)) { document.body.oncopy = null; document.body.oncut = null; document.body.onpaste = null; document.body.onselectstart = null; document.body.oncontextmenu = null; } window.oncopy = null; window.oncut = null; window.onpaste = null; window.onselectstart = null; window.oncontextmenu = null; } catch (error) { } } function removeAntiCopyCSS() { if (isWhitelistedDomain()) { return; } try { const style = document.createElement('style'); style.textContent = ` *:not(textarea):not(.monaco-editor):not(.CodeMirror):not(.ace_editor):not(.codepen-editor) { user-select: text !important; -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; pointer-events: auto !important; } [oncopy]:not(textarea):not(.monaco-editor):not(.CodeMirror):not(.ace_editor):not(.codepen-editor), [oncut]:not(textarea):not(.monaco-editor):not(.CodeMirror):not(.ace_editor):not(.codepen-editor), [onpaste]:not(textarea):not(.monaco-editor):not(.CodeMirror):not(.ace_editor):not(.codepen-editor), [onselectstart]:not(textarea):not(.monaco-editor):not(.CodeMirror):not(.ace_editor):not(.codepen-editor), [oncontextmenu]:not(textarea):not(.monaco-editor):not(.CodeMirror):not(.ace_editor):not(.codepen-editor) { oncopy="return true;" oncut="return true;" onpaste="return true;" onselectstart="return true;" oncontextmenu="return true;" } `; document.head.appendChild(style); } catch (error) { } } function enableInteractions() { if (isWhitelistedDomain()) { return; } try { const eventTypes = ['contextmenu', 'copy', 'cut', 'paste', 'selectstart']; eventTypes.forEach(type => { window.addEventListener(type, function(e) { if (!isEditorElement(e.target)) { e.stopPropagation(); } }, true); }); } catch (error) { } } window.addEventListener('load', function() { if (!isWhitelistedDomain()) { removeAllEventListeners(); removeGlobalEventHandlers(); removeAntiCopyCSS(); enableInteractions(); } else { } // 添加快捷键重新执行功能(Ctrl+Shift+R) document.addEventListener('keydown', function(e) { if (e.ctrlKey && e.shiftKey && e.keyCode === 82 && !isWhitelistedDomain()) { e.preventDefault(); removeAllEventListeners(); removeGlobalEventHandlers(); removeAntiCopyCSS(); enableInteractions(); } }); }); const observer = new MutationObserver(function(mutations) { if (isWhitelistedDomain()) return; mutations.forEach(function(mutation) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { for (let i = 0; i < mutation.addedNodes.length; i++) { const node = mutation.addedNodes[i]; if (node.nodeType === 1 && !isEditorElement(node)) { if (eventListeners.has(node)) { const listeners = [...eventListeners.get(node)]; listeners.forEach(listener => { originalRemoveEventListener.call(node, listener.type, listener.listener, listener.options); }); eventListeners.delete(node); } } } } }); }); if (!isWhitelistedDomain()) { observer.observe(document.body, { childList: true, subtree: true }); } })();