// ==UserScript==
// @name 解除复制限制
// @name:zh 解除复制限制
// @name:en Unlock Copy Restrictions
// @name:ja コピー制限解除
// @name:ko 복사 제한 해제
// @name:es Desbloquear restricciones de copia
// @namespace gura8390/copy/2
// @version 1.5
// @license MIT
// @icon https://img.icons8.com/nolan/64/password1.png
// @description 解除网页复制限制并提供可视化控制
// @description:zh 解除网页复制限制并提供可视化控制
// @description:en Unlock web copy restrictions with visual control
// @description:ja ウェブのコピー制限を解除し、視覚的な制御を提供
// @description:ko 웹 페이지의 복사 제한을 해제하고 시각적 제어를 제공합니다
// @description:es Desbloquea restricciones de copia en la web y proporciona control visual
// @author lbihhe
// @match *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// 本地化字典
const locales = {
en: {
menu_toggle_script: "🔄 Toggle Script State",
menu_toggle_button: "👁️ Toggle Button Display",
btn_unlock: "🔓 Unlock Restrictions",
btn_lock: "🔒 Restore Defaults",
toast_unlocked: "✔️ Copy restrictions unlocked!",
toast_locked: "✔️ Restrictions restored!"
},
zh: {
menu_toggle_script: "🔄 切换脚本状态",
menu_toggle_button: "👁️ 切换按钮显示",
btn_unlock: "🔓 解除限制",
btn_lock: "🔒 恢复原状",
toast_unlocked: "✔️ 复制限制已解除!",
toast_locked: "✔️ 限制已恢复!"
},
ja: {
menu_toggle_script: "🔄 スクリプト状態を切り替え",
menu_toggle_button: "👁️ ボタン表示を切り替え",
btn_unlock: "🔓 制限解除",
btn_lock: "🔒 デフォルトに戻す",
toast_unlocked: "✔️ コピー制限が解除されました!",
toast_locked: "✔️ 制限が復元されました!"
},
ko: {
menu_toggle_script: "🔄 스크립트 상태 전환",
menu_toggle_button: "👁️ 버튼 표시 전환",
btn_unlock: "🔓 제한 해제",
btn_lock: "🔒 기본값 복원",
toast_unlocked: "✔️ 복사 제한이 해제되었습니다!",
toast_locked: "✔️ 제한이 복원되었습니다!"
},
es: {
menu_toggle_script: "🔄 Cambiar estado del script",
menu_toggle_button: "👁️ Cambiar visualización del botón",
btn_unlock: "🔓 Desbloquear restricciones",
btn_lock: "🔒 Restaurar valores predeterminados",
toast_unlocked: "✔️ ¡Restricciones de copia desbloqueadas!",
toast_locked: "✔️ ¡Restricciones restauradas!"
}
};
// 语言检测
const lang = navigator.language.toLowerCase();
let userLang = 'en';
if (lang.startsWith('zh')) userLang = 'zh';
else if (lang.startsWith('ja')) userLang = 'ja';
else if (lang.startsWith('ko')) userLang = 'ko';
else if (lang.startsWith('es')) userLang = 'es';
const t = locales[userLang];
// 配置项
const CONFIG = {
ENABLED: 'copy_enabled',
SHOW_BUTTON: 'show_button'
};
// 网站特殊规则
const websiteRules = [
{
regexp: /doc88\.com/,
path: null,
init: function() {
// 隐藏干扰元素
const style = document.createElement('style');
style.innerHTML = '#left-menu{display: none !important;}';
document.head.appendChild(style);
// 双重路径获取方式
GM_xmlhttpRequest({
method: "GET",
url: "https://res3.doc88.com/resources/js/modules/main-v2.min.js?v=2.56",
onload: (response) => {
const result = /\("#cp_textarea"\).val\(([\S]*?)\);/.exec(response.responseText);
if (result) this.path = result[1];
}
});
const cpFn = unsafeWindow.copyText?.toString() || '';
const fnResult = /<textarea[\s\S]*?>'\+([\S]*?)\+"<\/textarea>/.exec(cpFn);
if (fnResult) this.path = fnResult[1];
// 重写复制事件
document.addEventListener('copy', (e) => {
if (!this.path) return;
let select = unsafeWindow;
this.path.split('.').forEach(v => select = select[v]);
if (!select) {
unsafeWindow.Config.vip = 1;
unsafeWindow.Config.logined = 1;
style.remove();
} else {
e.clipboardData.setData('text/plain', select);
e.preventDefault();
}
}, true);
}
}
];
// 全局变量
let unlockStyle = null;
const stopPropagation = e => e.stopPropagation();
const eventsList = ['contextmenu', 'copy', 'selectstart'];
// 初始化配置
const initConfig = () => {
if (GM_getValue(CONFIG.ENABLED, null) === null) GM_setValue(CONFIG.ENABLED, true);
if (GM_getValue(CONFIG.SHOW_BUTTON, null) === null) GM_setValue(CONFIG.SHOW_BUTTON, true);
};
// 注册菜单
const registerMenu = () => {
GM_registerMenuCommand(t.menu_toggle_script, () => {
GM_setValue(CONFIG.ENABLED, !GM_getValue(CONFIG.ENABLED));
location.reload();
});
GM_registerMenuCommand(t.menu_toggle_button, () => {
GM_setValue(CONFIG.SHOW_BUTTON, !GM_getValue(CONFIG.SHOW_BUTTON));
location.reload();
});
};
// 解除限制
const unlockCopy = () => {
if (document.querySelector('#copy-unlocker-style')) return;
unlockStyle = document.createElement('style');
unlockStyle.id = 'copy-unlocker-style';
unlockStyle.innerHTML = `* {
user-select: auto !important;
-webkit-user-select: auto !important;
-moz-user-select: auto !important;
-ms-user-select: auto !important;
}`;
document.head.appendChild(unlockStyle);
eventsList.forEach(event => {
document.body.addEventListener(event, stopPropagation, true);
});
};
// 恢复限制
const restoreCopy = () => {
if (unlockStyle) {
unlockStyle.remove();
unlockStyle = null;
}
eventsList.forEach(event => {
document.body.removeEventListener(event, stopPropagation, true);
});
};
// 提示框
const showSuccessToast = (msg, bgColor = '#4CAF50') => {
const toast = document.createElement('div');
toast.innerHTML = msg;
toast.style.cssText = `
position: fixed;
bottom: 80px;
right: 20px;
background: ${bgColor};
color: white;
padding: 12px 24px;
border-radius: 8px;
z-index: 9999;
opacity: 0;
animation: fadeSlideIn 0.6s forwards, fadeOut 0.6s 2.5s forwards;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
`;
if (!document.getElementById('toast-animations')) {
const style = document.createElement('style');
style.id = 'toast-animations';
style.innerHTML = `
@keyframes fadeSlideIn {
0% { transform: translateY(100%); opacity: 0; }
60% { transform: translateY(-10px); opacity: 1; }
100% { transform: translateY(0); opacity: 1; }
}
@keyframes fadeOut {
to { opacity: 0; }
}
`;
document.head.appendChild(style);
}
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3200);
};
// 浮动按钮
const createFloatButton = () => {
const btn = document.createElement('button');
btn.id = 'copy-unlocker-btn';
updateButtonLabel();
btn.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
z-index: 9999;
padding: 12px 24px;
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
color: white;
border: none;
border-radius: 30px;
cursor: pointer;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
transition: background 0.3s, transform 0.2s;
font-size: 14px;
`;
btn.addEventListener('mouseenter', () => btn.style.transform = 'scale(1.05)');
btn.addEventListener('mouseleave', () => btn.style.transform = 'scale(1)');
btn.addEventListener('click', handleButtonClick);
function updateButtonLabel() {
btn.innerHTML = GM_getValue(CONFIG.ENABLED) ? t.btn_lock : t.btn_unlock;
}
function handleButtonClick() {
const enabled = GM_getValue(CONFIG.ENABLED);
GM_setValue(CONFIG.ENABLED, !enabled);
if (enabled) {
restoreCopy();
showSuccessToast(t.toast_locked, '#F44336');
} else {
unlockCopy();
showSuccessToast(t.toast_unlocked, '#4CAF50');
}
updateButtonLabel();
}
return btn;
};
// 主函数
const main = () => {
initConfig();
registerMenu();
// 应用网站特殊规则
const currentRule = websiteRules.find(rule => rule.regexp.test(location.href));
if (currentRule) {
currentRule.init();
return;
}
// 通用处理
if (GM_getValue(CONFIG.ENABLED)) unlockCopy();
else restoreCopy();
if (GM_getValue(CONFIG.SHOW_BUTTON)) {
document.body.appendChild(createFloatButton());
}
};
// 启动
main();
})();