Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js: Difference between revisions

MediaWiki interface page
Add search bar with autocomplete suggestions to custom header
No edit summary
Line 43: Line 43:
         '<div id="mf-search-bar"><div id="mf-search-inner"><form action="/wiki/Special:Search" id="mf-search-form"><input type="text" name="search" id="mf-search-input" placeholder="Search the wiki..." autocomplete="off" /><button type="submit" id="mf-search-btn" aria-label="Search"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></button></form><div id="mf-search-suggestions"></div></div></div>';
         '<div id="mf-search-bar"><div id="mf-search-inner"><form action="/wiki/Special:Search" id="mf-search-form"><input type="text" name="search" id="mf-search-input" placeholder="Search the wiki..." autocomplete="off" /><button type="submit" id="mf-search-btn" aria-label="Search"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></button></form><div id="mf-search-suggestions"></div></div></div>';
     document.body.insertBefore( header, document.body.firstChild );
     document.body.insertBefore( header, document.body.firstChild );
    /* ── Fix sitename typo wherever it appears in Citizen UI ──────────────── */
    document.querySelectorAll(
        ".citizen-footer__sitetitle, .citizen-footer a, .mw-logo-wordmark"
    ).forEach( function ( el ) {
        if ( el.textContent.indexOf( "Mehirs" ) !== -1 ) {
            el.textContent = el.textContent.replace( /Mehirs/g, "Menhirs" );
        }
    } );


     /* ── Mobile hamburger ────────────────────────────────────────────────────────────── */
     /* ── Mobile hamburger ────────────────────────────────────────────────────────────── */

Revision as of 08:40, 12 March 2026

