Ir al contenido

MediaWiki:Common.js

De wikiscba

Nota: Después de publicar, quizás necesite actualizar la caché de su navegador para ver los cambios.

  • Firefox/Safari: Mantenga presionada la tecla Shift mientras pulsa el botón Actualizar, o presiona Ctrl+F5 o Ctrl+R (⌘+R en Mac)
  • Google Chrome: presione Ctrl+Shift+R (⌘+Shift+R en Mac)
  • Edge: mantenga presionada Ctrl mientras pulsa Actualizar, o presione Ctrl+F5
/* Cualquier código JavaScript escrito aquí se cargará para todos los usuarios en cada carga de página */

// Este script resalta y permite navegar entre términos de búsqueda en el artículo destino
(function() {
    // Variables globales para el seguimiento de la navegación
    var currentHighlightIndex = -1;  // Índice del resaltado actual
    var highlightElements = [];      // Array de todos los elementos resaltados

    /**
     * Verifica si llegamos desde una búsqueda
     * @returns {boolean} true si venimos de una búsqueda
     */
    function isComingFromSearch() {
        var referrer = document.referrer;
        return referrer.includes('Special:Search') || 
               referrer.includes('title=Special:Search') ||
               referrer.includes('search=') ||
               referrer.includes('Special:Buscar') ||
               document.location.search.includes('highlight=');
    }

    /**
     * Obtiene los términos de búsqueda del referrer o parámetros de URL
     * @returns {Array} Array de términos de búsqueda
     */
    function getSearchTerms() {
        // Intenta obtener términos del parámetro highlight (usado por MediaWiki)
        var urlParams = new URLSearchParams(window.location.search);
        var highlight = urlParams.get('highlight');
        if (highlight) {
            return [highlight];
        }

        // Si no hay highlight, intenta obtener del referrer
        var referrer = document.referrer;
        if (!referrer) return [];

        var referrerUrl = new URL(referrer);
        var search = referrerUrl.searchParams.get('search') || 
                    referrerUrl.searchParams.get('query');
        
        if (!search) return [];
        
        // Limpia espacios extras y trata toda la búsqueda como un solo término
        search = search.trim();
        return [search];
    }

    /**
     * Crea y agrega los controles de navegación en la página
     */
    function createNavigationControls() {
        var nav = document.createElement('div');
        nav.className = 'search-navigation';
        nav.innerHTML = [
            '<div class="search-nav-controls">',
                '<button id="prevMatch">↑ Anterior</button>',
                '<span id="matchCounter">0 de 0</span>',
                '<button id="nextMatch">↓ Siguiente</button>',
            '</div>'
        ].join('');
        document.body.appendChild(nav);

        // Estilos CSS para los controles de navegación
        var style = document.createElement('style');
        style.textContent = [
            '.search-navigation {',
                'position: fixed;',
                'bottom: 20px;',
                'right: 20px;',
                'z-index: 1000;',
            '}',
            '.search-nav-controls {',
                'background: white;',
                'padding: 10px;',
                'border-radius: 8px;',
                'box-shadow: 0 2px 10px rgba(0,0,0,0.1);',
                'display: flex;',
                'gap: 10px;',
                'align-items: center;',
            '}',
            '.search-nav-controls button {',
                'padding: 5px 10px;',
                'border: 1px solid #ccc;',
                'border-radius: 4px;',
                'background: #f0f0f0;',
                'cursor: pointer;',
            '}',
            '.search-nav-controls button:hover {',
                'background: #e0e0e0;',
            '}',
            '.searchmatch {',
                'background-color: #ffeb3b;',
                'padding: 2px;',
                'border-radius: 2px;',
            '}',
            '.searchmatch.current {',
                'background-color: #ffa000;',
                'color: white;',
            '}'
        ].join('\n');
        document.head.appendChild(style);

        // Agregar listeners para los botones de navegación
        document.getElementById('prevMatch').addEventListener('click', navigateToPrevious);
        document.getElementById('nextMatch').addEventListener('click', navigateToNext);
    }

    /**
     * Actualiza el contador de coincidencias
     */
    function updateMatchCounter() {
        var counter = document.getElementById('matchCounter');
        if (counter && highlightElements.length > 0) {
            counter.textContent = (currentHighlightIndex + 1) + ' de ' + highlightElements.length;
        }
    }

    /**
     * Navega a una coincidencia específica
     * @param {number} index - Índice de la coincidencia
     */
    function navigateToMatch(index) {
        if (highlightElements.length === 0) return;

        // Quita el resaltado especial del elemento actual
        if (currentHighlightIndex >= 0) {
            highlightElements[currentHighlightIndex].classList.remove('current');
        }

        // Maneja el ciclo de navegación
        currentHighlightIndex = index;
        if (currentHighlightIndex >= highlightElements.length) {
            currentHighlightIndex = 0;
        }
        if (currentHighlightIndex < 0) {
            currentHighlightIndex = highlightElements.length - 1;
        }

        // Resalta y hace scroll al elemento actual
        var element = highlightElements[currentHighlightIndex];
        element.classList.add('current');
        element.scrollIntoView({
            behavior: 'smooth',
            block: 'center'
        });

        updateMatchCounter();
    }

    /**
     * Navega a la siguiente coincidencia
     */
    function navigateToNext() {
        navigateToMatch(currentHighlightIndex + 1);
    }

    /**
     * Navega a la coincidencia anterior
     */
    function navigateToPrevious() {
        navigateToMatch(currentHighlightIndex - 1);
    }

    /**
     * Resalta los términos de búsqueda en el contenido del artículo
     */
    function highlightSearchTerms() {
        // Solo procede si venimos de una búsqueda
        if (!isComingFromSearch()) return;

        var terms = getSearchTerms();
        if (!terms.length) return;

        var content = document.getElementById('mw-content-text');
        if (!content) return;

        terms.forEach(function(term) {
            // Ignora términos muy cortos
            if (term.length < 3) return;
            
            // Escapa caracteres especiales en el término de búsqueda
            var escapedTerm = term.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
            
            // Usa una expresión regular que busca la frase exacta
            //var regex = new RegExp('\\b(' + escapedTerm + ')\\b', 'gi');


           //usa una expresion regular que buscar un matcheo parcial, es decir el termino buscado
           //puede estar contenido en una palabra con diferente significado
           var regex = new RegExp('\\b(' + escapedTerm + '\\w*)', 'gi');


            // Crea un TreeWalker para recorrer todos los nodos de texto
            var walker = document.createTreeWalker(
                content,
                NodeFilter.SHOW_TEXT,
                null,
                false
            );

            // Recolecta todos los nodos de texto
            var node;
            var nodes = [];
            while (node = walker.nextNode()) {
                nodes.push(node);
            }

            // Procesa cada nodo de texto
            nodes.forEach(function(node) {
                if (node.parentNode.nodeName !== 'SCRIPT' && 
                    node.parentNode.nodeName !== 'STYLE' &&
                    !node.parentNode.classList.contains('searchmatch')) {
                    
                    var text = node.textContent;
                    if (regex.test(text)) {
                        var span = document.createElement('span');
                        span.innerHTML = text.replace(regex, '<span class="searchmatch">$1</span>');
                        node.parentNode.replaceChild(span, node);
                    }
                }
            });
        });

        // Recolecta todas las coincidencias resaltadas
        highlightElements = Array.from(document.getElementsByClassName('searchmatch'));
        
        // Si hay coincidencias, crea los controles y navega al primer resultado
        if (highlightElements.length > 0) {
            createNavigationControls();
            navigateToMatch(0);
        }
    }

    // Agrega soporte para navegación con teclas
    document.addEventListener('keydown', function(e) {
        if (e.key === 'F3' || (e.ctrlKey && e.key === 'g')) {
            e.preventDefault();
            navigateToNext();
        } else if (e.shiftKey && e.key === 'F3' || (e.ctrlKey && e.shiftKey && e.key === 'g')) {
            e.preventDefault();
            navigateToPrevious();
        }
    });

    // Inicia el script cuando la página esté lista
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', highlightSearchTerms);
    } else {
        highlightSearchTerms();
    }
})();