// ==UserScript== // @name 选择性解除复制限制 // @namespace http://tampermonkey.net/ // @version 2.4.0 // @description 触发条hover短延时弹出,快速扫过不触发,面板靠上不挡页面,框内不回弹 // @author You // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @run-at document-end // @license MIT // ==/UserScript== (function () { 'use strict'; // ========== 配置 ========== const STORAGE_KEY = 'copy_unlock_sites'; const PANEL_WIDTH = 145; const PANEL_HEIGHT = 40; const TRIGGER_SIZE = 4; const HOVER_DELAY = 120; // 鼠标停在触发条多久才弹出(ms),快速划过不触发 const HIDE_DELAY = 500; // 离开后多久缩回 const PANEL_TOP_OFFSET = 60;// 顶部距离缩短,贴近上方 function getCurrentSite() { return location.hostname; } function getUnlockedSites() { try { return GM_getValue(STORAGE_KEY, []); } catch (e) { return []; } } function saveUnlockedSites(sites) { try { GM_setValue(STORAGE_KEY, sites); } catch (e) { console.error('保存设置失败:', e); } } function isCurrentSiteUnlocked() { const sites = getUnlockedSites(); return sites.includes(getCurrentSite()); } function toggleCurrentSite(enabled) { const sites = getUnlockedSites(); const site = getCurrentSite(); const index = sites.indexOf(site); if (enabled && index === -1) { sites.push(site); saveUnlockedSites(sites); applyUnlock(); } else if (!enabled && index !== -1) { sites.splice(index, 1); saveUnlockedSites(sites); removeUnlock(); } } const STYLE_ID = 'copy-unlock-style'; function applyUnlock() { if (!document.getElementById(STYLE_ID)) { const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` *, *::before, *::after { -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; -webkit-user-drag: auto !important; } .no-copy, .no-select, .unselectable, [unselectable="on"], [class*="unselect"] { -webkit-user-select: text !important; -moz-user-select: text !important; -ms-user-select: text !important; user-select: text !important; } `; document.head.appendChild(style); } const events = ['copy', 'cut', 'selectstart', 'contextmenu', 'beforecopy', 'mousedown', 'mouseup']; events.forEach(eventName => { document.addEventListener(eventName, stopPropagationAndAllow, true); window.addEventListener(eventName, stopPropagationAndAllow, true); }); removeInlineEventHandlers(); document.querySelectorAll('*').forEach(el => { if (el.hasAttribute('unselectable')) el.setAttribute('unselectable', 'off'); if (el.style.webkitUserSelect === 'none' || el.style.userSelect === 'none' || el.style.MozUserSelect === 'none') { el.dataset.origUserSelect = el.style.userSelect; el.style.webkitUserSelect = 'text'; el.style.userSelect = 'text'; el.style.MozUserSelect = 'text'; } }); observeDynamicContent(); console.log('[复制解锁] 已解除当前网站的复制限制'); } function stopPropagationAndAllow(e) { if (e.type === 'copy' || e.type === 'cut') { e.stopImmediatePropagation(); return true; } if (e.type === 'selectstart' || e.type === 'beforecopy') { e.stopImmediatePropagation(); return true; } if (e.type === 'contextmenu') { e.stopImmediatePropagation(); return true; } return true; } function removeInlineEventHandlers() { const attrs = ['oncopy', 'oncut', 'onselectstart', 'oncontextmenu', 'onbeforecopy', 'onmousedown', 'onmouseup']; document.querySelectorAll('*').forEach(el => { attrs.forEach(attr => { if (el.hasAttribute(attr)) { if (!el.dataset['orig_' + attr]) el.dataset['orig_' + attr] = el.getAttribute(attr); el.removeAttribute(attr); } }); }); } let observer = null; function observeDynamicContent() { if (observer) return; observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { mutation.addedNodes.forEach(node => { if (node.nodeType === 1) { if (node.style && (node.style.userSelect === 'none' || node.style.webkitUserSelect === 'none')) { node.style.webkitUserSelect = 'text'; node.style.userSelect = 'text'; } if (node.querySelectorAll) { node.querySelectorAll('*').forEach(el => { if (el.style && (el.style.userSelect === 'none' || el.style.webkitUserSelect === 'none')) { el.style.webkitUserSelect = 'text'; el.style.userSelect = 'text'; } }); } } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); } function removeUnlock() { const style = document.getElementById(STYLE_ID); if (style) style.remove(); const events = ['copy', 'cut', 'selectstart', 'contextmenu', 'beforecopy', 'mousedown', 'mouseup']; events.forEach(eventName => { document.removeEventListener(eventName, stopPropagationAndAllow, true); window.removeEventListener(eventName, stopPropagationAndAllow, true); }); if (observer) { observer.disconnect(); observer = null; } restoreInlineEventHandlers(); document.querySelectorAll('[data-orig-user-select]').forEach(el => { el.style.userSelect = el.dataset.origUserSelect; el.style.webkitUserSelect = el.dataset.origUserSelect; delete el.dataset.origUserSelect; }); console.log('[复制解锁] 已恢复当前网站的复制限制'); } function restoreInlineEventHandlers() { const attrs = ['oncopy', 'oncut', 'onselectstart', 'oncontextmenu', 'onbeforecopy', 'onmousedown', 'onmouseup']; document.querySelectorAll('*').forEach(el => { attrs.forEach(attr => { const origAttr = 'orig_' + attr; if (el.dataset[origAttr]) { el.setAttribute(attr, el.dataset[origAttr]); delete el.dataset[origAttr]; } }); }); } let panel = null; let checkbox = null; let hideTimer = null; let hoverOpenTimer = null; // 触发条hover延时计时器 let isPanelOpen = false; let triggerBar = null; function createPanel() { const wrapper = document.createElement('div'); wrapper.id = 'copy-unlock-wrapper'; wrapper.style.cssText = ` position: fixed; top: ${PANEL_TOP_OFFSET}px; left: 0; z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; user-select: none; `; panel = document.createElement('div'); panel.id = 'copy-unlock-panel'; panel.style.cssText = ` position: relative; width: ${PANEL_WIDTH}px; height: ${PANEL_HEIGHT}px; background: #ffffff; border: 1px solid #ccc; border-left: none; border-radius: 0 4px 4px 0; transform: translateX(calc(-100% + ${TRIGGER_SIZE}px)); transition: transform 0.25s ease-out; overflow: hidden; `; triggerBar = document.createElement('div'); triggerBar.style.cssText = ` position: absolute; top: 0; right: -${TRIGGER_SIZE}px; width: ${TRIGGER_SIZE}px; height: 100%; background: #bbb; border-radius: 0 2px 2px 0; cursor: pointer; `; const content = document.createElement('div'); content.style.cssText = ` padding: 10px 12px; display: flex; align-items: center; gap: 6px; height: 100%; box-sizing: border-box; `; checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = 'copy-unlock-checkbox'; checkbox.style.cssText = ` width: 14px; height: 14px; cursor: pointer; flex-shrink: 0; `; checkbox.checked = isCurrentSiteUnlocked(); const labelText = document.createElement('span'); labelText.textContent = '解除本站限制'; labelText.style.cssText = ` font-size: 12px; color: #333; cursor: pointer; white-space: nowrap; `; labelText.addEventListener('click', () => { checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change')); }); content.appendChild(checkbox); content.appendChild(labelText); panel.appendChild(triggerBar); panel.appendChild(content); wrapper.appendChild(panel); document.body.appendChild(wrapper); function openPanel() { clearTimeout(hideTimer); clearTimeout(hoverOpenTimer); if (!isPanelOpen) { panel.style.transform = 'translateX(0)'; isPanelOpen = true; } } function closePanel() { clearTimeout(hideTimer); hideTimer = setTimeout(() => { if (isPanelOpen) { panel.style.transform = `translateX(calc(-100% + ${TRIGGER_SIZE}px))`; isPanelOpen = false; } }, HIDE_DELAY); } // 鼠标进入触发条:延时再打开,快速划过直接取消 triggerBar.addEventListener('mouseenter', () => { clearTimeout(hoverOpenTimer); hoverOpenTimer = setTimeout(openPanel, HOVER_DELAY); }); // 鼠标离开触发条立刻取消弹出计时 triggerBar.addEventListener('mouseleave', () => { clearTimeout(hoverOpenTimer); }); // 进入面板内部直接打开,清除所有关闭计时 panel.addEventListener('mouseenter', openPanel); // 完全离开整个组件才开始倒计时关闭 wrapper.addEventListener('mouseleave', closePanel); let touchOpen = false; triggerBar.addEventListener('click', (e) => { e.stopPropagation(); clearTimeout(hoverOpenTimer); if (touchOpen) { panel.style.transform = `translateX(calc(-100% + ${TRIGGER_SIZE}px))`; touchOpen = false; isPanelOpen = false; } else { panel.style.transform = 'translateX(0)'; touchOpen = true; isPanelOpen = true; } }); document.addEventListener('click', (e) => { if (touchOpen && !wrapper.contains(e.target)) { panel.style.transform = `translateX(calc(-100% + ${TRIGGER_SIZE}px))`; touchOpen = false; isPanelOpen = false; } }); checkbox.addEventListener('change', (e) => { const enabled = e.target.checked; toggleCurrentSite(enabled); }); } function init() { createPanel(); if (isCurrentSiteUnlocked()) { setTimeout(applyUnlock, 500); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();