Jump to content

MediaWiki:Minerva.js: Difference between revisions

From Vanipedia
Kashya (talk | contribs)
No edit summary
Kashya (talk | contribs)
No edit summary
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* Mobile and Desktop VaniSearch Popup Launcher */
/* VaniSearch Popup Launcher - Desktop & Mobile */
(function() {
(function() {
     function triggerVaniSearchPopup(queryText) {
     var lockPopupSubmitUntil = 0;
         // 1. If the VaniSearch extension provides a global popup function on the page, call it
 
         if (typeof window.openVaniSearch === 'function') {
    // Override global open_modal() so even if an inline HTML attribute calls it on keydown,
             window.openVaniSearch(queryText);
    // it cannot focus #mspu_txt_input until after the Enter key is released.
            return;
    window.open_modal = function() {
        var ori = document.getElementById('ori_search') || document.getElementById('searchInput');
        var modal = document.getElementById('mspu_div_wrapper');
        var inpSearch = document.getElementById('mspu_txt_input');
        var submitBtn = document.getElementById('mspu_but_submit');
 
        if (!modal || !inpSearch) return false;
 
        lockPopupSubmitUntil = Date.now() + 500;
 
         // Temporarily disable the popup's submit button for 500ms so a held Enter key cannot trigger it
        if (submitBtn) {
            submitBtn.disabled = true;
            setTimeout(function() { submitBtn.disabled = false; }, 500);
        }
 
         if (typeof window.sync_but_search === 'function') {
             try { window.sync_but_search(); } catch (e) {}
         }
         }
         if (typeof window.VaniSearch === 'object' && typeof window.VaniSearch.open === 'function') {
 
             window.VaniSearch.open(queryText);
         if (ori) {
             return;
             inpSearch.value = ori.value;
             ori.blur();
         }
         }


         // 2. If there is a hidden popup/modal element already in the DOM, open it and fill the input
        modal.style.display = 'block';
         var modal = document.querySelector('#vani-search-modal, #vanisearch-popup, .vani-search-modal, .vanisearch-overlay');
 
         if (modal) {
         // Delay focusing the popup input until the Enter keystroke is completely finished
            modal.style.display = 'flex';
        setTimeout(function() {
            modal.classList.add('is-open', 'active', 'visible');
            inpSearch.focus();
            var modalInput = modal.querySelector('input[type="text"], input[type="search"]');
        }, 250);
             if (modalInput) {
 
                 modalInput.value = queryText || '';
        return false;
                modalInput.focus();
    };
            }
 
             return;
    function protectModalForm() {
         var modal = document.getElementById('mspu_div_wrapper');
         if (!modal || modal.dataset.vaniHardLocked) return;
        modal.dataset.vaniHardLocked = 'true';
 
        // Remove any injected magnifying glass inside the popup that might have a redirect attached
        var strayBtns = modal.querySelectorAll('.vani-search-trigger-btn');
        strayBtns.forEach(function(b) { b.remove(); });
 
        var mForm = modal.querySelector('form');
        if (mForm) {
             // Completely block form submission if the modal opened less than 500ms ago
            mForm.addEventListener('submit', function(e) {
                 if (Date.now() < lockPopupSubmitUntil) {
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }
             }, true);
         }
         }


         // 3. Otherwise, open the Special:VaniSearch extension page first (without forcing immediate results)
         var inpSearch = document.getElementById('mspu_txt_input');
         // Stores the typed word in sessionStorage so Special:VaniSearch can pre-fill its input box
         if (inpSearch) {
        if (queryText) {
            // Strip name="search" while locked or stop Enter propagation during the lock window
            try { sessionStorage.setItem('vani_pending_query', queryText); } catch (e) {}
            ['keydown', 'keypress', 'keyup'].forEach(function(evt) {
                inpSearch.addEventListener(evt, function(e) {
                    if (Date.now() < lockPopupSubmitUntil && (e.key === 'Enter' || e.keyCode === 13)) {
                        e.preventDefault();
                        e.stopPropagation();
                        e.stopImmediatePropagation();
                        return false;
                    }
                }, true);
            });
         }
         }
        window.location.href = '/wiki/Special:VaniSearch';
     }
     }


     // If we just landed on Special:VaniSearch, pre-fill the extension's input box with the word the user typed
     function buildCleanSearchBox(oldElement, isMobile) {
    if (window.location.pathname.indexOf('Special:VaniSearch') !== -1) {
        var wrap = document.createElement('div');
         window.addEventListener('DOMContentLoaded', function() {
        wrap.className = isMobile ? 'vani-mobile-search-wrap' : 'vani-desktop-search-wrap';
             try {
         wrap.style.cssText = isMobile
                var pending = sessionStorage.getItem('vani_pending_query');
            ? 'flex:1 1 auto !important; display:flex !important; align-items:center !important; position:relative !important; margin:0 10px !important; z-index:999999 !important;'
                if (pending) {
             : 'display:flex !important; align-items:center !important; position:relative !important; width:100% !important; max-width:280px !important; z-index:999999 !important;';
                    sessionStorage.removeItem('vani_pending_query');
 
                    setTimeout(function() {
        var inp = document.createElement('input');
                        var extInput = document.querySelector('#content input[type="text"], #content input[type="search"], .vanisearch-input');
        inp.type = 'text'; // Must NOT be 'search' so older Common.js selectors ignore it
                        if (extInput) {
        inp.id = 'ori_search';
                            extInput.value = pending;
        // Intentionally NO inp.name = 'search' so older Common.js input[name="search"] selectors ignore it!
                            extInput.focus();
        inp.className = 'vani-clean-search-input';
                            extInput.dispatchEvent(new Event('input', { bubbles: true }));
        inp.placeholder = 'Search Vanipedia';
                        }
        inp.autocomplete = 'off';
                    }, 150);
        inp.style.cssText = 'width:100% !important; height:32px !important; background:#1f3e45 !important; background-image:none !important; color:#ffffff !important; -webkit-text-fill-color:#ffffff !important; border:1.5px solid #f5d77f !important; border-radius:16px !important; padding:2px 32px 2px 12px !important; font-size:15px !important; outline:none !important; box-sizing:border-box !important;';
                }
 
             } catch (e) {}
        var btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'vani-inline-go';
        btn.innerHTML = '🔍';
        btn.style.cssText = 'position:absolute !important; right:8px !important; top:50% !important; transform:translateY(-50%) !important; background:none !important; border:none !important; font-size:15px !important; cursor:pointer !important; padding:0 !important; line-height:1 !important; z-index:20 !important;';
 
        ['click', 'focus', 'focusin', 'touchstart', 'input', 'keypress'].forEach(function(evtType) {
             inp.addEventListener(evtType, function(e) {
                e.stopPropagation();
            }, true);
         });
         });
        // Swallow keydown for Enter so no document/window listener sees it
        inp.addEventListener('keydown', function(e) {
            e.stopPropagation();
            if (e.key === 'Enter' || e.keyCode === 13) {
                e.preventDefault();
                e.stopImmediatePropagation();
                window.open_modal();
            }
        }, true);
        btn.addEventListener('click', function(e) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            window.open_modal();
        }, true);
        wrap.appendChild(inp);
        wrap.appendChild(btn);
        oldElement.replaceWith(wrap);
     }
     }


     function installVaniSearch() {
     function setupAllSearchBars() {
         // 1. Mobile (Minerva header)
        protectModalForm();
 
         // 1. Mobile Header
         var minervaHeader = document.querySelector('.minerva-header');
         var minervaHeader = document.querySelector('.minerva-header');
         if (minervaHeader) {
         if (minervaHeader) {
             document.documentElement.classList.remove('overlay-enabled');
             document.documentElement.classList.remove('overlay-enabled');
             document.body.classList.remove('search-enabled', 'overlay-enabled');
             document.body.classList.remove('search-enabled', 'overlay-enabled');
             var b = minervaHeader.querySelector('.branding-box');
             var b = minervaHeader.querySelector('.branding-box');
             if (b) b.style.setProperty('display', 'none', 'important');
             if (b) b.style.setProperty('display', 'none', 'important');


             var f = minervaHeader.querySelector('.minerva-search-form');
             var mForm = minervaHeader.querySelector('form.minerva-search-form, form#searchform');
             if (f && !document.getElementById('vani-mobile-search-input')) {
             if (mForm && !minervaHeader.querySelector('.vani-mobile-search-wrap')) {
                var d = document.createElement('div');
                 buildCleanSearchBox(mForm, true);
                d.className = 'vani-mobile-search-wrap';
                d.style.cssText = 'flex:1 1 auto !important; display:flex !important; align-items:center !important; position:relative !important; margin:0 10px !important; z-index:999999 !important; pointer-events:auto !important;';
 
                var inp = document.createElement('input');
                inp.type = 'text';
                inp.id = 'vani-mobile-search-input';
                inp.name = 'search';
                inp.placeholder = 'Search Vanipedia';
                inp.autocomplete = 'off';
                inp.style.cssText = 'width:100% !important; height:32px !important; background:#1f3e45 !important; color:#ffffff !important; -webkit-text-fill-color:#ffffff !important; border:1.5px solid #f5d77f !important; border-radius:16px !important; padding:2px 32px 2px 12px !important; font-size:15px !important; outline:none !important; box-sizing:border-box !important; pointer-events:auto !important; user-select:text !important; -webkit-user-select:text !important;';
 
                var btn = document.createElement('button');
                btn.type = 'button';
                btn.innerHTML = '🔍';
                btn.style.cssText = 'position:absolute !important; right:8px !important; top:50% !important; transform:translateY(-50%) !important; background:none !important; border:none !important; font-size:15px !important; cursor:pointer !important; padding:0 !important; line-height:1 !important; z-index:10 !important; pointer-events:auto !important;';
 
                var go = function(e) {
                    if (e) { e.preventDefault(); e.stopPropagation(); }
                    triggerVaniSearchPopup(inp.value.trim());
                };
 
                 btn.addEventListener('click', go, true);
                inp.addEventListener('keydown', function(e) {
                    e.stopPropagation();
                    if (e.key === 'Enter' || e.keyCode === 13) go(e);
                }, true);
 
                d.appendChild(inp);
                d.appendChild(btn);
                f.replaceWith(d);
             }
             }
         }
         }


         // 2. Desktop (Vector header): Add right-side 🔍 icon without blocking extension's native Enter listener
         // 2. Desktop Header (matches wherever #searchform, #searchInput, or #ori_search currently sits, outside the modal)
        var desktopForm = document.querySelector('#p-search form, form#searchform');
         if (!document.querySelector('.vani-desktop-search-wrap') && !minervaHeader) {
         if (desktopForm && !desktopForm.classList.contains('minerva-search-form') && !desktopForm.dataset.vaniBound) {
             var dTarget = document.querySelector('form#searchform, #p-search form, .vector-search-box form');
            desktopForm.dataset.vaniBound = 'true';
             if (!dTarget) {
 
                 var rawInput = document.querySelector('#ori_search, #searchInput, input[type="search"]');
             var dInput = desktopForm.querySelector('input[type="search"], input[name="search"], #searchInput');
                 if (rawInput && !rawInput.closest('#mspu_div_wrapper') && !rawInput.classList.contains('vani-clean-search-input')) {
             if (dInput) {
                    dTarget = rawInput.closest('form') || rawInput.parentElement;
                 dInput.setAttribute('autocomplete', 'off');
                var wrap = dInput.parentElement;
                 if (wrap && !wrap.querySelector('.vani-inline-go')) {
                    wrap.style.position = 'relative';
                    var dBtn = document.createElement('button');
                    dBtn.type = 'button';
                    dBtn.className = 'vani-inline-go';
                    dBtn.innerHTML = '🔍';
                    dBtn.style.cssText = 'position:absolute !important; right:8px !important; top:50% !important; transform:translateY(-50%) !important; background:none !important; border:none !important; font-size:15px !important; cursor:pointer !important; padding:0 !important; line-height:1 !important; z-index:20 !important;';
                    dBtn.addEventListener('click', function(e) {
                        e.preventDefault();
                        // Dispatch an Enter keypress on the desktop input so the extension's native popup listener fires
                        var enterEvent = new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true, cancelable: true });
                        dInput.dispatchEvent(enterEvent);
                    });
                    wrap.appendChild(dBtn);
                 }
                 }
            }
            if (dTarget && !dTarget.closest('#mspu_div_wrapper')) {
                buildCleanSearchBox(dTarget, false);
             }
             }
         }
         }
Line 128: Line 161:


     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', installVaniSearch);
         document.addEventListener('DOMContentLoaded', setupAllSearchBars);
     } else {
     } else {
         installVaniSearch();
         setupAllSearchBars();
     }
     }
     setTimeout(installVaniSearch, 250);
     setTimeout(setupAllSearchBars, 200);
     setTimeout(installVaniSearch, 800);
     setTimeout(setupAllSearchBars, 700);
})();
})();

