文字链接可点击

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

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

// ==UserScript==
// @name         文字链接可点击
// @version      1.1
// @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_REGEX: /(?:\b(?:https?|ftp|file):\/\/[^\s<>{}[\]"']+|www\.[^\s<>{}[\]"']+)\b/gi,
        COOLDOWN: 50,
        STYLE_CLASS: 'url-converter-link'
    };

    // 保存选区状态
    let lastSelection = null;

    // 样式隔离
    const style = document.createElement('style');
    style.textContent = `
        .${CONFIG.STYLE_CLASS} {
            color: #ff4500 !important;
            text-decoration: underline dashed !important;
            cursor: pointer !important;
        }
    `;
    document.head.appendChild(style);

    // 选区保护机制
    function saveSelection() {
        const sel = window.getSelection();
        if (sel.rangeCount > 0) {
            lastSelection = sel.getRangeAt(0);
        }
    }

    function restoreSelection() {
        if (lastSelection) {
            const sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(lastSelection.cloneRange());
            lastSelection = null;
        }
    }

    document.addEventListener('mousedown', saveSelection);
    document.addEventListener('mouseup', saveSelection);

    // 主处理函数
    document.addEventListener('click', function handler(event) {
        if (event.target.closest('a')) return;
        if (window.getSelection().toString().length > 0) 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 clickOffset = range.startOffset;

        // 安全匹配逻辑
        const matches = [];
        let match;
        while ((match = CONFIG.URL_REGEX.exec(fullText)) !== null) {
            const start = match.index;
            const end = start + match[0].length;
            if (clickOffset >= start && clickOffset <= end) {
                matches.push({ start, end, url: match[0] });
                break; // 只处理第一个匹配项
            }
        }

        if (matches.length === 0) return;

        // 执行转换
        const { start, end, url } = matches[0];
        const parent = textNode.parentNode;

        // 创建新节点
        const before = document.createTextNode(fullText.slice(0, start));
        const after = document.createTextNode(fullText.slice(end));
        const link = document.createElement('a');
        link.className = CONFIG.STYLE_CLASS;
        link.href = url.startsWith('www.') ? `http://${url}` : url;
        link.textContent = url;

        // 原子化DOM操作
        requestAnimationFrame(() => {
            parent.insertBefore(before, textNode);
            parent.insertBefore(link, textNode);
            parent.insertBefore(after, textNode);
            parent.removeChild(textNode);

            // 延迟恢复选区
            setTimeout(restoreSelection, 10);
        });

        event.preventDefault();
        event.stopImmediatePropagation();
    }, true);
})();