// ==UserScript== // @name 在新标签页打开链接 // @namespace http://tampermonkey.net/ // @version 3.1 // @description 使用滑动开关图标的新标签页脚本,支持触屏操作,排除系统文件夹链接 // @author 晚风知我意 // @match https://*/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/517963/%E5%9C%A8%E6%96%B0%E6%A0%87%E7%AD%BE%E9%A1%B5%E6%89%93%E5%BC%80%E9%93%BE%E6%8E%A5.user.js // @updateURL https://update.greasyfork.icu/scripts/517963/%E5%9C%A8%E6%96%B0%E6%A0%87%E7%AD%BE%E9%A1%B5%E6%89%93%E5%BC%80%E9%93%BE%E6%8E%A5.meta.js // ==/UserScript== (function() { 'use strict'; // 配置参数 const CONFIG = { buttonSize: 48, activeColor: '#4CAF50', inactiveColor: '#F44336', hoverColor: '#388E3C', inactiveHoverColor: '#D32F2F', zIndex: 99999, positionOffset: 25, touchDelay: 300 }; // 主初始化函数 const init = () => { const domain = location.hostname.replace(/\./g, '-'); const toggleKey = `linkToggleEnabled_${domain}`; const positionKey = `buttonPosition_${domain}`; // 初始化设置 const settings = { isEnabled: GM_getValue(toggleKey, true), savedPosition: GM_getValue(positionKey) }; // 注入CSS样式 GM_addStyle(` .ntb-container-${domain} { position: fixed; z-index: ${CONFIG.zIndex}; cursor: move; transition: transform 0.2s; touch-action: none; } .ntb-button-${domain} { width: ${CONFIG.buttonSize}px; height: ${CONFIG.buttonSize}px; border-radius: 24px; display: flex; align-items: center; justify-content: center; background: transparent; box-shadow: none; cursor: pointer; border: none; outline: none; position: relative; overflow: visible; user-select: none; -webkit-tap-highlight-color: transparent; } .ntb-switch-track-${domain} { width: 36px; height: 20px; border-radius: 10px; background: ${settings.isEnabled ? CONFIG.activeColor : '#aaa'}; position: relative; transition: all 0.3s ease; opacity: 0.9; } .ntb-switch-thumb-${domain} { width: 16px; height: 16px; border-radius: 50%; background: white; position: absolute; left: ${settings.isEnabled ? '18px' : '2px'}; top: 2px; transition: all 0.3s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.2); } .ntb-button-${domain}:hover .ntb-switch-track-${domain} { background: ${settings.isEnabled ? CONFIG.hoverColor : '#888'}; opacity: 1; } .ntb-status-text-${domain} { position: absolute; bottom: 0px; font-size: 0px; color: ${settings.isEnabled ? 'white' : 'rgba(255,255,255,0.8)'}; font-weight: bold; width: 100%; text-align: center; text-shadow: 0 1px 2px rgba(0,0,0,0.5); } .ntb-button-${domain}::after { content: ''; position: absolute; top: -10px; left: -10px; right: -10px; bottom: -16px; z-index: -1; } `); // 创建按钮元素 const createButton = () => { const container = document.createElement('div'); container.className = `ntb-container-${domain}`; const button = document.createElement('div'); button.className = `ntb-button-${domain}`; button.title = settings.isEnabled ? '已启用: 链接将在新标签页打开 (点击关闭)' : '已禁用: 点击开启新标签页功能'; button.innerHTML = `
${settings.isEnabled ? 'ON' : 'OFF'}
`; container.appendChild(button); // 设置初始位置 - 右下角 if (settings.savedPosition) { container.style.left = `${settings.savedPosition.x}px`; container.style.top = `${settings.savedPosition.y}px`; } else { const viewportHeight = window.innerHeight; container.style.right = `${CONFIG.positionOffset}px`; container.style.bottom = `${CONFIG.positionOffset}px`; container.style.top = 'auto'; } return { container, button }; }; // 添加按钮到页面 const { container, button } = createButton(); document.body.appendChild(container); // 更新按钮状态 const updateButtonState = () => { const track = button.querySelector(`.ntb-switch-track-${domain}`); const thumb = button.querySelector(`.ntb-switch-thumb-${domain}`); const statusText = button.querySelector(`.ntb-status-text-${domain}`); if (settings.isEnabled) { track.style.background = CONFIG.activeColor; thumb.style.left = '18px'; statusText.textContent = 'ON'; statusText.style.color = CONFIG.activeColor; button.title = '已启用: 链接将在新标签页打开 (点击关闭)'; } else { track.style.background = '#ccc'; thumb.style.left = '2px'; statusText.textContent = 'OFF'; statusText.style.color = CONFIG.inactiveColor; button.title = '已禁用: 点击开启新标签页功能'; } // 悬停效果 const updateHoverState = () => { track.style.background = settings.isEnabled ? CONFIG.hoverColor : '#aaa'; }; const resetHoverState = () => { track.style.background = settings.isEnabled ? CONFIG.activeColor : '#ccc'; }; button.addEventListener('mouseenter', updateHoverState); button.addEventListener('mouseleave', resetHoverState); button.addEventListener('touchstart', updateHoverState); button.addEventListener('touchend', resetHoverState); }; // 检查是否是系统文件夹链接 const isSystemFolderLink = (href) => { // 匹配Windows文件路径 (如 file:///C:/ 或 file:///D:/) if (/^file:\/\/\/[a-zA-Z]:\//.test(href)) return true; // 匹配Mac/Linux文件路径 (如 file:///Users/ 或 file:///home/) if (/^file:\/\/\/(Users|home|etc|var|opt)\//.test(href)) return true; // 匹配网络共享路径 (如 file://server/share) if (/^file:\/\/\/\/[^\/]+\//.test(href)) return true; return false; }; // 处理链接点击 const handleLinkClick = (event) => { if (!settings.isEnabled) return; const link = event.target.closest('a'); if (!link || !link.href) return; // 排除特殊情况 if (link.hasAttribute('download') || link.href.startsWith('javascript:') || link.href.startsWith('mailto:') || link.href.startsWith('tel:') || isSystemFolderLink(link.href)) { return; } event.preventDefault(); event.stopPropagation(); window.open(link.href, '_blank'); }; // 拖拽功能 - 支持鼠标和触摸 let isDragging = false; let startX, startY, startLeft, startTop; let dragStartTime = 0; let touchTimer = null; const startDrag = (e) => { e.preventDefault(); // 获取初始位置 const clientX = e.clientX || e.touches[0].clientX; const clientY = e.clientY || e.touches[0].clientY; // 获取当前计算位置 const computedStyle = window.getComputedStyle(container); startLeft = parseInt(computedStyle.left) || 0; startTop = parseInt(computedStyle.top) || 0; // 如果使用right定位,转换为left定位 if (computedStyle.right !== 'auto') { const rightPos = parseInt(computedStyle.right); startLeft = window.innerWidth - rightPos - CONFIG.buttonSize; container.style.right = 'auto'; container.style.left = `${startLeft}px`; } startX = clientX; startY = clientY; dragStartTime = Date.now(); // 对于触摸设备,延迟判定是否为拖动 if (e.type === 'touchstart') { touchTimer = setTimeout(() => { isDragging = true; container.style.transition = 'none'; }, CONFIG.touchDelay); } else { isDragging = true; } // 添加事件监听 document.addEventListener('mousemove', drag); document.addEventListener('touchmove', drag, { passive: false }); document.addEventListener('mouseup', endDrag); document.addEventListener('touchend', endDrag); }; const drag = (e) => { if (!isDragging) return; e.preventDefault(); const clientX = e.clientX || e.touches[0].clientX; const clientY = e.clientY || e.touches[0].clientY; const dx = clientX - startX; const dy = clientY - startY; container.style.left = `${startLeft + dx}px`; container.style.top = `${startTop + dy}px`; container.style.right = 'auto'; }; const endDrag = (e) => { if (touchTimer) { clearTimeout(touchTimer); touchTimer = null; } if (!isDragging) { if (Date.now() - dragStartTime < CONFIG.touchDelay) { toggleFunctionality(e); } return; } isDragging = false; container.style.transition = ''; // 移除事件监听 document.removeEventListener('mousemove', drag); document.removeEventListener('touchmove', drag); document.removeEventListener('mouseup', endDrag); document.removeEventListener('touchend', endDrag); // 保存位置 const rect = container.getBoundingClientRect(); GM_setValue(positionKey, { x: rect.left, y: rect.top }); }; // 切换功能状态 const toggleFunctionality = (e) => { if (e) e.stopPropagation(); settings.isEnabled = !settings.isEnabled; GM_setValue(toggleKey, settings.isEnabled); updateButtonState(); // 添加动画效果 const thumb = button.querySelector(`.ntb-switch-thumb-${domain}`); thumb.style.transform = 'scale(1.2)'; setTimeout(() => { thumb.style.transform = ''; }, 200); }; // 设置事件监听 button.addEventListener('mousedown', startDrag); button.addEventListener('touchstart', startDrag, { passive: false }); button.addEventListener('click', (e) => { if (!isDragging && Date.now() - dragStartTime > CONFIG.touchDelay) { toggleFunctionality(e); } }); document.addEventListener('click', handleLinkClick, true); // 注册菜单命令 GM_registerMenuCommand('切换新标签页功能', toggleFunctionality); GM_registerMenuCommand('重置按钮位置', () => { container.style.right = `${CONFIG.positionOffset}px`; container.style.bottom = `${CONFIG.positionOffset}px`; container.style.left = 'auto'; container.style.top = 'auto'; GM_setValue(positionKey, null); }); // 初始状态 updateButtonState(); }; // 确保DOM加载完成后执行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();