您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
解除网页右键菜单/选择文本/拷贝粘贴/拖拽内容限制 恢复自由交互体验 轻点右下角灵动指示器 | 使用快捷键 Meta/Ctrl+Alt+L | 脚本菜单切换状态
// ==UserScript== // @name Universal Web Liberator // @name:zh-CN 网页枷锁破除 // @name:zh-TW 網頁枷鎖破除 // @description Disable webpage restrictions on Right-Click / Selection / Copy / Drag | Restore a seamless interactive experience | Tap the dynamic indicator in the bottom-right corner | Use the shortcut Meta/Ctrl+Alt+L | Toggle state via the userscript menu // @description:zh-CN 解除网页右键菜单/选择文本/拷贝粘贴/拖拽内容限制 恢复自由交互体验 轻点右下角灵动指示器 | 使用快捷键 Meta/Ctrl+Alt+L | 脚本菜单切换状态 // @description:zh-TW 解除網頁右鍵選單/選取文字/複製貼上/拖曳內容限制 恢復自由互動體驗 輕點右下角靈動指示器 | 使用快捷鍵 Meta/Ctrl+Alt+L | 腳本選單切換狀態 // @version 1.5.5 // @icon https://raw.githubusercontent.com/MiPoNianYou/UserScripts/main/Icons/Universal-Web-Liberator-Icon.svg // @author 念柚 // @namespace https://github.com/MiPoNianYou/UserScripts // @supportURL https://github.com/MiPoNianYou/UserScripts/issues // @license AGPL-3.0 // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @run-at document-start // ==/UserScript== (function () { "use strict"; const UIManager = { ELEMENT_IDS: { STATUS_NOTIFICATION: "WebLiberatorStatusNotification", DYNAMIC_INDICATOR: "WebLiberatorDynamicIndicator", OVERRIDE_STYLE_SHEET: "WebLiberatorOverrideStyleSheet", }, CSS_CLASSES: { STATUS_NOTIFICATION_VISIBLE: "wl-status-notification--visible", STATUS_NOTIFICATION_DISABLED: "wl-status-notification--disabled", BREATHING_DOT: "wl-breathing-dot", STATUS_NOTIFICATION_MESSAGE: "wl-status-notification-message", DYNAMIC_INDICATOR_EXPANDED: "wl-dynamic-indicator--expanded", DYNAMIC_INDICATOR_ACTIVE: "wl-dynamic-indicator--active", }, SETTINGS: { UI_FONT_STACK: "-apple-system, BlinkMacSystemFont, system-ui, sans-serif", ANIMATION_EASING_QUINT: "cubic-bezier(0.23, 1, 0.32, 1)", ANIMATION_EASING_APPLE_STANDARD: "cubic-bezier(0, 0, 0.58, 1)", ANIMATION_DURATION_MS: 300, NOTIFICATION_VISIBILITY_DURATION_MS: 2000, INDICATOR_EXPANDED_DURATION_MS: 2000, INDICATOR_TRANSITION_DURATION_S: "0.25s", ICON_TRANSITION_DURATION_S: "0.2s", ICON_TRANSITION_DELAY_S: "0.1s", }, STRINGS: { "zh-CN": { STATUS_ENABLED: "枷锁限制破除", STATUS_DISABLED: "枷锁限制恢复", MENU_CMD_ENABLED: "枷锁限制破除已启用 ✅", MENU_CMD_DISABLED: "枷锁限制破除已禁用 ❌", }, "zh-TW": { STATUS_ENABLED: "枷鎖限制破除", STATUS_DISABLED: "枷鎖限制恢復", MENU_CMD_ENABLED: "枷鎖限制破除已啟用 ✅", MENU_CMD_DISABLED: "枷鎖限制破除已禁用 ❌", }, "en-US": { STATUS_ENABLED: "Restrictions Bypassed", STATUS_DISABLED: "Restrictions Restored", MENU_CMD_ENABLED: "Restrictions Bypassed Activated ✅", MENU_CMD_DISABLED: "Restrictions Bypassed Deactivated ❌", }, }, SVG_ICONS: { DYNAMIC_INDICATOR: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 82.129 104.736"><path d="M22.46 67.969h37.306c1.757 0 3.027-1.367 3.027-3.076 0-1.66-1.27-2.93-3.027-2.93H22.46c-1.758 0-3.076 1.27-3.076 2.93 0 1.709 1.318 3.076 3.076 3.076m0 14.648h17.628c1.758 0 3.027-1.27 3.027-2.93 0-1.709-1.27-3.076-3.027-3.076H22.46c-1.758 0-3.076 1.367-3.076 3.076 0 1.66 1.318 2.93 3.076 2.93m8.058-32.47h3.808c.342 0 .586.146.88.439l2.733 2.637c2.149 2.246 4.2 2.197 6.348 0l2.735-2.637c.293-.244.488-.44.878-.44h3.76c3.076 0 4.541-1.416 4.541-4.443v-3.809c0-.341.049-.634.342-.878l2.685-2.735c2.198-2.148 2.198-4.199 0-6.396l-2.685-2.686c-.293-.244-.342-.537-.342-.879v-3.808c0-3.076-1.416-4.492-4.54-4.492H47.9c-.391 0-.587-.196-.88-.391l-2.734-2.637c-2.148-2.246-4.2-2.197-6.347 0l-2.735 2.637c-.244.293-.537.39-.879.39h-3.808c-3.077 0-4.541 1.368-4.541 4.493v3.808c0 .342-.05.635-.342.88l-2.686 2.685c-2.197 2.197-2.197 4.248 0 6.396l2.686 2.735c.293.244.342.537.342.879v3.808c0 3.027 1.464 4.444 4.54 4.444m8.593-5.909c-.732 0-1.367-.341-2.002-1.123l-4.394-5.37c-.342-.392-.586-.929-.586-1.466 0-1.123.83-2.1 2.1-2.1.683 0 1.22.343 1.806 1.026l3.028 3.809 6.982-11.182c.39-.635 1.025-1.123 1.758-1.123 1.074 0 2.1.781 2.1 1.953 0 .537-.294 1.123-.538 1.514l-8.203 12.89c-.488.83-1.22 1.172-2.05 1.172M0 89.404c0 10.205 5.03 15.284 15.137 15.284h51.855c10.108 0 15.137-5.079 15.137-15.284V15.332C82.129 5.176 77.099 0 66.992 0H15.137C5.029 0 0 5.176 0 15.332Zm7.861-.146v-73.78c0-4.882 2.588-7.617 7.666-7.617h51.075c5.078 0 7.666 2.735 7.666 7.618v73.779c0 4.883-2.588 7.568-7.666 7.568H15.527c-5.078 0-7.666-2.685-7.666-7.568" fill="currentColor"/></svg>`.trim(), }, statusNotificationTimer: null, statusNotificationRemovalTimer: null, dynamicIndicatorElement: null, indicatorCollapseTimer: null, userScriptMenuCommandId: null, injectCoreStyles() { const easeOutQuint = this.SETTINGS.ANIMATION_EASING_QUINT; const appleEaseOutStandard = this.SETTINGS.ANIMATION_EASING_APPLE_STANDARD; const animationDurationMs = this.SETTINGS.ANIMATION_DURATION_MS; const indicatorTransitionDuration = this.SETTINGS.INDICATOR_TRANSITION_DURATION_S; const iconTransitionDuration = this.SETTINGS.ICON_TRANSITION_DURATION_S; const iconTransitionDelay = this.SETTINGS.ICON_TRANSITION_DELAY_S; const baseCSS = ` :root { --ctp-frappe-red: rgb(231, 130, 132); --ctp-frappe-green: rgb(166, 209, 137); --ctp-frappe-text: rgb(198, 208, 245); --ctp-frappe-surface1: rgb(81, 87, 109); --ctp-frappe-surface2: rgb(98, 104, 128); --ctp-frappe-base: rgb(48, 52, 70); --ctp-frappe-crust: rgb(35, 38, 52); --ctp-latte-red: rgb(210, 15, 57); --ctp-latte-green: rgb(64, 160, 43); --ctp-latte-text: rgb(76, 79, 105); --ctp-latte-overlay0: rgb(156, 160, 176); --ctp-latte-surface1: rgb(188, 192, 204); --ctp-latte-surface2: rgb(172, 176, 190); --ctp-latte-base: rgb(239, 241, 245); --wl-notify-bg-dark: rgb(from var(--ctp-frappe-base) r g b / 0.85); --wl-notify-text-dark: var(--ctp-frappe-text); --wl-notify-border-dark: rgb(from var(--ctp-frappe-surface2) r g b / 0.25); --wl-notify-dot-color-enabled-dark: var(--ctp-frappe-green); --wl-notify-dot-color-disabled-dark: var(--ctp-frappe-red); --wl-notify-dot-glow-enabled-dark: rgb(from var(--ctp-frappe-green) r g b / 0.35); --wl-notify-dot-glow-disabled-dark: rgb(from var(--ctp-frappe-red) r g b / 0.35); --wl-indicator-inactive-bg-dark: var(--ctp-frappe-surface2); --wl-indicator-inactive-opacity-dark: 0.55; --wl-indicator-hover-bg-dark: rgb(from var(--ctp-frappe-surface1) r g b / 0.5); --wl-indicator-expanded-bg-dark: rgb(from var(--ctp-frappe-base) r g b / 0.85); --wl-indicator-active-bg-dark: rgb(from var(--ctp-frappe-green) r g b / 0.88); --wl-indicator-border-dark: rgb(from var(--ctp-frappe-surface2) r g b / 0.2); --wl-icon-color-dark: var(--ctp-frappe-text); --wl-icon-active-color-dark: var(--ctp-frappe-crust); --wl-notify-bg-light: rgb(from var(--ctp-latte-base) r g b / 0.85); --wl-notify-text-light: var(--ctp-latte-text); --wl-notify-border-light: rgb(from var(--ctp-latte-surface2) r g b / 0.25); --wl-notify-dot-color-enabled-light: var(--ctp-latte-green); --wl-notify-dot-color-disabled-light: var(--ctp-latte-red); --wl-notify-dot-glow-enabled-light: rgb(from var(--ctp-latte-green) r g b / 0.35); --wl-notify-dot-glow-disabled-light: rgb(from var(--ctp-latte-red) r g b / 0.35); --wl-indicator-inactive-bg-light: var(--ctp-latte-overlay0); --wl-indicator-inactive-opacity-light: 0.65; --wl-indicator-hover-bg-light: rgb(from var(--ctp-latte-surface1) r g b / 0.5); --wl-indicator-expanded-bg-light: rgb(from var(--ctp-latte-base) r g b / 0.85); --wl-indicator-active-bg-light: rgb(from var(--ctp-latte-green) r g b / 0.88); --wl-indicator-border-light: rgb(from var(--ctp-latte-surface2) r g b / 0.2); --wl-icon-color-light: var(--ctp-latte-text); --wl-icon-active-color-light: var(--ctp-latte-base); --wl-shadow-dark: 0 1px 2px rgba(0, 0, 0, 0.1), 0 6px 12px rgba(0, 0, 0, 0.2); --wl-shadow-light: 0 1px 2px rgba(90, 90, 90, 0.06), 0 6px 12px rgba(90, 90, 90, 0.12); } @keyframes wl-breathing-animation { 0%, 100% { transform: scale(0.85); opacity: 0.7; } 50% { transform: scale(1); opacity: 1; } } #${this.ELEMENT_IDS.STATUS_NOTIFICATION} { position: fixed; bottom: 20px; left: 50%; z-index: 2147483646; display: flex; align-items: center; padding: 10px 16px; border: 1px solid var(--wl-notify-border-dark); border-radius: 20px; background-color: var(--wl-notify-bg-dark); color: var(--wl-notify-text-dark); box-shadow: var(--wl-shadow-dark); box-sizing: border-box; opacity: 0; font-family: ${this.SETTINGS.UI_FONT_STACK}; text-align: left; backdrop-filter: blur(16px) saturate(180%); -webkit-backdrop-filter: blur(16px) saturate(180%); transform: translate(-50%, calc(100% + 40px)); transition: transform ${animationDurationMs}ms ${easeOutQuint}, opacity ${animationDurationMs * 0.8}ms ${easeOutQuint}; } #${this.ELEMENT_IDS.STATUS_NOTIFICATION}.${ this.CSS_CLASSES.STATUS_NOTIFICATION_VISIBLE } { transform: translate(-50%, 0); opacity: 1; } #${this.ELEMENT_IDS.STATUS_NOTIFICATION} .${ this.CSS_CLASSES.BREATHING_DOT } { width: 8px; height: 8px; margin-right: 10px; border-radius: 50%; background-color: var(--wl-notify-dot-color-enabled-dark); box-shadow: 0 0 8px 3px var(--wl-notify-dot-glow-enabled-dark); flex-shrink: 0; animation: wl-breathing-animation 2000ms ease-in-out infinite; transition: background-color 0.3s ease, box-shadow 0.3s ease; } #${this.ELEMENT_IDS.STATUS_NOTIFICATION}.${ this.CSS_CLASSES.STATUS_NOTIFICATION_DISABLED } .${this.CSS_CLASSES.BREATHING_DOT} { background-color: var(--wl-notify-dot-color-disabled-dark); box-shadow: 0 0 8px 3px var(--wl-notify-dot-glow-disabled-dark); } #${this.ELEMENT_IDS.STATUS_NOTIFICATION} .${ this.CSS_CLASSES.STATUS_NOTIFICATION_MESSAGE } { color: var(--wl-notify-text-dark); font-size: 13px; font-weight: 500; line-height: 1.2; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR} { position: fixed; bottom: 20px; right: 20px; z-index: 2147483647; display: flex; align-items: center; justify-content: center; width: 12px; height: 12px; border-radius: 50%; background-color: var(--wl-indicator-inactive-bg-dark); color: var(--wl-icon-color-dark); opacity: var(--wl-indicator-inactive-opacity-dark); overflow: hidden; cursor: pointer; backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); transition: width ${indicatorTransitionDuration} ${appleEaseOutStandard}, height ${indicatorTransitionDuration} ${appleEaseOutStandard}, opacity ${indicatorTransitionDuration} ${appleEaseOutStandard}, background-color ${indicatorTransitionDuration} ${appleEaseOutStandard}, color ${indicatorTransitionDuration} ${appleEaseOutStandard}, backdrop-filter ${indicatorTransitionDuration} ${appleEaseOutStandard}, box-shadow ${indicatorTransitionDuration} ${appleEaseOutStandard}, transform ${iconTransitionDuration} ${appleEaseOutStandard}; user-select: none !important; -webkit-user-select: none !important; } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}:hover { background-color: var(--wl-indicator-hover-bg-dark); opacity: 0.8; transform: scale(1.1); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_ACTIVE }:not(.${this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED}) { background-color: var(--wl-indicator-active-bg-dark); opacity: 0.6; } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED } { width: 40px; height: 40px; border: 1px solid var(--wl-indicator-border-dark); background-color: var(--wl-indicator-expanded-bg-dark); box-shadow: var(--wl-shadow-dark); opacity: 1; backdrop-filter: blur(16px) saturate(180%); -webkit-backdrop-filter: blur(16px) saturate(180%); transform: scale(1); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED }:hover { transform: scale(1.08); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED }.${this.CSS_CLASSES.DYNAMIC_INDICATOR_ACTIVE} { background-color: var(--wl-indicator-active-bg-dark); color: var(--wl-icon-active-color-dark); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR} svg { display: block; width: 20px; height: 20px; opacity: 0; transform: scale(0.5); transition: opacity ${iconTransitionDuration} ${appleEaseOutStandard} ${iconTransitionDelay}, transform ${iconTransitionDuration} ${appleEaseOutStandard} ${iconTransitionDelay}; pointer-events: none; } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED } svg { opacity: 1; transform: scale(1); } @media (prefers-color-scheme: light) { #${this.ELEMENT_IDS.STATUS_NOTIFICATION} { border: 1px solid var(--wl-notify-border-light); background-color: var(--wl-notify-bg-light); color: var(--wl-notify-text-light); } #${this.ELEMENT_IDS.STATUS_NOTIFICATION} .${ this.CSS_CLASSES.BREATHING_DOT } { background-color: var(--wl-notify-dot-color-enabled-light); box-shadow: 0 0 8px 3px var(--wl-notify-dot-glow-enabled-light); } #${this.ELEMENT_IDS.STATUS_NOTIFICATION}.${ this.CSS_CLASSES.STATUS_NOTIFICATION_DISABLED } .${this.CSS_CLASSES.BREATHING_DOT} { background-color: var(--wl-notify-dot-color-disabled-light); box-shadow: 0 0 8px 3px var(--wl-notify-dot-glow-disabled-light); } #${this.ELEMENT_IDS.STATUS_NOTIFICATION} .${ this.CSS_CLASSES.STATUS_NOTIFICATION_MESSAGE } { color: var(--wl-notify-text-light); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR} { background-color: var(--wl-indicator-inactive-bg-light); color: var(--wl-icon-color-light); opacity: var(--wl-indicator-inactive-opacity-light); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}:hover { background-color: var(--wl-indicator-hover-bg-light); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_ACTIVE }:not(.${this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED}) { background-color: var(--wl-indicator-active-bg-light); opacity: 0.6; } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED } { border: 1px solid var(--wl-indicator-border-light); background-color: var(--wl-indicator-expanded-bg-light); box-shadow: var(--wl-shadow-light); } #${this.ELEMENT_IDS.DYNAMIC_INDICATOR}.${ this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED }.${this.CSS_CLASSES.DYNAMIC_INDICATOR_ACTIVE} { background-color: var(--wl-indicator-active-bg-light); color: var(--wl-icon-active-color-light); } } `; try { GM_addStyle(baseCSS); } catch (e) {} }, displayStatusNotification(statusMessageKey) { if (this.statusNotificationTimer) clearTimeout(this.statusNotificationTimer); if (this.statusNotificationRemovalTimer) clearTimeout(this.statusNotificationRemovalTimer); this.statusNotificationTimer = null; this.statusNotificationRemovalTimer = null; const message = StateManager.getLocalizedString(statusMessageKey); const renderNotification = () => { let notificationElement = document.getElementById( this.ELEMENT_IDS.STATUS_NOTIFICATION ); if (!notificationElement && document.body) { notificationElement = document.createElement("div"); notificationElement.id = this.ELEMENT_IDS.STATUS_NOTIFICATION; notificationElement.innerHTML = ` <div class="${this.CSS_CLASSES.BREATHING_DOT}"></div> <div class="${this.CSS_CLASSES.STATUS_NOTIFICATION_MESSAGE}"></div> `.trim(); document.body.appendChild(notificationElement); } else if (!notificationElement) { return; } if (!StateManager.isScriptActive) { notificationElement.classList.add( this.CSS_CLASSES.STATUS_NOTIFICATION_DISABLED ); } else { notificationElement.classList.remove( this.CSS_CLASSES.STATUS_NOTIFICATION_DISABLED ); } const messageElement = notificationElement.querySelector( `.${this.CSS_CLASSES.STATUS_NOTIFICATION_MESSAGE}` ); if (messageElement) messageElement.textContent = message; notificationElement.classList.remove( this.CSS_CLASSES.STATUS_NOTIFICATION_VISIBLE ); void notificationElement.offsetWidth; requestAnimationFrame(() => { const currentElement = document.getElementById( this.ELEMENT_IDS.STATUS_NOTIFICATION ); if (currentElement) { currentElement.classList.add( this.CSS_CLASSES.STATUS_NOTIFICATION_VISIBLE ); } }); this.statusNotificationTimer = setTimeout(() => { const currentElement = document.getElementById( this.ELEMENT_IDS.STATUS_NOTIFICATION ); if (currentElement) { currentElement.classList.remove( this.CSS_CLASSES.STATUS_NOTIFICATION_VISIBLE ); this.statusNotificationRemovalTimer = setTimeout(() => { document .getElementById(this.ELEMENT_IDS.STATUS_NOTIFICATION) ?.remove(); this.statusNotificationTimer = null; this.statusNotificationRemovalTimer = null; }, this.SETTINGS.ANIMATION_DURATION_MS); } else { this.statusNotificationTimer = null; this.statusNotificationRemovalTimer = null; } }, this.SETTINGS.NOTIFICATION_VISIBILITY_DURATION_MS); }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", renderNotification, { once: true, }); } else { renderNotification(); } }, updateUserScriptMenuCommand() { if (this.userScriptMenuCommandId) { try { GM_unregisterMenuCommand(this.userScriptMenuCommandId); } catch (e) {} this.userScriptMenuCommandId = null; } const labelKey = StateManager.isScriptActive ? "MENU_CMD_ENABLED" : "MENU_CMD_DISABLED"; const commandLabel = StateManager.getLocalizedString(labelKey); try { this.userScriptMenuCommandId = GM_registerMenuCommand( commandLabel, ScriptManager.toggleActivationWithDebounce ); } catch (e) { this.userScriptMenuCommandId = null; } }, createDynamicIndicator() { if ( !document.body || document.getElementById(this.ELEMENT_IDS.DYNAMIC_INDICATOR) ) { return; } this.dynamicIndicatorElement = document.createElement("div"); this.dynamicIndicatorElement.id = this.ELEMENT_IDS.DYNAMIC_INDICATOR; this.dynamicIndicatorElement.innerHTML = this.SVG_ICONS.DYNAMIC_INDICATOR; document.body.appendChild(this.dynamicIndicatorElement); this.updateDynamicIndicatorStateVisuals(); EventManager.bindDynamicIndicatorEvents(this.dynamicIndicatorElement); }, ensureDynamicIndicatorExists() { if ( this.dynamicIndicatorElement && document.body?.contains(this.dynamicIndicatorElement) ) { this.updateDynamicIndicatorStateVisuals(); return; } let existingIndicator = document.getElementById( this.ELEMENT_IDS.DYNAMIC_INDICATOR ); if (existingIndicator) { this.dynamicIndicatorElement = existingIndicator; this.updateDynamicIndicatorStateVisuals(); EventManager.bindDynamicIndicatorEvents(this.dynamicIndicatorElement); return; } if (document.body) { this.createDynamicIndicator(); } else { document.addEventListener( "DOMContentLoaded", () => this.createDynamicIndicator(), { once: true } ); } }, updateDynamicIndicatorStateVisuals() { const indicator = this.dynamicIndicatorElement || document.getElementById(this.ELEMENT_IDS.DYNAMIC_INDICATOR); if (indicator) { indicator.classList.toggle( this.CSS_CLASSES.DYNAMIC_INDICATOR_ACTIVE, StateManager.isScriptActive ); } }, expandAndCollapseDynamicIndicator() { const indicator = this.dynamicIndicatorElement || document.getElementById(this.ELEMENT_IDS.DYNAMIC_INDICATOR); if (!indicator) return; clearTimeout(this.indicatorCollapseTimer); indicator.classList.remove(this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED); void indicator.offsetWidth; requestAnimationFrame(() => { indicator.classList.add(this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED); this.indicatorCollapseTimer = setTimeout(() => { indicator.classList.remove( this.CSS_CLASSES.DYNAMIC_INDICATOR_EXPANDED ); this.indicatorCollapseTimer = null; }, this.SETTINGS.INDICATOR_EXPANDED_DURATION_MS); }); }, }; const StateManager = { STORAGE_KEYS: { ACTIVATION_STATE_PREFIX: "webliberator_state_", STATE_ENABLED_VALUE: "enabled", }, stateToggleDebounceMS: 200, defaultActivationState: false, isScriptActive: false, currentLocale: "en-US", localizedStrings: UIManager.STRINGS["en-US"], pageOrigin: window.location.origin, loadAndSetInitialState() { this.isScriptActive = this.defaultActivationState; this.currentLocale = this.detectUserLanguage(); this.localizedStrings = UIManager.STRINGS[this.currentLocale] || UIManager.STRINGS["en-US"]; this.loadActivationState(); }, detectUserLanguage() { const languages = navigator.languages || [navigator.language]; for (const lang of languages) { const langLower = lang.toLowerCase(); if (langLower === "zh-cn") return "zh-CN"; if ( langLower === "zh-tw" || langLower === "zh-hk" || langLower === "zh-mo" || langLower === "zh-hant" ) return "zh-TW"; if (langLower === "en-us") return "en-US"; if (langLower.startsWith("zh")) return "zh-CN"; if (langLower.startsWith("en")) return "en-US"; } return "en-US"; }, getActivationStateStorageKey() { const origin = String(this.pageOrigin || "").replace(/\/$/, ""); return `${this.STORAGE_KEYS.ACTIVATION_STATE_PREFIX}${origin}`; }, loadActivationState() { const storageKey = this.getActivationStateStorageKey(); let storedValue = null; try { storedValue = GM_getValue(storageKey, null); } catch (e) {} if (storedValue === this.STORAGE_KEYS.STATE_ENABLED_VALUE) { this.isScriptActive = true; } else if (storedValue === null) { this.isScriptActive = this.defaultActivationState; } else { this.isScriptActive = this.defaultActivationState; try { GM_deleteValue(storageKey); } catch (e) {} } }, saveActivationState() { const storageKey = this.getActivationStateStorageKey(); try { if (this.isScriptActive) { GM_setValue(storageKey, this.STORAGE_KEYS.STATE_ENABLED_VALUE); } else { GM_deleteValue(storageKey); } } catch (e) {} }, toggleActivation() { this.isScriptActive = !this.isScriptActive; this.saveActivationState(); return this.isScriptActive; }, getLocalizedString(key) { return ( this.localizedStrings[key] ?? UIManager.STRINGS["en-US"][key] ?? `${key}?` ); }, }; const RestrictionManager = { RESTRICTION_OVERRIDES: { EVENTS_TO_INTERCEPT: [ "contextmenu", "selectstart", "copy", "cut", "paste", "drag", "dragstart", ], INLINE_HANDLER_ATTRIBUTES_TO_CLEAR: [ "onmousedown", "oncontextmenu", "onselect", "onselectstart", "oncopy", "oncut", "onpaste", "onbeforecopy", "onbeforecut", "onbeforepaste", "ondrag", "ondragstart", ], }, overrideStyleSheetElement: null, applyOverrides() { this.injectRestrictionOverrideStyles(); EventManager.registerEventInterceptors(); }, removeOverrides() { this.removeRestrictionOverrideStyles(); EventManager.unregisterEventInterceptors(); }, injectRestrictionOverrideStyles() { if ( this.overrideStyleSheetElement || document.getElementById(UIManager.ELEMENT_IDS.OVERRIDE_STYLE_SHEET) ) return; const css = ` *, *::before, *::after { user-select: text !important; -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; cursor: auto !important; -webkit-user-drag: auto !important; user-drag: auto !important; } body { cursor: auto !important; } ::selection { background-color: highlight !important; color: highlighttext !important; } ::-moz-selection { background-color: highlight !important; color: highlighttext !important; } `; this.overrideStyleSheetElement = document.createElement("style"); this.overrideStyleSheetElement.id = UIManager.ELEMENT_IDS.OVERRIDE_STYLE_SHEET; this.overrideStyleSheetElement.textContent = css; (document.head || document.documentElement).appendChild( this.overrideStyleSheetElement ); }, removeRestrictionOverrideStyles() { this.overrideStyleSheetElement?.remove(); this.overrideStyleSheetElement = null; document .getElementById(UIManager.ELEMENT_IDS.OVERRIDE_STYLE_SHEET) ?.remove(); }, clearElementInlineHandlers(element) { if (!element || element.nodeType !== Node.ELEMENT_NODE) return; for (const prop of this.RESTRICTION_OVERRIDES .INLINE_HANDLER_ATTRIBUTES_TO_CLEAR) { if ( prop in element && (typeof element[prop] === "function" || element[prop] !== null) ) { try { element[prop] = null; } catch (e) {} } if (element.hasAttribute(prop)) { try { element.removeAttribute(prop); } catch (e) {} } } }, handlersRecursively(rootNode) { if (!StateManager.isScriptActive || !rootNode) return; const processSingleNode = (element) => { if (element.nodeType !== Node.ELEMENT_NODE) return; if ( element.closest( `#${UIManager.ELEMENT_IDS.DYNAMIC_INDICATOR}, #${UIManager.ELEMENT_IDS.STATUS_NOTIFICATION}` ) ) { return; } this.clearElementInlineHandlers(element); if (element.shadowRoot?.mode === "open") { for (const childInShadow of Array.from(element.shadowRoot.children)) { processSingleNode(childInShadow); } } for (const child of Array.from(element.children)) { processSingleNode(child); } }; try { if ( rootNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE || rootNode.nodeType === Node.DOCUMENT_NODE ) { for (const child of Array.from(rootNode.children)) { processSingleNode(child); } } else if (rootNode.nodeType === Node.ELEMENT_NODE) { processSingleNode(rootNode); } } catch (error) {} }, }; const EventManager = { domMutationObserver: null, debouncedHandleDOMMutation: null, DOM_OBSERVER_DEBOUNCE_MS: 300, init() { this.debouncedHandleDOMMutation = this.debounce( this.handleDOMMutation.bind(this), this.DOM_OBSERVER_DEBOUNCE_MS ); this.initializeGlobalKeyboardShortcutListener(); }, debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func.apply(this, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, bindDynamicIndicatorEvents(indicatorElement) { if (indicatorElement && !indicatorElement.dataset.listenerAttached) { indicatorElement.addEventListener( "click", this.handleDynamicIndicatorClick.bind(this) ); indicatorElement.dataset.listenerAttached = "true"; } }, stopPropagationHandler(event) { event.stopImmediatePropagation(); }, registerEventInterceptors() { RestrictionManager.RESTRICTION_OVERRIDES.EVENTS_TO_INTERCEPT.forEach( (type) => { document.addEventListener(type, this.stopPropagationHandler, { capture: true, passive: false, }); } ); }, unregisterEventInterceptors() { RestrictionManager.RESTRICTION_OVERRIDES.EVENTS_TO_INTERCEPT.forEach( (type) => { document.removeEventListener(type, this.stopPropagationHandler, { capture: true, }); } ); }, handleDOMMutation(mutations) { if (!StateManager.isScriptActive) return; for (const mutation of mutations) { if (mutation.type === "childList") { for (const node of mutation.addedNodes) { RestrictionManager.handlersRecursively(node); } } } }, initializeDOMObserver() { if (this.domMutationObserver || !document.documentElement) return; const observerOptions = { childList: true, subtree: true }; this.domMutationObserver = new MutationObserver( this.debouncedHandleDOMMutation ); try { this.domMutationObserver.observe( document.documentElement, observerOptions ); } catch (error) { this.domMutationObserver = null; } }, disconnectDOMObserver() { if (this.domMutationObserver) { this.domMutationObserver.disconnect(); this.domMutationObserver = null; } }, handleDynamicIndicatorClick(event) { event.stopPropagation(); UIManager.expandAndCollapseDynamicIndicator(); ScriptManager.toggleActivationWithDebounce(); }, handleKeyboardShortcut(event) { if ( (event.ctrlKey || event.metaKey) && event.altKey && event.code === "KeyL" ) { event.preventDefault(); event.stopPropagation(); ScriptManager.toggleActivationWithDebounce(); } }, initializeGlobalKeyboardShortcutListener() { document.addEventListener( "keydown", this.handleKeyboardShortcut.bind(this), { capture: true } ); }, }; const ScriptManager = { init() { try { StateManager.loadAndSetInitialState(); UIManager.injectCoreStyles(); UIManager.ensureDynamicIndicatorExists(); UIManager.updateUserScriptMenuCommand(); if (StateManager.isScriptActive) { RestrictionManager.applyOverrides(); RestrictionManager.handlersRecursively( document.documentElement ); EventManager.initializeDOMObserver(); } EventManager.init(); } catch (error) {} }, toggleActivation() { const isActiveNow = StateManager.toggleActivation(); if (isActiveNow) { RestrictionManager.applyOverrides(); RestrictionManager.handlersRecursively( document.documentElement ); EventManager.initializeDOMObserver(); UIManager.displayStatusNotification("STATUS_ENABLED"); } else { RestrictionManager.removeOverrides(); EventManager.disconnectDOMObserver(); UIManager.displayStatusNotification("STATUS_DISABLED"); } UIManager.updateUserScriptMenuCommand(); UIManager.updateDynamicIndicatorStateVisuals(); }, debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func.apply(this, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, toggleActivationWithDebounce: null, }; ScriptManager.toggleActivationWithDebounce = ScriptManager.debounce( ScriptManager.toggleActivation, StateManager.stateToggleDebounceMS ); if (window.self === window.top) { if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", () => ScriptManager.init(), { once: true } ); } else { ScriptManager.init(); } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址