// ==UserScript==
// @name External Link Navigator
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// ==/UserScript==

// TODO: * Treat subdomain urls as internal links
//       * Create menu button to get all internal links

(function() {
    "use strict";
    const validProtocolRegex = /^(?:(?:f|ht)tps?):\/\//i;

    // Function to get the title of a website
    function getWebsiteTitle(url, callback, depth = 0) {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: function(response) {
                if (response.finalUrl !== url && depth < 5) {
                    getWebsiteTitle(response.finalUrl, callback, depth + 1);
                } else {
                    const title = response.responseXML.title || response.finalUrl;
                    callback(title, response.finalUrl);
                }
            }
        });
    }

    // Function to open a link in a new tab
    function openLinkInNewTab(link) {
        GM_registerMenuCommand(
                `${link.name}`, () => GM_openInTab(link.href, { active: false, insert: true }), {
                title: link.href,
                id: link.id,
                autoClose: false
            }
        );
    }

    // Function to get external links and open them in new tabs
    function getExternalLinks() {
        const externalLinks = Array.from(document.links)
            .filter(link => link.host !== location.host && validProtocolRegex.test(link.href))
            .reduce((acc, link, index) => {
                if (!acc.some(l => l.href === link.href)) {
                    acc.push({
                        id: 'Z' + index,
                        name: (link.textContent || link.innerText || link.title)
                            .trim()
                            .replace(/\s+/g, " "),
                        href: link.href
                    });
                }
                return acc;
            }, []);

        if (externalLinks.length === 0) return [];

        externalLinks.forEach(link => {
            if (!link.name || link.name === link.href || validProtocolRegex.test(link.name)) {
                getWebsiteTitle(link.href, (title, url) => {
                    [link.name, link.href] = [title, url]
                    openLinkInNewTab(link);
                });
            } else {
                openLinkInNewTab(link);
            }
        });

        return externalLinks;
    }

    // Register menu command to get external links
    GM_registerMenuCommand("Get External Links", () => {
        const startTime = new Date().getTime(); // Record start time
        const links = getExternalLinks();
        const endTime = new Date().getTime(); // Record end time
        const timeDiff = endTime - startTime; // Calculate time difference
        console.log(`Number of External Links: ${links.length} loaded in ${timeDiff}ms`);
    }, {
        id: 1,
        autoClose: false
    });
})();
Edit
Pub: 11 Feb 2024 02:19 UTC
Edit: 11 Feb 2024 02:42 UTC
Views: 64