MediaWiki:Common.js

From zunagi

Revision as of 14:03, 11 July 2026 by Lee G (talk | contribs)

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. */
$( function () {
    const form = document.getElementById( 'zunagi-search-form' );
    const box = document.getElementById( 'zunagi-search-suggestions' );

    if ( !form || !box ) {
        return;
    }

    const input = form.querySelector(
        'input[type="search"], input[name="search"], input[name="searchText"]'
    );

    if ( !input ) {
        return;
    }

    let timer = null;
    let requestNumber = 0;
    let selectedIndex = -1;
    let results = [];

    function closeSuggestions() {
        box.innerHTML = '';
        box.classList.remove( 'is-open' );
        selectedIndex = -1;
        results = [];
    }

    function openPage( title ) {
        window.location.href = mw.util.getUrl( title );
    }

    function stripHtml( html ) {
        const element = document.createElement( 'div' );
        element.innerHTML = html;
        return element.textContent || '';
    }

    function renderSuggestions( pages ) {
        box.innerHTML = '';
        results = pages;
        selectedIndex = -1;

        if ( !pages.length ) {
            closeSuggestions();
            return;
        }

        pages.forEach( function ( page ) {
            const link = document.createElement( 'a' );
            const title = document.createElement( 'span' );
            const description = document.createElement( 'span' );

            link.href = mw.util.getUrl( page.title );
            link.className = 'zunagi-search-suggestion';

            title.className = 'zunagi-search-suggestion-title';
            title.textContent = page.title;

            description.className = 'zunagi-search-suggestion-description';
            description.textContent = stripHtml( page.snippet || '' );

            link.appendChild( title );

            if ( description.textContent ) {
                link.appendChild( description );
            }

            box.appendChild( link );
        } );

        box.classList.add( 'is-open' );
    }

    function updateSelection() {
        const items = box.querySelectorAll( '.zunagi-search-suggestion' );

        items.forEach( function ( item, index ) {
            item.classList.toggle(
                'is-selected',
                index === selectedIndex
            );
        } );
    }

    input.setAttribute( 'autocomplete', 'off' );

    input.addEventListener( 'input', function () {
        const query = input.value.trim();

        clearTimeout( timer );

        if ( query.length < 2 ) {
            closeSuggestions();
            return;
        }

        timer = setTimeout( function () {
            const thisRequest = ++requestNumber;

            new mw.Api().get( {
                action: 'query',
                list: 'search',
                srsearch: query,
                srnamespace: 0,
                srlimit: 8,
                srprop: 'snippet',
                format: 'json'
            } ).done( function ( data ) {
                if ( thisRequest !== requestNumber ) {
                    return;
                }

                renderSuggestions(
                    data.query && data.query.search
                        ? data.query.search
                        : []
                );
            } ).fail( function () {
                closeSuggestions();
            } );
        }, 300 );
    } );

    input.addEventListener( 'keydown', function ( event ) {
        const items = box.querySelectorAll(
            '.zunagi-search-suggestion'
        );

        if ( event.key === 'ArrowDown' && items.length ) {
            event.preventDefault();
            selectedIndex =
                ( selectedIndex + 1 ) % items.length;
            updateSelection();
        } else if ( event.key === 'ArrowUp' && items.length ) {
            event.preventDefault();
            selectedIndex =
                selectedIndex <= 0
                    ? items.length - 1
                    : selectedIndex - 1;
            updateSelection();
        } else if (
            event.key === 'Enter' &&
            selectedIndex >= 0 &&
            results[ selectedIndex ]
        ) {
            event.preventDefault();
            openPage( results[ selectedIndex ].title );
        } else if ( event.key === 'Escape' ) {
            closeSuggestions();
        }
    } );

    document.addEventListener( 'click', function ( event ) {
        if (
            !form.contains( event.target ) &&
            !box.contains( event.target )
        ) {
            closeSuggestions();
        }
    } );
} );