// ==UserScript== // @name Universal Web Liberator // @name:zh-CN 网页枷锁破除 // @name:zh-TW 網頁枷鎖破除 // @description Regain Control Unlocks RightClick/Selection/CopyPaste/Drag On Any Website // @description:zh-CN 解除网页右键/选择/复制及拖拽限制 恢复自由交互体验 // @description:zh-TW 解除網頁右鍵/選取/複製及拖曳限制 恢復自由互動體驗 // @version 1.1.2 // @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/refs/heads/main/Icons/UniversalWebLiberatorIcon.svg // @author 念柚 // @namespace https://github.com/MiPoNianYou/UserScripts // @supportURL https://github.com/MiPoNianYou/UserScripts/issues // @license GPL-3.0 // @match *://*/* // @grant none // @run-at document-start // @downloadURL none // ==/UserScript== class WebLiberator { static EventsToStop = ["contextmenu", "selectstart", "copy", "cut", "paste"]; static RestrictedEventProps = [ "oncontextmenu", "onselectstart", "oncopy", "oncut", "onpaste", "ondrag", "ondragstart", ]; constructor() { this.InitMutationObserver(); this.ExecuteCoreFeatures(); } ExecuteCoreFeatures() { this.PurgeEventListeners(document.documentElement); this.InjectLiberationStyles(); this.BindGlobalEvents(); this.ProcessExistingNodes(); } StopImmediatePropagationHandler(event) { event.stopImmediatePropagation(); } BindGlobalEvents() { WebLiberator.EventsToStop.forEach((type) => { document.addEventListener( type, this.StopImmediatePropagationHandler, true ); }); } InjectLiberationStyles() { const StyleSheet = new CSSStyleSheet(); StyleSheet.replaceSync(` *, *::before, *::after { user-select: text !important; -webkit-user-drag: auto !important; user-drag: auto !important; } `); document.adoptedStyleSheets = [...document.adoptedStyleSheets, StyleSheet]; } ProcessExistingNodes() { requestIdleCallback(() => { if (document.body) { this.PurgeEventListeners(document.body); const elements = document.body.querySelectorAll("*"); for (const element of elements) { this.PurgeEventListeners(element); } } }); } PurgeEventListeners(element) { if (!element || element.nodeType !== Node.ELEMENT_NODE) return; for (const prop of WebLiberator.RestrictedEventProps) { element[prop] = null; } } HandleMutation(mutations) { for (const mutation of mutations) { if (mutation.type === "childList") { for (const node of mutation.addedNodes) { if (node.nodeType === Node.ELEMENT_NODE) { this.PurgeEventListeners(node); const descendants = node.querySelectorAll("*"); for (const child of descendants) { this.PurgeEventListeners(child); } } } } } } InitMutationObserver() { this.observer = new MutationObserver(this.HandleMutation.bind(this)); this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: false, }); } } new WebLiberator();