No edit summary
No edit summary
Line 228: Line 228:


/**
/**
  * Quick Search — Navigate directly to wiki page (DEBUG VERSION)
  * Quick Search — Navigate directly to wiki page
  * Intercepts the Page Forms #forminput inside #crt-quick-search so that
  * Intercepts the Page Forms #forminput inside #crt-quick-search so that
  * clicking "Go" (or pressing Enter, or selecting an autocomplete suggestion)
  * clicking "Go" (or pressing Enter, or selecting an autocomplete suggestion)
  * navigates to the wiki article instead of opening the form editor.
  * navigates to the wiki article instead of opening the form editor.
*
* Note: Page Forms creates the actual <input> element dynamically inside a
* .pfFormInputWrapper div, so we must wait for it to appear before attaching
* listeners.
  */
  */
(function () {
(function () {
     'use strict';
     'use strict';


     var PAGE = mw.config.get('wgPageName');
     if (mw.config.get('wgPageName') !== 'Main_Page') return;
    console.log('[QuickSearch] wgPageName =', PAGE);


     // Only run on Main Page
     function goToPage(input) {
    if (PAGE !== 'Main_Page') {
        var value = input.value.trim();
        console.log('[QuickSearch] Not Main_Page, exiting.');
        if (value) {
         return;
            window.location.href = mw.util.getUrl(value);
         }
     }
     }


     function initQuickSearch() {
     function setup(container, form) {
         console.log('[QuickSearch] initQuickSearch() called');
         // The actual text input created by Page Forms inside .pfFormInputWrapper
        var input = container.querySelector('.pfFormInputWrapper input[type="text"], .pfFormInputWrapper input:not([type="hidden"])');
        if (!input) return false;
 
        // Intercept form submission (Enter key / Go button)
        form.addEventListener('submit', function (e) {
            e.preventDefault();
            e.stopPropagation();
            goToPage(input);
        });
 
        // Navigate immediately when an autocomplete suggestion is selected
        $(input).on('pfautocomplete', function () {
            setTimeout(function () { goToPage(input); }, 50);
        });
 
        return true;
    }


    function init() {
         var container = document.getElementById('crt-quick-search');
         var container = document.getElementById('crt-quick-search');
        console.log('[QuickSearch] container #crt-quick-search =', container);
         if (!container) return;
         if (!container) return;
        // Log all child elements to see structure
        console.log('[QuickSearch] container innerHTML (first 500 chars):', container.innerHTML.substring(0, 500));


         var form = container.querySelector('form');
         var form = container.querySelector('form');
        console.log('[QuickSearch] form =', form);
         if (!form) return;
         if (!form) {
            console.log('[QuickSearch] No <form> found. Trying other selectors...');
            // Page Forms might not wrap in a <form> — try the div it creates
            var pfDiv = container.querySelector('.pfFormInputWrapper, .forminput-wrapper, div[id^="pf"]');
            console.log('[QuickSearch] PF wrapper div =', pfDiv);
        }


         var input = container.querySelector('input[type="text"]');
         // Try immediately — if PF has already initialized, we're done
        console.log('[QuickSearch] input[type="text"] =', input);
         if (setup(container, form)) return;
        if (!input) {
            // Try broader selectors
            var anyInput = container.querySelector('input');
            console.log('[QuickSearch] any input =', anyInput);
            if (anyInput) {
                console.log('[QuickSearch] input type =', anyInput.type, ', name =', anyInput.name, ', class =', anyInput.className);
            }
        }
 
         if (!form || !input) {
            console.warn('[QuickSearch] Could not find form or input. Aborting.');
            return;
        }


         function goToPage() {
         // Otherwise watch for PF to inject the <input> dynamically
            var value = input.value.trim();
        var observer = new MutationObserver(function (mutations, obs) {
             console.log('[QuickSearch] goToPage() value =', JSON.stringify(value));
             if (setup(container, form)) {
            if (value) {
                 obs.disconnect();
                 var url = mw.util.getUrl(value);
                console.log('[QuickSearch] Navigating to:', url);
                window.location.href = url;
             }
             }
        }
        form.addEventListener('submit', function (e) {
            console.log('[QuickSearch] Form submit intercepted!');
            e.preventDefault();
            e.stopPropagation();
            goToPage();
         });
         });
        observer.observe(container, { childList: true, subtree: true });


         if (typeof $ !== 'undefined') {
         // Safety timeout: stop observing after 10s
            $(input).on('pfautocomplete', function () {
        setTimeout(function () { observer.disconnect(); }, 10000);
                console.log('[QuickSearch] pfautocomplete event fired');
                setTimeout(goToPage, 50);
            });
            console.log('[QuickSearch] pfautocomplete listener attached');
        } else {
            console.warn('[QuickSearch] jQuery not available');
        }
 
        console.log('[QuickSearch] Setup complete!');
     }
     }


     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
        console.log('[QuickSearch] DOM loading, adding DOMContentLoaded listener');
         document.addEventListener('DOMContentLoaded', init);
         document.addEventListener('DOMContentLoaded', initQuickSearch);
     } else {
     } else {
         console.log('[QuickSearch] DOM already ready, calling initQuickSearch now');
         init();
        initQuickSearch();
     }
     }
})();
})();

