Jump to content

MediaWiki:Gadget-OpenExternal.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// Open all external links on a new window
mw.loader.using(['mediawiki.util']).then(function () {
    // Constants
    const internalLinkActions = [
        'edit', 'history', 'delete', 'submit', 'watch', 'unwatch', 
        'protect', 'unprotect', 'markpatrolled', 'purge', 'render'
    ];
    const currentHostname = window.location.hostname;

    // Helper function to apply external link attributes
    function makeExternal(link) {
        link.setAttribute('target', '_blank');
        link.setAttribute('rel', 'noopener noreferrer');
    }

    // Helper function to check if link should be treated as internal
    function isInternalAction(url) {
        try {
            const linkUrl = new URL(url);
            const action = linkUrl.searchParams.get('action');
            return linkUrl.hostname === currentHostname && action && internalLinkActions.includes(action);
        } catch (e) {
            return false;
        }
    }

    // Helper function to apply hover effects to social media icons
    function applySocialIconHoverEffects() {
        document.querySelectorAll('.link-open-external a').forEach(function(link) {
            // Only apply to links that don't already have the effects
            if (link.dataset.hoverEffectsApplied) return;
            
            // Apply base transition styles
            link.style.transition = 'all 0.2s ease-out';
            link.style.transformOrigin = 'center';
            
            // Add hover event listeners for scale effect
            link.addEventListener('mouseenter', function() {
                this.style.transform = 'scale(1.10)';
                this.style.textDecoration = 'none';
            });
            
            link.addEventListener('mouseleave', function() {
                this.style.transform = 'scale(1)';
            });
            
            // Mark as processed to avoid duplicate event listeners
            link.dataset.hoverEffectsApplied = 'true';
        });
    }

    function openExternalLinks() {
        // Process MediaWiki external/interwiki links
        document.querySelectorAll('a.external, a.extiw').forEach(function (link) {
            if (link.getAttribute('target') === '_blank') return;
            
            if (!isInternalAction(link.href)) {
                makeExternal(link);
            }
        });

        // Dedicated social media link processing: MediaWiki file syntax conversion
        document.querySelectorAll('div.link-open-external a, div.ntldstats a').forEach(function (link) {
            // Remove interfering class that redirects to File description pages
            if (link.classList.contains('mw-file-description')) {
                link.classList.remove('mw-file-description');
            }
            // Extract real URL from MediaWiki file link title attribute coming from SocialMedia.lua
            if (link.href.includes('/File:') && link.title && link.title.startsWith('link=')) {
                link.href = link.title.substring(5); // Remove 'link=' prefix
            }
            makeExternal(link);
        });

        // Smart detection for other unclassified external links
        document.querySelectorAll('a[href^="http"]').forEach(function (link) {
            if (link.getAttribute('target') === '_blank') return;
            
            // Skip if already processed by prior selectors
            if (link.closest('div.link-open-external, div.ntldstats') || 
                link.classList.contains('external') || 
                link.classList.contains('extiw')) {
                return;
            }
            
            try {
                const linkUrl = new URL(link.href);
                if (linkUrl.hostname !== currentHostname) {
                    makeExternal(link);
                }
            } catch (e) {
                makeExternal(link);
            }
        });

        // Apply hover effects to all social media icons after DOM processing
        applySocialIconHoverEffects();
    }

    // MutationObserver to handle dynamically inserted external links (like sidebar icons)
    function setupDynamicLinkWatcher() {
        const observer = new MutationObserver(function (mutations) {
            for (const mutation of mutations) {
                if (mutation.type !== 'childList') continue;
                
                for (const node of mutation.addedNodes) {
                    if (node.nodeType !== Node.ELEMENT_NODE) continue;
                    
                    // Check if node contains external links or canonical containers
                    if (node.matches && (
                        node.matches('a[href^="http"], div.link-open-external') ||
                        node.querySelector && node.querySelector('a[href^="http"], div.link-open-external')
                    )) {
                        openExternalLinks();
                        return; // Process once and exit early
                    }
                }
            }
        });

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

    mw.hook('wikipage.content').add(openExternalLinks);
    
    // Set up the dynamic link watcher after initial page load
    $(function () {
        setupDynamicLinkWatcher();
    });
});