MediaWiki:Common.js: Difference between revisions

No edit summary
No edit summary
Line 229: Line 229:
/**
/**
  * Quick Search Widget — Custom autocomplete for CRT model lookup
  * Quick Search Widget — Custom autocomplete for CRT model lookup
  * Replaces Page Forms #forminput with a fully custom implementation.
  * The Main Page wikitext contains the styled container, label, and a
  * Fetches suggestions from the PF autocomplete API, navigates directly
* "Loading search…" placeholder (using only MW-allowed HTML tags).
  * to the wiki page on selection.
  * This script replaces the placeholder with the actual input, button,
  * and dropdown, then wires up event handlers.
  */
  */
(function () {
(function () {
Line 245: Line 246:


     // ── Helpers ─────────────────────────────────────────────────────
     // ── Helpers ─────────────────────────────────────────────────────
    function getPageUrl(title) {
        return mw.util.getUrl(title);
    }
     function navigate(title) {
     function navigate(title) {
         title = (title || '').trim();
         title = (title || '').trim();
         if (title) window.location.href = getPageUrl(title);
         if (title) window.location.href = mw.util.getUrl(title);
     }
     }


Line 260: Line 257:
         style.id = 'ecrt-qs-styles';
         style.id = 'ecrt-qs-styles';
         style.textContent = [
         style.textContent = [
             '#crt-quick-search {',
            /* Container & label — match the server-rendered box */
             '.ecrt-qs-container {',
             '  background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);',
             '  background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);',
             '  border: 2px solid #7dd3fc;',
             '  border: 2px solid #7dd3fc;',
Line 274: Line 272:
             '  font-size: 0.9rem;',
             '  font-size: 0.9rem;',
             '}',
             '}',
            /* Placeholder shown before JS loads */
            '.ecrt-qs-placeholder {',
            '  padding: 0.625rem 0.875rem;',
            '  font-size: 0.95rem;',
            '  border: 1.5px solid #cbd5e1;',
            '  border-radius: 0.5rem;',
            '  background: #f1f5f9;',
            '  color: #94a3b8;',
            '  font-style: italic;',
            '}',
            /* Interactive controls */
             '.ecrt-qs-wrap {',
             '.ecrt-qs-wrap {',
             '  position: relative;',
             '  position: relative;',
Line 297: Line 308:
             '  box-shadow: 0 0 0 3px rgba(2,132,199,0.15);',
             '  box-shadow: 0 0 0 3px rgba(2,132,199,0.15);',
             '}',
             '}',
             '.ecrt-qs-input::placeholder {',
             '.ecrt-qs-input::placeholder { color: #94a3b8; }',
            '  color: #94a3b8;',
            '}',
             '.ecrt-qs-btn {',
             '.ecrt-qs-btn {',
             '  padding: 0.625rem 1.25rem;',
             '  padding: 0.625rem 1.25rem;',
Line 354: Line 363:
             '}',
             '}',


             /* Night‑mode overrides */
             /* Night-mode overrides */
             '.skin-theme-clientpref-night #crt-quick-search {',
             '.skin-theme-clientpref-night .ecrt-qs-container {',
             '  background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);',
             '  background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);',
             '  border-color: #334155;',
             '  border-color: #334155;',
             '}',
             '}',
             '.skin-theme-clientpref-night .ecrt-qs-label { color: #7dd3fc; }',
             '.skin-theme-clientpref-night .ecrt-qs-label { color: #7dd3fc; }',
            '.skin-theme-clientpref-night .ecrt-qs-placeholder {',
            '  background: #0f172a;',
            '  border-color: #334155;',
            '  color: #475569;',
            '}',
             '.skin-theme-clientpref-night .ecrt-qs-input {',
             '.skin-theme-clientpref-night .ecrt-qs-input {',
             '  background: #1e293b;',
             '  background: #1e293b;',
Line 390: Line 404:
     }
     }


     // ── Build the widget ────────────────────────────────────────────
     // ── Init ────────────────────────────────────────────────────────
     function build(container) {
     function init() {
        var container = document.getElementById('crt-quick-search');
        var wrap      = document.getElementById('ecrt-qs-controls');
        if (!container || !wrap) return;
 
         injectStyles();
         injectStyles();


         // Label
         // ── Build interactive controls ──────────────────────────────
        var label = document.createElement('div');
        label.className = 'ecrt-qs-label';
        label.textContent = '\uD83D\uDD0D Quick Search';
 
        // Wrapper (for positioning dropdown)
        var wrap = document.createElement('div');
        wrap.className = 'ecrt-qs-wrap';
 
        // Row: input + button
         var row = document.createElement('div');
         var row = document.createElement('div');
         row.className = 'ecrt-qs-row';
         row.className = 'ecrt-qs-row';
Line 421: Line 430:
         row.appendChild(btn);
         row.appendChild(btn);


        // Dropdown
         var dropdown = document.createElement('div');
         var dropdown = document.createElement('div');
         dropdown.className = 'ecrt-qs-dropdown';
         dropdown.className = 'ecrt-qs-dropdown';


        // Replace placeholder with real controls
        wrap.innerHTML = '';
         wrap.appendChild(row);
         wrap.appendChild(row);
         wrap.appendChild(dropdown);
         wrap.appendChild(dropdown);
        container.appendChild(label);
        container.appendChild(wrap);


         // ── State ───────────────────────────────────────────────────
         // ── State ───────────────────────────────────────────────────
         var items = [];       // current suggestion titles
         var items     = [];
         var activeIdx = -1;   // keyboard-highlighted index (-1 = none)
         var activeIdx = -1;
         var timer = null;
         var timer     = null;


         function openDropdown()  { dropdown.classList.add('is-open'); }
         function openDropdown()  { dropdown.classList.add('is-open'); }
Line 439: Line 447:
             dropdown.classList.remove('is-open');
             dropdown.classList.remove('is-open');
             activeIdx = -1;
             activeIdx = -1;
        }
        function setActive(idx) {
            var els = dropdown.querySelectorAll('.ecrt-qs-item');
            els.forEach(function (el) { el.classList.remove('is-active'); });
            activeIdx = idx;
            if (idx >= 0 && idx < els.length) {
                els[idx].classList.add('is-active');
                els[idx].scrollIntoView({ block: 'nearest' });
            }
         }
         }


Line 459: Line 477:
                 el.className = 'ecrt-qs-item';
                 el.className = 'ecrt-qs-item';
                 el.textContent = r.title;
                 el.textContent = r.title;
                el.setAttribute('data-index', i);
                 el.addEventListener('mousedown', function (e) {
                 el.addEventListener('mousedown', function (e) {
                    // mousedown instead of click so it fires before blur
                     e.preventDefault();
                     e.preventDefault();
                     navigate(r.title);
                     navigate(r.title);
Line 482: Line 498:
             dropdown.appendChild(msg);
             dropdown.appendChild(msg);
             openDropdown();
             openDropdown();
        }
        function setActive(idx) {
            var els = dropdown.querySelectorAll('.ecrt-qs-item');
            els.forEach(function (el) { el.classList.remove('is-active'); });
            activeIdx = idx;
            if (idx >= 0 && idx < els.length) {
                els[idx].classList.add('is-active');
                // Scroll into view if needed
                els[idx].scrollIntoView({ block: 'nearest' });
            }
         }
         }


Line 505: Line 510:
                 .then(function (r) { return r.json(); })
                 .then(function (r) { return r.json(); })
                 .then(function (data) {
                 .then(function (data) {
                    // Only render if input still matches (avoid stale results)
                     if (input.value.trim().toLowerCase() === substr.toLowerCase()) {
                     if (input.value.trim().toLowerCase() === substr.toLowerCase()) {
                         renderItems((data && data.pfautocomplete) || []);
                         renderItems((data && data.pfautocomplete) || []);
Line 531: Line 535:
         input.addEventListener('keydown', function (e) {
         input.addEventListener('keydown', function (e) {
             if (!dropdown.classList.contains('is-open') || !items.length) {
             if (!dropdown.classList.contains('is-open') || !items.length) {
                // Enter with no dropdown → navigate to whatever is typed
                 if (e.key === 'Enter') {
                 if (e.key === 'Enter') {
                     e.preventDefault();
                     e.preventDefault();
Line 566: Line 569:
         });
         });


        // Close dropdown when clicking outside
         document.addEventListener('click', function (e) {
         document.addEventListener('click', function (e) {
             if (!wrap.contains(e.target)) {
             if (!container.contains(e.target)) {
                 closeDropdown();
                 closeDropdown();
             }
             }
         });
         });


        // Re-open on focus if there's already a query worth showing
         input.addEventListener('focus', function () {
         input.addEventListener('focus', function () {
             if (items.length && input.value.trim().length >= MIN_CHARS) {
             if (items.length && input.value.trim().length >= MIN_CHARS) {
Line 581: Line 582:
     }
     }


     // ── Init ────────────────────────────────────────────────────────
     // ── Run ─────────────────────────────────────────────────────────
    function init() {
        var container = document.getElementById('crt-quick-search');
        if (!container) return;
        build(container);
    }
 
     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', init);
         document.addEventListener('DOMContentLoaded', init);
MediaWiki Appliance - Powered by TurnKey Linux