Latest revision as of 03:16, 13 November 2026

/* VaniSearch Popup Launcher - Desktop & Mobile */
(function() {
    var lockPopupSubmitUntil = 0;

    // Override global open_modal() so even if an inline HTML attribute calls it on keydown,
    // it cannot focus #mspu_txt_input until after the Enter key is released.
    window.open_modal = function() {
        var ori = document.getElementById('ori_search') || document.getElementById('searchInput');
        var modal = document.getElementById('mspu_div_wrapper');
        var inpSearch = document.getElementById('mspu_txt_input');
        var submitBtn = document.getElementById('mspu_but_submit');

        if (!modal || !inpSearch) return false;

        lockPopupSubmitUntil = Date.now() + 500;

        // Temporarily disable the popup's submit button for 500ms so a held Enter key cannot trigger it
        if (submitBtn) {
            submitBtn.disabled = true;
            setTimeout(function() { submitBtn.disabled = false; }, 500);
        }

        if (typeof window.sync_but_search === 'function') {
            try { window.sync_but_search(); } catch (e) {}
        }

        if (ori) {
            inpSearch.value = ori.value;
            ori.blur();
        }

        modal.style.display = 'block';

        // Delay focusing the popup input until the Enter keystroke is completely finished
        setTimeout(function() {
            inpSearch.focus();
        }, 250);

        return false;
    };

    function protectModalForm() {
        var modal = document.getElementById('mspu_div_wrapper');
        if (!modal || modal.dataset.vaniHardLocked) return;
        modal.dataset.vaniHardLocked = 'true';

        // Remove any injected magnifying glass inside the popup that might have a redirect attached
        var strayBtns = modal.querySelectorAll('.vani-search-trigger-btn');
        strayBtns.forEach(function(b) { b.remove(); });

        var mForm = modal.querySelector('form');
        if (mForm) {
            // Completely block form submission if the modal opened less than 500ms ago
            mForm.addEventListener('submit', function(e) {
                if (Date.now() < lockPopupSubmitUntil) {
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }
            }, true);
        }

        var inpSearch = document.getElementById('mspu_txt_input');
        if (inpSearch) {
            // Strip name="search" while locked or stop Enter propagation during the lock window
            ['keydown', 'keypress', 'keyup'].forEach(function(evt) {
                inpSearch.addEventListener(evt, function(e) {
                    if (Date.now() < lockPopupSubmitUntil && (e.key === 'Enter' || e.keyCode === 13)) {
                        e.preventDefault();
                        e.stopPropagation();
                        e.stopImmediatePropagation();
                        return false;
                    }
                }, true);
            });
        }
    }

    function buildCleanSearchBox(oldElement, isMobile) {
        var wrap = document.createElement('div');
        wrap.className = isMobile ? 'vani-mobile-search-wrap' : 'vani-desktop-search-wrap';
        wrap.style.cssText = isMobile
            ? 'flex:1 1 auto !important; display:flex !important; align-items:center !important; position:relative !important; margin:0 10px !important; z-index:999999 !important;'
            : 'display:flex !important; align-items:center !important; position:relative !important; width:100% !important; max-width:280px !important; z-index:999999 !important;';

        var inp = document.createElement('input');
        inp.type = 'text'; // Must NOT be 'search' so older Common.js selectors ignore it
        inp.id = 'ori_search';
        // Intentionally NO inp.name = 'search' so older Common.js input[name="search"] selectors ignore it!
        inp.className = 'vani-clean-search-input';
        inp.placeholder = 'Search Vanipedia';
        inp.autocomplete = 'off';
        inp.style.cssText = 'width:100% !important; height:32px !important; background:#1f3e45 !important; background-image:none !important; color:#ffffff !important; -webkit-text-fill-color:#ffffff !important; border:1.5px solid #f5d77f !important; border-radius:16px !important; padding:2px 32px 2px 12px !important; font-size:15px !important; outline:none !important; box-sizing:border-box !important;';

        var btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'vani-inline-go';
        btn.innerHTML = '🔍';
        btn.style.cssText = 'position:absolute !important; right:8px !important; top:50% !important; transform:translateY(-50%) !important; background:none !important; border:none !important; font-size:15px !important; cursor:pointer !important; padding:0 !important; line-height:1 !important; z-index:20 !important;';

        ['click', 'focus', 'focusin', 'touchstart', 'input', 'keypress'].forEach(function(evtType) {
            inp.addEventListener(evtType, function(e) {
                e.stopPropagation();
            }, true);
        });

        // Swallow keydown for Enter so no document/window listener sees it
        inp.addEventListener('keydown', function(e) {
            e.stopPropagation();
            if (e.key === 'Enter' || e.keyCode === 13) {
                e.preventDefault();
                e.stopImmediatePropagation();
                window.open_modal();
            }
        }, true);

        btn.addEventListener('click', function(e) {
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            window.open_modal();
        }, true);

        wrap.appendChild(inp);
        wrap.appendChild(btn);
        oldElement.replaceWith(wrap);
    }

    function setupAllSearchBars() {
        protectModalForm();

        // 1. Mobile Header
        var minervaHeader = document.querySelector('.minerva-header');
        if (minervaHeader) {
            document.documentElement.classList.remove('overlay-enabled');
            document.body.classList.remove('search-enabled', 'overlay-enabled');
            var b = minervaHeader.querySelector('.branding-box');
            if (b) b.style.setProperty('display', 'none', 'important');

            var mForm = minervaHeader.querySelector('form.minerva-search-form, form#searchform');
            if (mForm && !minervaHeader.querySelector('.vani-mobile-search-wrap')) {
                buildCleanSearchBox(mForm, true);
            }
        }

        // 2. Desktop Header (matches wherever #searchform, #searchInput, or #ori_search currently sits, outside the modal)
        if (!document.querySelector('.vani-desktop-search-wrap') && !minervaHeader) {
            var dTarget = document.querySelector('form#searchform, #p-search form, .vector-search-box form');
            if (!dTarget) {
                var rawInput = document.querySelector('#ori_search, #searchInput, input[type="search"]');
                if (rawInput && !rawInput.closest('#mspu_div_wrapper') && !rawInput.classList.contains('vani-clean-search-input')) {
                    dTarget = rawInput.closest('form') || rawInput.parentElement;
                }
            }
            if (dTarget && !dTarget.closest('#mspu_div_wrapper')) {
                buildCleanSearchBox(dTarget, false);
            }
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', setupAllSearchBars);
    } else {
        setupAllSearchBars();
    }
    setTimeout(setupAllSearchBars, 200);
    setTimeout(setupAllSearchBars, 700);
})();