/* Menhirs Fate Wiki - Custom Header */
( function () {
    "use strict";

    /* ── Google Fonts ──────────────────────────────────────────────────────────────── */
    var link = document.createElement( "link" );
    link.rel = "stylesheet";
    link.href = "https://fonts.googleapis.com/css2?family=Crimson+Text:ital,wght@0,400;0,600;0,700;1,400&display=swap";
    document.head.appendChild( link );

    /* ── Custom Header ─────────────────────────────────────────────────────────────── */
    var header = document.createElement( "div" );
    header.id = "mf-header";
    header.innerHTML =
        '<div id="mf-header-top"><a href="https://www.menhirsfate.com/" id="mf-logo-link"><img src="https://www.menhirsfate.com/wp-content/uploads/2024/08/white-logo-scaled-120x65.png" alt="Menhirs Fate" id="mf-logo" /></a></div>' +
        '<nav id="mf-ribbon"><div id="mf-ribbon-inner">' +
            '<a href="https://www.menhirsfate.com/" class="mf-nav-item mf-nav-back">&larr; MenhirsFate.com</a>' +
            '<a href="/wiki/Main_Page" class="mf-nav-item">Home</a>' +
            '<div class="mf-has-dropdown"><span class="mf-nav-label">Nations <span class="mf-caret">&#9662;</span></span><div class="mf-dropdown">' +
                '<a href="/wiki/The_Crownlands/Avereaux">Avereaux</a>' +
                '<a href="/wiki/The_Wonder">The Wonder</a>' +
                '<a href="/wiki/Valdraeth">Valdraeth</a>' +
                '<a href="/wiki/The_Urdrevan_People">The Urdrevan People</a>' +
                '<a href="/wiki/The_Republic_of_Portavas">The Republic of Portavas</a>' +
                '<a href="/wiki/The_Hammerstadt_Charter">The Hammerstadt Charter</a>' +
                '<a href="/wiki/Kairos">Kairos</a>' +
                '<a href="/wiki/Syradonia">Syradonia</a>' +
                '<a href="/wiki/Morvalis">Morvalis</a>' +
            '</div></div>' +
            '<div class="mf-has-dropdown"><span class="mf-nav-label">Gameplay <span class="mf-caret">&#9662;</span></span><div class="mf-dropdown">' +
                '<a href="/wiki/Look_and_Feel">Look and Feel</a>' +
                '<a href="/wiki/Basic_Skills">Basic Skills</a>' +
                '<a href="/wiki/Magical_Skills">Magical Skills</a>' +
                '<a href="/wiki/Monstering">Monstering</a>' +
                '<a href="/wiki/Accessibility">Accessibility</a>' +
            '</div></div>' +
            '<div class="mf-has-dropdown"><span class="mf-nav-label">Resources <span class="mf-caret">&#9662;</span></span><div class="mf-dropdown">' +
                '<a href="/wiki/Funded_Builds">Funded Builds</a>' +
                '<a href="/wiki/Camping_Information">Camping Information</a>' +
                '<a href="/wiki/Special:Categories">Browse by Category</a>' +
            '</div></div>' +
        '</div></nav>' +
        '<div id="mf-search-bar"><div id="mf-search-inner"><form action="/wiki/Special:Search" id="mf-search-form"><input type="text" name="search" id="mf-search-input" placeholder="Search the wiki..." autocomplete="off" /><button type="submit" id="mf-search-btn" aria-label="Search"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg></button></form><div id="mf-search-suggestions"></div></div></div>';
    document.body.insertBefore( header, document.body.firstChild );

    /* ── Mobile hamburger ────────────────────────────────────────────────────────────── */
    var ribbon = document.getElementById( "mf-ribbon-inner" );
    var burger = document.createElement( "button" );
    burger.id = "mf-burger";
    burger.setAttribute( "aria-label", "Toggle menu" );
    burger.innerHTML = "<span></span><span></span><span></span>";
    document.getElementById( "mf-header-top" ).appendChild( burger );

    burger.addEventListener( "click", function () {
        ribbon.classList.toggle( "mf-open" );
        burger.classList.toggle( "mf-open" );
    } );

    ribbon.addEventListener( "click", function ( e ) {
        if ( e.target.tagName === "A" ) {
            ribbon.classList.remove( "mf-open" );
            burger.classList.remove( "mf-open" );
        }
    } );

    /* ── Dropdown toggles ────────────────────────────────────────────────────────────── */
    var labels = document.querySelectorAll( ".mf-has-dropdown > .mf-nav-label" );
    Array.prototype.forEach.call( labels, function ( label ) {
        label.addEventListener( "click", function ( e ) {
            e.stopPropagation();
            var parent = this.parentNode;
            var open = document.querySelectorAll( ".mf-has-dropdown.mf-dropdown-open" );
            Array.prototype.forEach.call( open, function ( s ) {
                if ( s !== parent ) { s.classList.remove( "mf-dropdown-open" ); }
            } );
            parent.classList.toggle( "mf-dropdown-open" );
        } );
    } );

    document.addEventListener( "click", function () {
        var open = document.querySelectorAll( ".mf-has-dropdown.mf-dropdown-open" );
        Array.prototype.forEach.call( open, function ( el ) {
            el.classList.remove( "mf-dropdown-open" );
        } );
    } );



    /* ── Search bar suggestions ──────────────────────────────────────────────── */
    var searchInput = document.getElementById( "mf-search-input" );
    var suggestionsBox = document.getElementById( "mf-search-suggestions" );
    var searchDebounce = null;

    if ( searchInput && suggestionsBox ) {
        searchInput.addEventListener( "input", function () {
            var query = searchInput.value.trim();
            clearTimeout( searchDebounce );
            if ( query.length < 2 ) {
                suggestionsBox.style.display = "none";
                suggestionsBox.innerHTML = "";
                return;
            }
            searchDebounce = setTimeout( function () {
                new mw.Api().get( {
                    action: "opensearch",
                    search: query,
                    limit: 8,
                    namespace: 0
                } ).then( function ( data ) {
                    var titles = data[ 1 ] || [];
                    if ( titles.length === 0 ) {
                        suggestionsBox.style.display = "none";
                        suggestionsBox.innerHTML = "";
                        return;
                    }
                    suggestionsBox.innerHTML = "";
                    titles.forEach( function ( title ) {
                        var a = document.createElement( "a" );
                        a.href = mw.util.getUrl( title );
                        a.textContent = title;
                        a.className = "mf-suggestion";
                        suggestionsBox.appendChild( a );
                    } );
                    suggestionsBox.style.display = "block";
                } );
            }, 200 );
        } );

        searchInput.addEventListener( "focus", function () {
            if ( suggestionsBox.children.length > 0 ) {
                suggestionsBox.style.display = "block";
            }
        } );

        document.addEventListener( "click", function ( e ) {
            if ( !e.target.closest( "#mf-search-bar" ) ) {
                suggestionsBox.style.display = "none";
            }
        } );
    }
}() );