Revision as of 22:13, 18 February 2026

/* Any JavaScript here will be loaded for all users on every page load. */

/**
 * Night Mode Toggle for Minerva Neue
 * Theme is applied early via LocalSettings.php hook to prevent FOUC
 * This script just adds the toggle button
 */
(function () {
    'use strict';

    var STORAGE_KEY = 'minerva-night-mode';

    // SVG icons (20x20)
    var MOON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
    var SUN_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';

    /**
     * Detect OS color scheme preference
     */
    function getOSPreference() {
        if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
            return 'night';
        }
        return 'day';
    }

    /**
     * Get saved theme, or detect from OS on first visit
     */
    function getSavedTheme() {
        try {
            var saved = localStorage.getItem(STORAGE_KEY);
            if (saved) {
                return saved;
            }
            // First visit: detect OS preference and save it
            var osPreference = getOSPreference();
            localStorage.setItem(STORAGE_KEY, osPreference);
            return osPreference;
        } catch (e) {
            return getOSPreference();
        }
    }

    function saveTheme(theme) {
        try {
            localStorage.setItem(STORAGE_KEY, theme);
        } catch (e) {}
    }

    function applyTheme(theme) {
        var html = document.documentElement;
        html.classList.remove('skin-theme-clientpref-day', 'skin-theme-clientpref-night');
        html.classList.add('skin-theme-clientpref-' + theme);
    }

    function updateButtonState(button, theme) {
        var icon = button.querySelector('.nightmode-icon');
        
        if (theme === 'night') {
            icon.innerHTML = SUN_SVG;
            button.setAttribute('title', 'Switch to light mode');
        } else {
            icon.innerHTML = MOON_SVG;
            button.setAttribute('title', 'Switch to dark mode');
        }
    }

    function createToggleButton() {
        var currentTheme = getSavedTheme();

        var li = document.createElement('li');
        
        var button = document.createElement('a');
        button.setAttribute('role', 'button');
        button.setAttribute('href', '#');
        button.id = 'ca-nightmode';
        button.className = 'cdx-button cdx-button--size-large cdx-button--fake-button cdx-button--fake-button--enabled cdx-button--icon-only cdx-button--weight-quiet';
        button.style.cursor = 'pointer';

        var icon = document.createElement('span');
        icon.className = 'nightmode-icon';
        icon.style.cssText = 'display:flex;align-items:center;justify-content:center;width:20px;height:20px;';

        var label = document.createElement('span');
        label.className = 'sr-only';
        label.style.cssText = 'position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0;';
        label.textContent = currentTheme === 'night' ? 'Light mode' : 'Dark mode';
        
        button.appendChild(icon);
        button.appendChild(label);
        li.appendChild(button);

        updateButtonState(button, currentTheme);

        button.addEventListener('click', function (e) {
            e.preventDefault();
            var newTheme = getSavedTheme() === 'night' ? 'day' : 'night';
            saveTheme(newTheme);
            applyTheme(newTheme);
            updateButtonState(button, newTheme);
        });

        return li;
    }

    function insertButton() {
        var notificationsList = document.querySelector('.minerva-notifications ul');
        
        if (notificationsList) {
            notificationsList.appendChild(createToggleButton());
        } else {
            var userNav = document.querySelector('.minerva-user-navigation');
            if (userNav) {
                var container = document.createElement('div');
                container.className = 'minerva-notifications';
                var ul = document.createElement('ul');
                ul.appendChild(createToggleButton());
                container.appendChild(ul);
                userNav.insertBefore(container, userNav.firstChild);
            }
        }
    }

    // Insert button when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', insertButton);
    } else {
        insertButton();
    }
})();





/**
 * Source Editor Warning Banner
 * Shows a styled warning above the editor when users open the standard
 * source editor on CRT model or AV device pages, directing them to the
 * Page Forms editor instead.
 *
 */
