Jump to content
🚛
Streamline your cross-border operation. From ACE & ACI eManifests to PARS tracking and U.S. In-Bond filing, BorderConnect is the fastest way to cross.

MediaWiki:Common.js

From BorderConnect Wiki

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.
/* Any JavaScript here will be loaded for all users on every page load. */

/* Load FontAwesome 6.6.0 (Includes Butterfly & Bluesky) */
mw.loader.load('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/all.min.css', 'text/css');

/* Force all external links to open in a new tab */
$( function() {
    $( 'a.external' ).attr( 'target', '_blank' );
});

window.wpDarkModeAutoToggle = true;

/* --- Inject Hero Search Bar on Main Page (Fixed Suggestions) --- */
$(function() {
    // Only run this if the placeholder exists
    if ($('#bc-hero-search').length) {
        var searchHTML = 
            '<form action="/index.php" id="searchform">' +
            '<input type="hidden" name="title" value="Special:Search">' +
            '<div style="position: relative; max-width: 600px; margin: 0 auto;">' +
            
            // ADDED: class="mw-searchInput"
            // ADDED: accesskey="f" (Standard MediaWiki hotkey)
            '<input id="heroSearchInput" class="mw-searchInput" name="search" type="search" placeholder="Search for In-Bond, ACE Manifest..." autocomplete="off" accesskey="f" style="width: 100%; padding: 15px 120px 15px 25px; border-radius: 50px; border: none; font-size: 16px; box-shadow: 0 4px 6px rgba(0,0,0,0.2); outline: none;">' +
            
            '<button type="submit" name="go" value="Go" style="position: absolute; right: 5px; top: 5px; bottom: 5px; background: #266065; color: white; border: none; border-radius: 50px; padding: 0 25px; font-weight: bold; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.2);">Search</button>' +
            '</div>' +
            '</form>';
            
        $('#bc-hero-search').html(searchHTML);

        // --- MANUALLY BIND SUGGESTIONS ---
        // We wait for the module to load, then force the bind
        mw.loader.using( 'mediawiki.searchSuggest', function () {
            $( '#heroSearchInput' ).searchSuggest();
        });
    }
});

/* --- INJECT CUSTOM SIDEBAR MENU --- */
$(function() {
    // Define the menu HTML
    var customMenu = 
        '<nav class="mw-portlet mw-portlet-bc-tools vector-menu vector-menu-portal portal" aria-labelledby="p-bctools-label" role="navigation">' +
        '<h3 id="p-bctools-label" class="vector-menu-heading"><span>BorderConnect Tools</span></h3>' +
        '<div class="vector-menu-content">' +
        '<ul class="vector-menu-content-list">' +
            '<li><a href="https://www.borderconnect.com/auth/login" target="_blank">Login to System</a></li>' +
            '<li><a href="https://www.borderprint.com" target="_blank">Order Labels (BorderPrint)</a></li>' +
            '<li><a href="https://www.borderconnect.com/contact" target="_blank">Contact Support</a></li>' +
        '</ul>' +
        '</div>' +
        '</nav>';

    // Insert it above the "Tools" (tb) section or "Interaction" section
    // Try '#p-tb' (Tools) or '#p-navigation' (Navigation)
    $('#p-navigation').after(customMenu); 
});

/* --- SMART VIDEO MODAL (Auto-Inject) --- */
$(function() {
    
    // 1. Check if Modal HTML exists. If not, create it.
    if ($('#bc-video-modal').length === 0) {
        $('body').append(`
            <div id="bc-video-modal" class="bc-modal-overlay">
                <div class="bc-modal-content">
                    <div class="bc-modal-close">&times;</div>
                    <iframe id="bc-modal-iframe" width="100%" height="100%" src="" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
                </div>
            </div>
        `);
    }

    // 2. Setup Triggers (Thumbnail & Click)
    $('.bc-video-trigger').each(function() {
        var $trigger = $(this);
        var videoID = $trigger.data('video-id');
        
        // Inject Image Tag if missing
        if ($trigger.find('img').length === 0) {
            var thumbUrl = "https://img.youtube.com/vi/" + videoID + "/maxresdefault.jpg";
            $trigger.prepend('<img src="' + thumbUrl + '" alt="Video Thumbnail">');
        }
    });

    // 3. Handle Click (Using 'body' delegation to ensure it always catches the click)
    $('body').on('click', '.bc-video-trigger', function() {
        var videoID = $(this).data('video-id');
        var embedUrl = "https://www.youtube.com/embed/" + videoID + "?autoplay=1&rel=0&modestbranding=1";
        
        $('#bc-modal-iframe').attr('src', embedUrl);
        $('#bc-video-modal').addClass('active');
        $('body').css('overflow', 'hidden'); // Stop background scrolling
    });

    // 4. Close Logic
    function closeModal() {
        $('#bc-video-modal').removeClass('active');
        $('#bc-modal-iframe').attr('src', '');
        $('body').css('overflow', '');
    }

    $('body').on('click', '.bc-modal-close', closeModal);
    
    // Close on background click
    $('body').on('click', '#bc-video-modal', function(e) {
        if (e.target === this) closeModal();
    });
    
    // Close on Escape Key
    $(document).keydown(function(e) {
        if (e.key === "Escape") closeModal();
    });
});

