文字链接可点击

URL文本转a标签,代码全部来自DeepSeek,可能存在bug

目前為 2025-01-31 提交的版本,檢視 最新版本

// ==UserScript==
// @name         文字链接可点击
// @version      1.0
// @description  URL文本转a标签,代码全部来自DeepSeek,可能存在bug
// @author       cangming99
// @match        *://*/*
// @grant        none
// @run-at       document-idle
// @license MIT
// @namespace https://greasyfork.org/users/826934
// ==/UserScript==

(function() {
    'use strict';

    // 配置参数(可根据需要调整)
    const CONFIG = {
        // 增强兼容性的URL正则
        URL_REGEX: /(?:\b(?:https?|ftp|file):\/\/[^\s<>{}[\]"']+|www\.[^\s<>{}[\]"']+)\b/gi,
        // 最大匹配次数(防止超长文本卡死)
        MAX_MATCHES: 10,
        // 样式配置
        LINK_STYLE: {
            color: '#ff4500',
            'text-decoration': 'underline dashed',
            'cursor': 'pointer',
            'position': 'relative'
        },
        // 点击后冷却时间(毫秒)
        COOLDOWN: 30
    };

    // 状态控制器
    let isProcessing = false;

    // 样式注入
    const style = document.createElement('style');
    style.textContent = `
        .url-converter-link {
            color: ${CONFIG.LINK_STYLE.color} !important;
            text-decoration: ${CONFIG.LINK_STYLE['text-decoration']} !important;
            cursor: ${CONFIG.LINK_STYLE.cursor} !important;
            position: ${CONFIG.LINK_STYLE.position};
        }
    `;
    document.head.appendChild(style);

    // 主处理函数
    function handleClick(event) {
        if (isProcessing) return;
        if (event.target.closest('a, button, input')) return;

        const range = document.caretRangeFromPoint(event.clientX, event.clientY);
        if (!range || range.startContainer.nodeType !== Node.TEXT_NODE) return;

        const textNode = range.startContainer;
        const fullText = textNode.data;
        const clickPos = range.startOffset;

        // 性能保护:限制处理范围
        const start = Math.max(0, clickPos - 150);
        const end = Math.min(fullText.length, clickPos + 150);
        const contextText = fullText.slice(start, end);

        let match;
        let matchCount = 0;
        const regex = new RegExp(CONFIG.URL_REGEX.source, 'gi');

        while ((match = regex.exec(contextText)) !== null && matchCount < CONFIG.MAX_MATCHES) {
            matchCount++;
            const [url] = match;
            const globalStart = start + match.index;
            const globalEnd = globalStart + url.length;

            if (clickPos >= globalStart && clickPos <= globalEnd) {
                isProcessing = true;

                // 创建链接元素
                const link = document.createElement('a');
                link.className = 'url-converter-link';
                link.href = url.startsWith('www.') ? `http://${url}` : url;
                link.textContent = url;

                // 分割文本节点
                const before = document.createTextNode(fullText.slice(0, globalStart));
                const after = document.createTextNode(fullText.slice(globalEnd));

                // 安全替换
                const parent = textNode.parentNode;
                parent.insertBefore(before, textNode);
                parent.insertBefore(link, textNode);
                parent.insertBefore(after, textNode);
                parent.removeChild(textNode);

                // 重置状态
                setTimeout(() => {
                    isProcessing = false;
                }, CONFIG.COOLDOWN);

                event.preventDefault();
                event.stopImmediatePropagation();
                return;
            }
        }
    }

    // 事件监听优化
    document.addEventListener('click', handleClick, {
        capture: true,
        passive: false
    });
})();