Greasy Fork

拖拽打开链接

拖拽链接时在新标签页打开

// ==UserScript==
// @name         拖拽打开链接
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  拖拽链接时在新标签页打开
// @license MIT
// @icon         data:image/jpg;base64,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
// @author       zm
// @match        http://*/*
// @match        https://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// ==/UserScript==

(async function() {
    'use strict';

    // 全局变量保存当前菜单标题
    let currentMenuTitle = null;
    let refreshTimeout = null;
    
    // 初始化配置项(首次运行时创建)
    const DEFAULT_CONFIG = {
        openInNewTab: false // 默认不启用新标签页打开
    };

    // 配置管理器
    const ConfigManager = {
        async init() {
            const savedConfig = await GM_getValue('drag_open_config');
            this.config = savedConfig ? JSON.parse(savedConfig) : {...DEFAULT_CONFIG};
        },
        async set(key, value) {
            this.config[key] = value;
            await GM_setValue('drag_open_config', JSON.stringify(this.config));
        },
        get(key) {
            return this.config[key];
        }
    };

    // 初始化配置
    await ConfigManager.init();

    // 注册菜单命令
    registerConfigMenu(ConfigManager);

    // 添加状态提示
    function showStatusNotification(enabled) {
        const msg = enabled ? '✅ 已启用:拖拽链接将在新标签打开' : '❌ 已禁用:拖拽链接不会在新标签打开';
        const toast = document.createElement('div');
        toast.style.cssText = `
            position: fixed;
            right: 30px;
            bottom: 30px;
            background: rgba(0,0,0,0.8);
            color: white;
            padding: 12px 24px;
            border-radius: 6px;
            z-index: 99999;
            font-family: sans-serif;
            transition: opacity 0.5s ease-out;
        `;
        toast.textContent = msg;
        document.body.appendChild(toast);
        setTimeout(() => {
            toast.style.opacity = '0';
            setTimeout(() => toast.remove(), 500);
        }, 2000);
    }

    // 菜单注册函数
    function registerConfigMenu(configManager) {
        const newTitle = `拖拽链接新标签页: ${configManager.get('openInNewTab') ? '✅ 启用' : '❌ 禁用'}`;
        
        if (currentMenuTitle === newTitle) return; // 完全相同直接返回

        // 防抖 300ms 避免连续快速调用
        // 清除之前的防抖定时器
        if (refreshTimeout) clearTimeout(refreshTimeout);
        // 设置新的防抖定时器,避免频繁刷新菜单
        refreshTimeout = setTimeout(async () => {
            try {
                // 如果已有菜单项,先注销
                if (currentMenuTitle) {
                    await GM_unregisterMenuCommand(currentMenuTitle);
                }
                // 注册新的菜单项,并在回调中处理点击逻辑
                await GM_registerMenuCommand(newTitle, async () => {
                    const newValue = !configManager.get('openInNewTab');
                    await configManager.set('openInNewTab', newValue);
                    showStatusNotification(newValue); // 显示状态提示
                    registerConfigMenu(configManager); // 递归刷新菜单
                });

                currentMenuTitle = newTitle;
            } catch (e) {
                console.error('菜单刷新失败:', e);
            }
        }, 300);

    }


    // 监听拖拽开始事件
    document.addEventListener('dragstart', function(e) {
         // 1. 阻止所有图片的拖拽事件(含链接包裹的图片)
        // if (e.target.tagName === 'IMG' || e.target.closest('a img')) {
        //     e.preventDefault();// 阻止默认拖拽行为
        //     e.stopImmediatePropagation();// 阻止其他监听器
        //     return;
        // }

        // 2. 保留文字链接的拖拽高亮功能
        if (e.target.tagName === 'A' && e.target.href) {
            // 添加高亮样式
            e.target.style.border = '2px dashed orange';
            e.target.style.padding = '2px';
            
            // 清除拖拽数据中的图片信息(防止Lens识别)
            e.dataTransfer.setData('text/plain', e.target.href);
        }
    },true);

    // 监听拖拽结束事件
    document.addEventListener('dragend', function(e) {
        // 移除高亮样式
        if (e.target.tagName === 'A' && e.target.href) {
            e.target.style.border = '';
            e.target.style.padding = '';
        }
    });

    // 监听拖拽释放事件(可选)
    document.addEventListener('drop', function(e) {
        e.preventDefault();
        // 仅允许纯文本链接释放
        const link = e.dataTransfer.getData('text/plain');
        if (link && isValidUrl(link.trim()) && ConfigManager.get('openInNewTab')) {
            window.open(link, '_blank');
        }

    });
    
    function isValidUrl(url) {
        try {
            // 自动补全缺少协议的URL(如 example.com => http://example.com)
            const fullUrl = url.includes('://') ? url : 'http://' + url;
            const parsed = new URL(fullUrl);
            return ['http:', 'https:'].includes(parsed.protocol);
        } catch (e) {
            return false;
        }
    }

    // 防止默认拖拽行为(必须启用),因为浏览器默认会阻止 drop 事件的触发,从而导致拖拽效果无效。
    document.addEventListener('dragover', function(e) {
        e.preventDefault();
    });

})();