/* ============================================================
   --- FORCE TOC THEME UPDATE ---
   Watches the HTML tag for theme changes and forces a class 
   onto the TOC to ensure it switches styles instantly.
   ============================================================ */

$(function() {
    // 1. Identify the TOC (Standard or Vector 2022)
    var tocSelector = '#toc, .vector-toc, .mw-parser-output .toc';

    // 2. The function that decides if we are Dark or Light
    function updateTOC() {
        var $html = $('html');
        var $toc = $(tocSelector);
        
        // Check if "Night" class is active
        var isNight = $html.hasClass('skin-theme-clientpref-night');
        
        // Check if "OS" class is active AND system is Dark
        var isOS = $html.hasClass('skin-theme-clientpref-os');
        var systemDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;

        if (isNight || (isOS && systemDark)) {
            // FORCE DARK MODE
            $toc.addClass('force-dark-mode');
        } else {
            // REVERT TO LIGHT
            $toc.removeClass('force-dark-mode');
        }
    }

    // 3. Run immediately on load
    updateTOC();

    // 4. Watch for changes (User toggles the switch)
    var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.attributeName === "class") {
                updateTOC();
            }
        });
    });

    // Start observing the <html class="..."> attribute
    observer.observe(document.documentElement, {
        attributes: true
    });
});

/* ============================================================
   --- SEARCH ICON COLOR FIX (JS UPDATE FOR STUBBORN CLASSES) ---
   ============================================================ */

$(document).ready(function() {
    // 1. Function to force the icon to white
    function forceSearchIconWhite() {
        // Targets standard icons AND the specific -vue class you found
        $('.cdx-text-input__icon, .cdx-search-input__start-icon, .cdx-text-input__icon-vue')
            .css({
                'filter': 'brightness(0) invert(1)',
                'color': '#ffffff',
                'fill': '#ffffff',
                'opacity': '1'
            });
    }

    // 2. Function to reset to dark (when typing/focused)
    function resetSearchIconDark() {
        $('.cdx-text-input__icon, .cdx-search-input__start-icon, .cdx-text-input__icon-vue')
            .css({
                'filter': 'none',
                'color': '#546e7a', // Dark Grey
                'fill': '#546e7a',
                'opacity': '0.5'
            });
    }

    // 3. Attach Events using delegation
    $('body').on('focus', '.cdx-text-input__input', function() {
        resetSearchIconDark();
    });

    // This catches the moment the user clicks away
    $('body').on('blur', '.cdx-text-input__input', function() {
        forceSearchIconWhite();
    });

    // 4. Run on load and also after a tiny delay 
    // (MediaWiki sometimes renders the search bar slightly late)
    forceSearchIconWhite();
    setTimeout(forceSearchIconWhite, 500);

    // 5. Watch for any input changes to prevent "reverting" while typing/clicking
    $('.cdx-text-input__input').on('input change', function() {
        if (!$(this).is(':focus')) {
            forceSearchIconWhite();
        }
    });
});

/* ============================================================
   --- NATIVE SCROLL MONITOR (FIXED) ---
   ============================================================ */

document.addEventListener('scroll', function() {
    // Check if we have scrolled down more than 50 pixels
    if (window.scrollY > 50) {
        document.body.classList.add('bc-scrolled');
    } else {
        document.body.classList.remove('bc-scrolled');
    }
});

// Run once immediately on load to catch refresh state
if (window.scrollY > 50) {
    document.body.classList.add('bc-scrolled');
}