(function () {
    'use strict';

    // Only run on action=edit in the main namespace (ns 0)
    if (mw.config.get('wgAction') !== 'edit' || mw.config.get('wgNamespaceNumber') !== 0) {
        return;
    }

    mw.hook('wikipage.editform').add(function () {
        // Don't add the banner twice
        if (document.getElementById('ecrt-source-edit-warning')) return;

        var textarea = document.getElementById('wpTextbox1');
        if (!textarea) return;

        var content = textarea.value;
        var formName = null;

        if (/\{\{CRT model/i.test(content)) {
            formName = 'CRT model';
        } else if (/\{\{AV device/i.test(content)) {
            formName = 'AV device';
        }

        if (!formName) return;

        var pageName = mw.config.get('wgPageName');
        var formEditUrl = mw.util.getUrl('Special:FormEdit/' + formName + '/' + pageName);
        var typeName = formName === 'CRT model' ? 'CRT model' : 'AV device';

        var banner = document.createElement('div');
        banner.id = 'ecrt-source-edit-warning';
        banner.style.cssText = [
            'background-color: #fef2f2',
            'border: 1px solid #fca5a5',
            'border-left: 4px solid #ef4444',
            'border-radius: 8px',
            'padding: 16px 20px',
            'margin-bottom: 16px',
            'font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
            'line-height: 1.5'
        ].join(';');

        banner.innerHTML =
            '<div style="display:flex;align-items:flex-start;gap:12px;">' +
                '<span style="color:#dc2626;font-size:20px;line-height:1;flex-shrink:0;">⚠️</span>' +
                '<div>' +
                    '<div style="color:#991b1b;font-weight:600;font-size:15px;margin-bottom:4px;">' +
                        'You are using the source editor' +
                    '</div>' +
                    '<div style="color:#7f1d1d;font-size:13px;line-height:1.6;">' +
                        'This page is a <strong>' + typeName + '</strong>. ' +
                        'To avoid formatting issues and keep data consistent, please use the ' +
                        '<strong>structured form editor</strong> instead.' +
                        '<br>' +
                        '<a href="' + mw.html.escape(formEditUrl) + '" ' +
                            'style="display:inline-block;margin-top:10px;padding:8px 16px;' +
                            'background-color:#dc2626;color:#fff;font-weight:600;font-size:13px;' +
                            'border-radius:6px;text-decoration:none;">' +
                            '&#9998;&nbsp; Edit with form editor' +
                        '</a>' +
                        '<span style="display:inline-block;margin-top:10px;margin-left:12px;' +
                            'color:#991b1b;font-size:12px;font-style:italic;">' +
                            'Only use the source editor if you know what you\'re doing.' +
                        '</span>' +
                        '<div style="margin-top:10px;color:#991b1b;font-size:12px;">' +
                            '<strong>Tip:</strong> To go directly to the form editor next time, ' +
                            'click the <strong>"Edit this data"</strong> button on the page instead of "Edit".' +
                        '</div>' +
                    '</div>' +
                '</div>' +
            '</div>';

        // Insert above the edit form
        var editForm = document.getElementById('editform');
        if (editForm) {
            editForm.parentNode.insertBefore(banner, editForm);
        }
    });
})();





/**
 * Quick Search — Navigate directly to wiki page
 * Intercepts the Page Forms #forminput inside #crt-quick-search so that
 * clicking "Go" (or pressing Enter, or selecting an autocomplete suggestion)
 * navigates to the wiki article instead of opening the form editor.
 *
 * Note: Page Forms creates the actual <input> element dynamically inside a
 * .pfFormInputWrapper div, so we must wait for it to appear before attaching
 * listeners.
 */
(function () {
    'use strict';

    if (mw.config.get('wgPageName') !== 'Main_Page') return;

    function goToPage(input) {
        var value = input.value.trim();
        if (value) {
            window.location.href = mw.util.getUrl(value);
        }
    }

    function setup(container, form) {
        // The actual text input created by Page Forms inside .pfFormInputWrapper
        var input = container.querySelector('.pfFormInputWrapper input[type="text"], .pfFormInputWrapper input:not([type="hidden"])');
        if (!input) return false;

        // Intercept form submission (Enter key / Go button)
        form.addEventListener('submit', function (e) {
            e.preventDefault();
            e.stopPropagation();
            goToPage(input);
        });

        // Navigate immediately when an autocomplete suggestion is selected
        $(input).on('pfautocomplete', function () {
            setTimeout(function () { goToPage(input); }, 50);
        });

        return true;
    }

    function init() {
        var container = document.getElementById('crt-quick-search');
        if (!container) return;

        var form = container.querySelector('form');
        if (!form) return;

        // Try immediately — if PF has already initialized, we're done
        if (setup(container, form)) return;

        // Otherwise watch for PF to inject the <input> dynamically
        var observer = new MutationObserver(function (mutations, obs) {
            if (setup(container, form)) {
                obs.disconnect();
            }
        });
        observer.observe(container, { childList: true, subtree: true });

        // Safety timeout: stop observing after 10s
        setTimeout(function () { observer.disconnect(); }, 10000);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();





// Load CRT Search module on the CRT search page
if (mw.config.get('wgPageName') === 'CRT_Search') {
    mw.loader.load('/wiki/MediaWiki:CRT_Search.js?action=raw&ctype=text/javascript');
}

// Load AV Device Search module on the AV Device Search page
if (mw.config.get('wgPageName') === 'AV_Device_Search') {
    mw.loader.load('/wiki/MediaWiki:AV_Device_Search.js?action=raw&ctype=text/javascript');
}
MediaWiki Appliance - Powered by TurnKey Linux