Convert Any links to Clickable Links (Lightweight)

URLやドメイン名をクリック可能なリンクに変換(軽量化版)

Versión del día 13/03/2025. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name         Convert Any links to Clickable Links (Lightweight)
// @namespace    kdroidwin.hatenablog.com
// @version      2.4
// @description  URLやドメイン名をクリック可能なリンクに変換(軽量化版)
// @author       Kdroidwin
// @match        *://*/*
// @exclude      *://github.com/*
// @exclude      *://chat.openai.com/*
// @exclude      *://blog.hatena.ne.jp/*
// @grant        none
// @license      GPL-3.0
// ==/UserScript==

(function() {
    'use strict';

    const urlPattern = /\b(?:h?ttps?:\/\/[^\s<>"]+|(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:\/[^\s<>"]*)?)\b/g;

    function isEditable(node) {
        while (node) {
            if (node.nodeName === 'INPUT' || node.nodeName === 'TEXTAREA' || 
                (node.getAttribute && node.getAttribute('contenteditable') === 'true')) return true;
            node = node.parentNode;
        }
        return false;
    }

    function convertTextToLinks(root) {
        const textNodes = [];
        root.querySelectorAll(':not(a):not(input):not(textarea):not([contenteditable])').forEach(el => {
            for (let node of el.childNodes) {
                if (node.nodeType === 3 && urlPattern.test(node.nodeValue)) textNodes.push(node);
            }
        });

        textNodes.forEach(node => {
            const frag = document.createDocumentFragment();
            let lastIndex = 0;
            node.nodeValue.replace(urlPattern, (match, offset) => {
                frag.appendChild(document.createTextNode(node.nodeValue.slice(lastIndex, offset)));

                const a = document.createElement('a');
                a.href = match.startsWith('ttp') ? 'h' + match : match.includes('://') ? match : 'https://' + match;
                a.textContent = match;
                a.target = '_blank';
                frag.appendChild(a);

                lastIndex = offset + match.length;
            });
            frag.appendChild(document.createTextNode(node.nodeValue.slice(lastIndex)));
            node.parentNode.replaceChild(frag, node);
        });
    }

    const observer = new MutationObserver(() => {
        convertTextToLinks(document.body);
    });

    observer.observe(document.body, { childList: true, subtree: true });

    convertTextToLinks(document.body);
})();