Diferencia entre revisiones de «MediaWiki:Common.js»

De Oroza RO Wiki
Ir a la navegación Ir a la búsqueda
Sin resumen de edición
Sin resumen de edición
Línea 298: Línea 298:
       var node = $content && $content[0] ? $content[0] : document;
       var node = $content && $content[0] ? $content[0] : document;
       initOrozaPetSearchV9(node);
       initOrozaPetSearchV9(node);
    });
  }
})();
/* =========================================================
  OROZA RO UNIVERSAL WIKI SEARCH
  ---------------------------------------------------------
  - Quick results from sidebar links and categories
  - Full title and page-content search through mw.Api()
  - Keyboard navigation, debounce, cache, and native fallback
========================================================= */
(function () {
  'use strict';
  var SELECTOR = '[data-oroza-global-search]';
  var MIN_QUERY_LENGTH = 2;
  var API_LIMIT = 8;
  var MENU_LIMIT = 5;
  var DEBOUNCE_MS = 280;
  function normalizeText(value) {
    var text = String(value || '')
      .toLowerCase()
      .replace(/_/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();
    try {
      text = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
    } catch (e) {}
    return text;
  }
  function stripHtml(value) {
    var node = document.createElement('div');
    node.innerHTML = String(value || '');
    return (node.textContent || node.innerText || '')
      .replace(/\s+/g, ' ')
      .trim();
  }
  function decodePageName(href) {
    try {
      var url = new URL(href, window.location.href);
      var title = url.searchParams.get('title');
      if (!title) {
        var marker = '/wiki/index.php/';
        var markerIndex = url.pathname.indexOf(marker);
        if (markerIndex !== -1) {
          title = url.pathname.slice(markerIndex + marker.length);
        }
      }
      if (!title) return '';
      return decodeURIComponent(title).replace(/_/g, ' ').trim();
    } catch (e) {
      return '';
    }
  }
  function getLocale() {
    var isEnglish = window.location.hostname === 'en.orozaro.com';
    return isEnglish ? {
      placeholder: 'Search guides, commands, systems, events, equipment...',
      ariaLabel: 'Search the entire Oroza RO Wiki',
      searchButton: 'Search',
      loading: 'Searching the entire Wiki...',
      menuGroup: 'Quick links by category',
      wikiGroup: 'Results across the Wiki',
      noResultsTitle: 'No results found',
      noResultsText: 'Try a shorter term, another spelling, or a related word.',
      categoryFallback: 'Wiki content',
      sectionPrefix: 'Section',
      resultSingular: 'result',
      resultPlural: 'results',
      viewAll: 'View all results',
      suggestion: 'Did you mean',
      apiErrorTitle: 'Live search is temporarily unavailable',
      apiErrorText: 'You can still press Enter or use the Search button to open MediaWiki search.'
    } : {
      placeholder: 'Buscar guías, comandos, sistemas, eventos, equipo...',
      ariaLabel: 'Buscar en toda la Wiki de Oroza RO',
      searchButton: 'Buscar',
      loading: 'Buscando en toda la Wiki...',
      menuGroup: 'Accesos rápidos por categoría',
      wikiGroup: 'Resultados en toda la Wiki',
      noResultsTitle: 'No encontramos resultados',
      noResultsText: 'Prueba con una palabra más corta, otra escritura o un término relacionado.',
      categoryFallback: 'Contenido de la Wiki',
      sectionPrefix: 'Sección',
      resultSingular: 'resultado',
      resultPlural: 'resultados',
      viewAll: 'Ver todos los resultados',
      suggestion: 'Quizá quisiste decir',
      apiErrorTitle: 'La búsqueda instantánea no está disponible temporalmente',
      apiErrorText: 'Aun puedes presionar Enter o usar el botón Buscar para abrir la búsqueda nativa de MediaWiki.'
    };
  }
  function collectMenuEntries() {
    var entries = [];
    var titles = document.querySelectorAll('.oroza-sidebar .menu-title');
    Array.prototype.forEach.call(titles, function (titleNode) {
      var category = (titleNode.textContent || '').replace(/\s+/g, ' ').trim();
      var list = titleNode.nextElementSibling;
      if (!list || String(list.tagName).toLowerCase() !== 'ul') return;
      var links = list.querySelectorAll('a[href]');
      Array.prototype.forEach.call(links, function (link) {
        var title = decodePageName(link.getAttribute('href'));
        var labelNode = link.querySelector('.menu-text');
        var label = labelNode ? labelNode.textContent : link.textContent;
        label = String(label || '').replace(/›/g, '').replace(/\s+/g, ' ').trim();
        title = title || label;
        if (!title || !link.href) return;
        entries.push({
          title: title,
          label: label || title,
          category: category,
          href: link.href,
          normalized: normalizeText(title + ' ' + label + ' ' + category)
        });
      });
    });
    return entries;
  }
  function findMenuMatches(entries, query) {
    var normalizedQuery = normalizeText(query);
    if (!normalizedQuery) return [];
    return entries
      .map(function (entry) {
        var normalizedTitle = normalizeText(entry.title + ' ' + entry.label);
        var score = 0;
        if (normalizedTitle === normalizedQuery) score = 100;
        else if (normalizedTitle.indexOf(normalizedQuery) === 0) score = 80;
        else if (entry.normalized.indexOf(normalizedQuery) !== -1) score = 50;
        return { entry: entry, score: score };
      })
      .filter(function (item) { return item.score > 0; })
      .sort(function (a, b) {
        if (b.score !== a.score) return b.score - a.score;
        return a.entry.label.localeCompare(b.entry.label);
      })
      .slice(0, MENU_LIMIT)
      .map(function (item) { return item.entry; });
  }
  function buildCategoryMap(entries) {
    var map = Object.create(null);
    entries.forEach(function (entry) {
      map[normalizeText(entry.title)] = entry.category;
      map[normalizeText(entry.label)] = entry.category;
    });
    return map;
  }
  function createElement(tag, className, text) {
    var element = document.createElement(tag);
    if (className) element.className = className;
    if (typeof text !== 'undefined') element.textContent = text;
    return element;
  }
  function initSearch(root) {
    var widget = root.querySelector ? root.querySelector(SELECTOR) : null;
    if (!widget || widget.dataset.orozaReady === '1') return;
    widget.dataset.orozaReady = '1';
    var form = widget.querySelector('.oroza-global-search-form');
    var input = widget.querySelector('.oroza-global-search-input');
    var results = widget.querySelector('.oroza-global-search-results');
    var clearButton = widget.querySelector('[data-oroza-search-clear]');
    var submitText = widget.querySelector('.oroza-global-search-submit span:last-child');
    if (!form || !input || !results) return;
    var labels = getLocale();
    var menuEntries = collectMenuEntries();
    var categoryMap = buildCategoryMap(menuEntries);
    var cache = Object.create(null);
    var debounceTimer = null;
    var requestSequence = 0;
    var activeIndex = -1;
    var api = (window.mw && mw.Api) ? new mw.Api() : null;
    input.placeholder = labels.placeholder;
    input.setAttribute('aria-label', labels.ariaLabel);
    if (submitText) submitText.textContent = labels.searchButton;
    function getNativeSearchUrl(query) {
      try {
        var url = new URL(form.action, window.location.href);
        url.searchParams.set('title', 'Special:Search');
        url.searchParams.set('search', query);
        url.searchParams.set('fulltext', '1');
        return url.toString();
      } catch (e) {
        return '/wiki/index.php?title=Special:Search&fulltext=1&search=' + encodeURIComponent(query);
      }
    }
    function setOpen(isOpen) {
      results.hidden = !isOpen;
      input.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
      if (!isOpen) activeIndex = -1;
    }
    function clearResults() {
      while (results.firstChild) results.removeChild(results.firstChild);
      setOpen(false);
    }
    function addGroupTitle(text) {
      results.appendChild(createElement('div', 'oroza-search-group-title', text));
    }
    function addResultItem(item) {
      var link = createElement('a', 'oroza-search-result-link');
      link.href = item.href;
      link.setAttribute('role', 'option');
      link.setAttribute('aria-selected', 'false');
      var main = createElement('div', 'oroza-search-result-main');
      main.appendChild(createElement('span', 'oroza-search-result-title', item.label || item.title));
      if (item.snippet) {
        main.appendChild(createElement('p', 'oroza-search-result-snippet', item.snippet));
      }
      link.appendChild(main);
      link.appendChild(createElement('span', 'oroza-search-result-category', item.category || labels.categoryFallback));
      results.appendChild(link);
    }
    function addSuggestion(suggestion) {
      if (!suggestion) return;
      var box = createElement('div', 'oroza-search-suggestion');
      box.appendChild(document.createTextNode(labels.suggestion + ': '));
      var button = createElement('button', '', suggestion);
      button.type = 'button';
      button.addEventListener('click', function () {
        input.value = suggestion;
        input.dispatchEvent(new Event('input', { bubbles: true }));
        input.focus();
      });
      box.appendChild(button);
      results.appendChild(box);
    }
    function addFooter(query, totalHits) {
      var footer = createElement('div', 'oroza-search-footer');
      var countText = '';
      if (typeof totalHits === 'number') {
        countText = totalHits + ' ' + (totalHits === 1 ? labels.resultSingular : labels.resultPlural);
      }
      footer.appendChild(createElement('span', '', countText));
      var link = createElement('a', '', labels.viewAll + ' →');
      link.href = getNativeSearchUrl(query);
      footer.appendChild(link);
      results.appendChild(footer);
    }
    function renderLoading(menuMatches) {
      while (results.firstChild) results.removeChild(results.firstChild);
      if (menuMatches.length) {
        addGroupTitle(labels.menuGroup);
        menuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
      }
      var status = createElement('div', 'oroza-search-status');
      status.appendChild(createElement('span', 'oroza-search-spinner'));
      status.appendChild(createElement('span', '', labels.loading));
      results.appendChild(status);
      setOpen(true);
    }
    function renderEmpty(query, menuMatches) {
      while (results.firstChild) results.removeChild(results.firstChild);
      if (menuMatches.length) {
        addGroupTitle(labels.menuGroup);
        menuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
        addFooter(query, menuMatches.length);
      } else {
        var empty = createElement('div', 'oroza-search-empty');
        empty.appendChild(createElement('span', 'oroza-search-empty-icon', '🔍'));
        empty.appendChild(createElement('p', 'oroza-search-empty-title', labels.noResultsTitle));
        empty.appendChild(createElement('p', 'oroza-search-empty-text', labels.noResultsText));
        results.appendChild(empty);
        addFooter(query, 0);
      }
      setOpen(true);
    }
    function renderApiError(query, menuMatches) {
      while (results.firstChild) results.removeChild(results.firstChild);
      if (menuMatches.length) {
        addGroupTitle(labels.menuGroup);
        menuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
      }
      var empty = createElement('div', 'oroza-search-empty');
      empty.appendChild(createElement('span', 'oroza-search-empty-icon', '⚠️'));
      empty.appendChild(createElement('p', 'oroza-search-empty-title', labels.apiErrorTitle));
      empty.appendChild(createElement('p', 'oroza-search-empty-text', labels.apiErrorText));
      results.appendChild(empty);
      addFooter(query);
      setOpen(true);
    }
    function renderResults(query, menuMatches, payload) {
      while (results.firstChild) results.removeChild(results.firstChild);
      var apiResults = payload && payload.items ? payload.items : [];
      var seen = Object.create(null);
      var visibleMenuMatches = [];
      var visibleApiResults = [];
      menuMatches.forEach(function (item) {
        var key = normalizeText(item.title);
        if (!key || seen[key]) return;
        seen[key] = true;
        visibleMenuMatches.push(item);
      });
      apiResults.forEach(function (item) {
        var key = normalizeText(item.title);
        if (!key || seen[key]) return;
        seen[key] = true;
        visibleApiResults.push(item);
      });
      if (!visibleMenuMatches.length && !visibleApiResults.length) {
        renderEmpty(query, []);
        return;
      }
      if (visibleMenuMatches.length) {
        addGroupTitle(labels.menuGroup);
        visibleMenuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
      }
      if (visibleApiResults.length) {
        addGroupTitle(labels.wikiGroup);
        visibleApiResults.forEach(addResultItem);
      }
      addSuggestion(payload.suggestion);
      addFooter(query, payload.totalHits);
      setOpen(true);
    }
    function transformApiData(data) {
      var query = data && data.query ? data.query : {};
      var searchInfo = query.searchinfo || {};
      var items = Array.isArray(query.search) ? query.search : [];
      return {
        totalHits: typeof searchInfo.totalhits === 'number' ? searchInfo.totalhits : items.length,
        suggestion: searchInfo.suggestion || '',
        items: items.map(function (item) {
          var category = categoryMap[normalizeText(item.title)] || labels.categoryFallback;
          var section = stripHtml(item.sectiontitle || '');
          var snippet = stripHtml(item.snippet || '');
          if (section) {
            snippet = labels.sectionPrefix + ': ' + section + (snippet ? ' — ' + snippet : '');
          }
          return {
            title: item.title,
            label: item.title,
            href: (window.mw && mw.util) ? mw.util.getUrl(item.title) : '/wiki/index.php?title=' + encodeURIComponent(item.title.replace(/ /g, '_')),
            category: category,
            snippet: snippet
          };
        })
      };
    }
    function runSearch(rawQuery) {
      var query = String(rawQuery || '').trim();
      var menuMatches = findMenuMatches(menuEntries, query);
      var normalizedQuery = normalizeText(query);
      if (query.length < MIN_QUERY_LENGTH) {
        clearResults();
        return;
      }
      renderLoading(menuMatches);
      if (cache[normalizedQuery]) {
        renderResults(query, menuMatches, cache[normalizedQuery]);
        return;
      }
      if (!api) {
        renderApiError(query, menuMatches);
        return;
      }
      var thisRequest = ++requestSequence;
      api.get({
        action: 'query',
        list: 'search',
        srsearch: query,
        srnamespace: 0,
        srlimit: API_LIMIT,
        srinfo: 'totalhits|suggestion|rewrittenquery',
        srprop: 'snippet|titlesnippet|sectiontitle|redirecttitle',
        srenablerewrites: 1,
        formatversion: 2,
        utf8: 1
      }).done(function (data) {
        if (thisRequest !== requestSequence) return;
        var payload = transformApiData(data);
        cache[normalizedQuery] = payload;
        renderResults(query, menuMatches, payload);
      }).fail(function () {
        if (thisRequest !== requestSequence) return;
        renderApiError(query, menuMatches);
      });
    }
    function getResultLinks() {
      return Array.prototype.slice.call(results.querySelectorAll('.oroza-search-result-link'));
    }
    function setActiveResult(index) {
      var links = getResultLinks();
      if (!links.length) return;
      links.forEach(function (link) {
        link.classList.remove('is-active');
        link.setAttribute('aria-selected', 'false');
      });
      if (index < 0) index = links.length - 1;
      if (index >= links.length) index = 0;
      activeIndex = index;
      links[activeIndex].classList.add('is-active');
      links[activeIndex].setAttribute('aria-selected', 'true');
      links[activeIndex].scrollIntoView({ block: 'nearest' });
    }
    input.addEventListener('input', function () {
      var query = input.value;
      clearButton.hidden = !query;
      activeIndex = -1;
      window.clearTimeout(debounceTimer);
      if (String(query || '').trim().length < MIN_QUERY_LENGTH) {
        requestSequence++;
        clearResults();
        return;
      }
      debounceTimer = window.setTimeout(function () {
        runSearch(query);
      }, DEBOUNCE_MS);
    });
    input.addEventListener('focus', function () {
      if (input.value.trim().length >= MIN_QUERY_LENGTH && results.childNodes.length) {
        setOpen(true);
      }
    });
    input.addEventListener('keydown', function (event) {
      if (event.key === 'ArrowDown') {
        event.preventDefault();
        if (results.hidden) runSearch(input.value);
        setActiveResult(activeIndex + 1);
      } else if (event.key === 'ArrowUp') {
        event.preventDefault();
        setActiveResult(activeIndex - 1);
      } else if (event.key === 'Enter' && activeIndex >= 0) {
        var links = getResultLinks();
        if (links[activeIndex]) {
          event.preventDefault();
          window.location.href = links[activeIndex].href;
        }
      } else if (event.key === 'Escape') {
        setOpen(false);
        input.blur();
      }
    });
    clearButton.addEventListener('click', function () {
      input.value = '';
      clearButton.hidden = true;
      requestSequence++;
      window.clearTimeout(debounceTimer);
      clearResults();
      input.focus();
    });
    document.addEventListener('click', function (event) {
      if (!widget.contains(event.target)) setOpen(false);
    });
    form.addEventListener('submit', function (event) {
      if (!input.value.trim()) {
        event.preventDefault();
        input.focus();
      }
    });
  }
  function boot(root) {
    root = root || document;
    var widgets = root.querySelectorAll ? root.querySelectorAll(SELECTOR) : [];
    Array.prototype.forEach.call(widgets, function (widget) {
      initSearch(widget.parentNode || document);
    });
  }
  function bootWithMediaWikiModules(root) {
    if (window.mw && mw.loader && mw.loader.using) {
      mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () {
        boot(root);
      }, function () {
        boot(root);
      });
    } else {
      boot(root);
    }
  }
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', function () {
      bootWithMediaWikiModules(document);
    });
  } else {
    bootWithMediaWikiModules(document);
  }
  if (window.mw && mw.hook) {
    mw.hook('wikipage.content').add(function ($content) {
      var node = $content && $content[0] ? $content[0] : document;
      bootWithMediaWikiModules(node);
      bootWithMediaWikiModules(document);
     });
     });
   }
   }
})();
})();

Revisión del 10:53 10 jul 2026

(function () {
  function safeNormalize(text) {
    text = String(text || '').toLowerCase().trim();

    try {
      return text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
    } catch (e) {
      return text;
    }
  }

  function hasMark(cellText) {
    cellText = String(cellText || '').replace(/\s+/g, '').trim();

    return (
      cellText !== '' &&
      (
        cellText.indexOf('✔') !== -1 ||
        cellText.indexOf('✅') !== -1 ||
        cellText.indexOf('☑') !== -1
      )
    );
  }

  function getActiveCategoryCol(tableId) {
    var activeBtn = document.querySelector('.oroza-cat-btn.active[data-table="' + tableId + '"]');
    if (!activeBtn) {
      var firstBtn = document.querySelector('.oroza-cat-btn[data-table="' + tableId + '"]');
      if (!firstBtn) return null;
      firstBtn.classList.add('active');
      return firstBtn.getAttribute('data-col'); 
    }
    return activeBtn.getAttribute('data-col'); // Puede retornar "all" en string
  }

  function getSearchTextForRow(row, tableId) {
    var cells = row.getElementsByTagName('td');
    var text = row.getAttribute('data-search') || '';
    // Como añadimos "Prob.", ahora las columnas de información base son 3 en Slot 1 y 4 en Slot 2
    var visibleInfoCols = tableId === 'tableSlot1' ? 3 : 4;

    for (var i = 0; i < visibleInfoCols; i++) {
      if (cells[i]) {
        text += ' ' + (cells[i].textContent || cells[i].innerText || '');
      }
    }

    return safeNormalize(text);
  }

  function updateCategoryView(tableId, inputId) {
    var table = document.getElementById(tableId);
    var input = document.getElementById(inputId);

    if (!table || !table.tBodies.length || !table.tHead || !table.tHead.rows.length) return;

    var activeColRaw = getActiveCategoryCol(tableId);
    if (activeColRaw === null) return;

    var showAll = (activeColRaw === 'all');
    var activeCol = showAll ? -1 : parseInt(activeColRaw, 10);

    var searchText = input ? safeNormalize(input.value) : '';
    var rows = table.tBodies[0].getElementsByTagName('tr');
    var headerRow = table.tHead.rows[0];
    
    // Columnas que SIEMPRE se ven (Atributo, Prob, Rango, etc.)
    var staticCols = tableId === 'tableSlot1' ? 3 : 4;

    // Actualizar Header
    for (var h = 0; h < headerRow.cells.length; h++) {
      headerRow.cells[h].style.display = (showAll || h < staticCols || h === activeCol) ? '' : 'none';
    }

    // Actualizar Body
    for (var i = 0; i < rows.length; i++) {
      var row = rows[i];
      var cells = row.getElementsByTagName('td');
      
      // Si estamos en "Todos", empezamos asumiendo que sí tiene categoría válida
      var matchCategory = showAll;
      var matchText = true;

      // Si se escogió una categoría específica, verificamos si tiene "✔"
      if (!showAll && cells[activeCol]) {
        matchCategory = hasMark(cells[activeCol].textContent || cells[activeCol].innerText);
      }

      // Verificación de búsqueda por texto
      if (searchText !== '') {
        var searchPool = getSearchTextForRow(row, tableId);
        if (searchPool.indexOf(searchText) === -1) {
          matchText = false;
        }
      }

      row.style.display = (matchCategory && matchText) ? '' : 'none';

      for (var c = 0; c < cells.length; c++) {
        cells[c].style.display = (showAll || c < staticCols || c === activeCol) ? '' : 'none';
      }
    }
  }

  function bindCategoryButtons(tableId, inputId) {
    var buttons = document.querySelectorAll('.oroza-cat-btn[data-table="' + tableId + '"]');

    for (var i = 0; i < buttons.length; i++) {
      if (buttons[i].dataset.orozaBound === '1') continue;

      buttons[i].dataset.orozaBound = '1';
      buttons[i].addEventListener('click', function () {
        var sameTableButtons = document.querySelectorAll('.oroza-cat-btn[data-table="' + tableId + '"]');
        for (var j = 0; j < sameTableButtons.length; j++) {
          sameTableButtons[j].classList.remove('active');
        }

        this.classList.add('active');
        updateCategoryView(tableId, inputId);
      });
    }
  }

  function bindSearchInput(tableId, inputId) {
    var input = document.getElementById(inputId);
    if (!input || input.dataset.orozaBound === '1') return;

    input.dataset.orozaBound = '1';
    input.addEventListener('input', function () {
      updateCategoryView(tableId, inputId);
    });
  }

  function initOrozaDropFilters() {
    if (document.getElementById('tableSlot1')) {
      bindCategoryButtons('tableSlot1', 'textSlot1');
      bindSearchInput('tableSlot1', 'textSlot1');
      updateCategoryView('tableSlot1', 'textSlot1');
    }

    if (document.getElementById('tableSlot2')) {
      bindCategoryButtons('tableSlot2', 'textSlot2');
      bindSearchInput('tableSlot2', 'textSlot2');
      updateCategoryView('tableSlot2', 'textSlot2');
    }
  }

  if (window.mw && mw.hook) {
    mw.hook('wikipage.content').add(function () {
      initOrozaDropFilters();
    });
  } else if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initOrozaDropFilters);
  } else {
    initOrozaDropFilters();
  }
})();

/* =========================================================
   RESALTAR MENÚ LATERAL ACTIVO (VERSIÓN NATIVA MEDIAWIKI)
========================================================= */
function highlightActiveSidebarLink() {
  // 1. Obtenemos el nombre exacto de la página actual desde el motor de MediaWiki
  var currentPage = mw.config.get('wgPageName');
  if (!currentPage) return;

  // Limpiamos el nombre de la página actual (todo a minúsculas y espacios a guiones bajos)
  currentPage = currentPage.replace(/ /g, '_').toLowerCase();

  var sidebarLinks = document.querySelectorAll('.oroza-sidebar li a');

  for (var i = 0; i < sidebarLinks.length; i++) {
    var linkHref = sidebarLinks[i].getAttribute('href');
    if (!linkHref) continue;

    // 2. Extraemos el nombre de la página del enlace del menú
    var linkPage = "";
    if (linkHref.indexOf('title=') !== -1) {
      linkPage = linkHref.split('title=')[1].split('&')[0];
    } else {
      linkPage = linkHref.split('/').pop(); // Toma lo último después del slash (ej. "Como-instalarlo")
    }

    if (!linkPage) continue;

    // 3. Decodificamos caracteres especiales (como acentos o espacios %20)
    try {
      linkPage = decodeURIComponent(linkPage);
    } catch(e) {}

    // Limpiamos el enlace de la misma forma para que la comparación sea exacta
    linkPage = linkPage.replace(/ /g, '_').toLowerCase();

    // 4. Comparamos
    if (currentPage === linkPage) {
      sidebarLinks[i].classList.add('active');
    } else {
      sidebarLinks[i].classList.remove('active'); // Limpia los demás por si acaso
    }
  }
}

// Integración con MediaWiki
if (window.mw && mw.hook) {
  mw.hook('wikipage.content').add(highlightActiveSidebarLink);
} else if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', highlightActiveSidebarLink);
} else {
  highlightActiveSidebarLink();
}



/* =========================================================
   OROZA PET ATTACK REBALANCE V13.3 - SIMPLE SEARCH
   Paste into MediaWiki:Common.js
========================================================= */
(function () {
  function normalize(text) {
    return (text || '')
      .toString()
      .toLowerCase()
      .normalize('NFD')
      .replace(/[\u0300-\u036f]/g, '')
      .replace(/_/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();
  }

  function initOrozaPetSearchV9(root) {
    root = root || document;

    var input = root.querySelector ? root.querySelector('#orozaPetSearchV6') : document.getElementById('orozaPetSearchV6');
    if (!input || input.dataset.ready === 'v9') return;
    input.dataset.ready = 'v9';

    var container = input.closest('.oroza-pet-v6') || document;
    var cards = Array.prototype.slice.call(container.querySelectorAll('.oroza-pet-v6-card'));
    var sections = Array.prototype.slice.call(container.querySelectorAll('[data-pet-section]'));
    var result = container.querySelector('#orozaPetSearchV6Result');
    var clearBtn = container.querySelector('[data-pet-clear]');

    function applySearch() {
      var q = normalize(input.value);
      var shown = 0;

      cards.forEach(function (card) {
        var haystack = normalize((card.getAttribute('data-pet-search') || '') + ' ' + card.textContent);
        var match = !q || haystack.indexOf(q) !== -1;

        card.classList.toggle('is-hidden', !match);
        if (match) shown++;
      });

      sections.forEach(function (section) {
        var visibleCards = section.querySelectorAll('.oroza-pet-v6-card:not(.is-hidden)').length;
        var shouldHide = visibleCards === 0 && q;
        section.classList.toggle('is-hidden', shouldHide);
        if (!shouldHide && q) section.open = true;
      });

      if (result) {
        if (shown === 0) {
          result.textContent = 'No pets were found with that text.';
        } else if (q) {
          result.textContent = 'Visible results: ' + shown + '. Open the card to review each Pokémon skill and level.';
        } else {
          result.textContent = 'Showing all pets.';
        }
      }
    }

    input.addEventListener('input', applySearch);

    if (clearBtn) {
      clearBtn.addEventListener('click', function () {
        input.value = '';
        applySearch();
        input.focus();
      });
    }

    applySearch();
  }

  function boot() {
    initOrozaPetSearchV9(document);
  }

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

  if (window.mw && window.mw.hook) {
    window.mw.hook('wikipage.content').add(function ($content) {
      var node = $content && $content[0] ? $content[0] : document;
      initOrozaPetSearchV9(node);
    });
  }
})();


/* =========================================================
   OROZA RO UNIVERSAL WIKI SEARCH
   ---------------------------------------------------------
   - Quick results from sidebar links and categories
   - Full title and page-content search through mw.Api()
   - Keyboard navigation, debounce, cache, and native fallback
========================================================= */
(function () {
  'use strict';

  var SELECTOR = '[data-oroza-global-search]';
  var MIN_QUERY_LENGTH = 2;
  var API_LIMIT = 8;
  var MENU_LIMIT = 5;
  var DEBOUNCE_MS = 280;

  function normalizeText(value) {
    var text = String(value || '')
      .toLowerCase()
      .replace(/_/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();

    try {
      text = text.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
    } catch (e) {}

    return text;
  }

  function stripHtml(value) {
    var node = document.createElement('div');
    node.innerHTML = String(value || '');
    return (node.textContent || node.innerText || '')
      .replace(/\s+/g, ' ')
      .trim();
  }

  function decodePageName(href) {
    try {
      var url = new URL(href, window.location.href);
      var title = url.searchParams.get('title');

      if (!title) {
        var marker = '/wiki/index.php/';
        var markerIndex = url.pathname.indexOf(marker);
        if (markerIndex !== -1) {
          title = url.pathname.slice(markerIndex + marker.length);
        }
      }

      if (!title) return '';
      return decodeURIComponent(title).replace(/_/g, ' ').trim();
    } catch (e) {
      return '';
    }
  }

  function getLocale() {
    var isEnglish = window.location.hostname === 'en.orozaro.com';

    return isEnglish ? {
      placeholder: 'Search guides, commands, systems, events, equipment...',
      ariaLabel: 'Search the entire Oroza RO Wiki',
      searchButton: 'Search',
      loading: 'Searching the entire Wiki...',
      menuGroup: 'Quick links by category',
      wikiGroup: 'Results across the Wiki',
      noResultsTitle: 'No results found',
      noResultsText: 'Try a shorter term, another spelling, or a related word.',
      categoryFallback: 'Wiki content',
      sectionPrefix: 'Section',
      resultSingular: 'result',
      resultPlural: 'results',
      viewAll: 'View all results',
      suggestion: 'Did you mean',
      apiErrorTitle: 'Live search is temporarily unavailable',
      apiErrorText: 'You can still press Enter or use the Search button to open MediaWiki search.'
    } : {
      placeholder: 'Buscar guías, comandos, sistemas, eventos, equipo...',
      ariaLabel: 'Buscar en toda la Wiki de Oroza RO',
      searchButton: 'Buscar',
      loading: 'Buscando en toda la Wiki...',
      menuGroup: 'Accesos rápidos por categoría',
      wikiGroup: 'Resultados en toda la Wiki',
      noResultsTitle: 'No encontramos resultados',
      noResultsText: 'Prueba con una palabra más corta, otra escritura o un término relacionado.',
      categoryFallback: 'Contenido de la Wiki',
      sectionPrefix: 'Sección',
      resultSingular: 'resultado',
      resultPlural: 'resultados',
      viewAll: 'Ver todos los resultados',
      suggestion: 'Quizá quisiste decir',
      apiErrorTitle: 'La búsqueda instantánea no está disponible temporalmente',
      apiErrorText: 'Aun puedes presionar Enter o usar el botón Buscar para abrir la búsqueda nativa de MediaWiki.'
    };
  }

  function collectMenuEntries() {
    var entries = [];
    var titles = document.querySelectorAll('.oroza-sidebar .menu-title');

    Array.prototype.forEach.call(titles, function (titleNode) {
      var category = (titleNode.textContent || '').replace(/\s+/g, ' ').trim();
      var list = titleNode.nextElementSibling;

      if (!list || String(list.tagName).toLowerCase() !== 'ul') return;

      var links = list.querySelectorAll('a[href]');
      Array.prototype.forEach.call(links, function (link) {
        var title = decodePageName(link.getAttribute('href'));
        var labelNode = link.querySelector('.menu-text');
        var label = labelNode ? labelNode.textContent : link.textContent;

        label = String(label || '').replace(/›/g, '').replace(/\s+/g, ' ').trim();
        title = title || label;

        if (!title || !link.href) return;

        entries.push({
          title: title,
          label: label || title,
          category: category,
          href: link.href,
          normalized: normalizeText(title + ' ' + label + ' ' + category)
        });
      });
    });

    return entries;
  }

  function findMenuMatches(entries, query) {
    var normalizedQuery = normalizeText(query);
    if (!normalizedQuery) return [];

    return entries
      .map(function (entry) {
        var normalizedTitle = normalizeText(entry.title + ' ' + entry.label);
        var score = 0;

        if (normalizedTitle === normalizedQuery) score = 100;
        else if (normalizedTitle.indexOf(normalizedQuery) === 0) score = 80;
        else if (entry.normalized.indexOf(normalizedQuery) !== -1) score = 50;

        return { entry: entry, score: score };
      })
      .filter(function (item) { return item.score > 0; })
      .sort(function (a, b) {
        if (b.score !== a.score) return b.score - a.score;
        return a.entry.label.localeCompare(b.entry.label);
      })
      .slice(0, MENU_LIMIT)
      .map(function (item) { return item.entry; });
  }

  function buildCategoryMap(entries) {
    var map = Object.create(null);

    entries.forEach(function (entry) {
      map[normalizeText(entry.title)] = entry.category;
      map[normalizeText(entry.label)] = entry.category;
    });

    return map;
  }

  function createElement(tag, className, text) {
    var element = document.createElement(tag);
    if (className) element.className = className;
    if (typeof text !== 'undefined') element.textContent = text;
    return element;
  }

  function initSearch(root) {
    var widget = root.querySelector ? root.querySelector(SELECTOR) : null;
    if (!widget || widget.dataset.orozaReady === '1') return;

    widget.dataset.orozaReady = '1';

    var form = widget.querySelector('.oroza-global-search-form');
    var input = widget.querySelector('.oroza-global-search-input');
    var results = widget.querySelector('.oroza-global-search-results');
    var clearButton = widget.querySelector('[data-oroza-search-clear]');
    var submitText = widget.querySelector('.oroza-global-search-submit span:last-child');

    if (!form || !input || !results) return;

    var labels = getLocale();
    var menuEntries = collectMenuEntries();
    var categoryMap = buildCategoryMap(menuEntries);
    var cache = Object.create(null);
    var debounceTimer = null;
    var requestSequence = 0;
    var activeIndex = -1;
    var api = (window.mw && mw.Api) ? new mw.Api() : null;

    input.placeholder = labels.placeholder;
    input.setAttribute('aria-label', labels.ariaLabel);
    if (submitText) submitText.textContent = labels.searchButton;

    function getNativeSearchUrl(query) {
      try {
        var url = new URL(form.action, window.location.href);
        url.searchParams.set('title', 'Special:Search');
        url.searchParams.set('search', query);
        url.searchParams.set('fulltext', '1');
        return url.toString();
      } catch (e) {
        return '/wiki/index.php?title=Special:Search&fulltext=1&search=' + encodeURIComponent(query);
      }
    }

    function setOpen(isOpen) {
      results.hidden = !isOpen;
      input.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
      if (!isOpen) activeIndex = -1;
    }

    function clearResults() {
      while (results.firstChild) results.removeChild(results.firstChild);
      setOpen(false);
    }

    function addGroupTitle(text) {
      results.appendChild(createElement('div', 'oroza-search-group-title', text));
    }

    function addResultItem(item) {
      var link = createElement('a', 'oroza-search-result-link');
      link.href = item.href;
      link.setAttribute('role', 'option');
      link.setAttribute('aria-selected', 'false');

      var main = createElement('div', 'oroza-search-result-main');
      main.appendChild(createElement('span', 'oroza-search-result-title', item.label || item.title));

      if (item.snippet) {
        main.appendChild(createElement('p', 'oroza-search-result-snippet', item.snippet));
      }

      link.appendChild(main);
      link.appendChild(createElement('span', 'oroza-search-result-category', item.category || labels.categoryFallback));
      results.appendChild(link);
    }

    function addSuggestion(suggestion) {
      if (!suggestion) return;

      var box = createElement('div', 'oroza-search-suggestion');
      box.appendChild(document.createTextNode(labels.suggestion + ': '));

      var button = createElement('button', '', suggestion);
      button.type = 'button';
      button.addEventListener('click', function () {
        input.value = suggestion;
        input.dispatchEvent(new Event('input', { bubbles: true }));
        input.focus();
      });

      box.appendChild(button);
      results.appendChild(box);
    }

    function addFooter(query, totalHits) {
      var footer = createElement('div', 'oroza-search-footer');
      var countText = '';

      if (typeof totalHits === 'number') {
        countText = totalHits + ' ' + (totalHits === 1 ? labels.resultSingular : labels.resultPlural);
      }

      footer.appendChild(createElement('span', '', countText));

      var link = createElement('a', '', labels.viewAll + ' →');
      link.href = getNativeSearchUrl(query);
      footer.appendChild(link);
      results.appendChild(footer);
    }

    function renderLoading(menuMatches) {
      while (results.firstChild) results.removeChild(results.firstChild);

      if (menuMatches.length) {
        addGroupTitle(labels.menuGroup);
        menuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
      }

      var status = createElement('div', 'oroza-search-status');
      status.appendChild(createElement('span', 'oroza-search-spinner'));
      status.appendChild(createElement('span', '', labels.loading));
      results.appendChild(status);
      setOpen(true);
    }

    function renderEmpty(query, menuMatches) {
      while (results.firstChild) results.removeChild(results.firstChild);

      if (menuMatches.length) {
        addGroupTitle(labels.menuGroup);
        menuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
        addFooter(query, menuMatches.length);
      } else {
        var empty = createElement('div', 'oroza-search-empty');
        empty.appendChild(createElement('span', 'oroza-search-empty-icon', '🔍'));
        empty.appendChild(createElement('p', 'oroza-search-empty-title', labels.noResultsTitle));
        empty.appendChild(createElement('p', 'oroza-search-empty-text', labels.noResultsText));
        results.appendChild(empty);
        addFooter(query, 0);
      }

      setOpen(true);
    }

    function renderApiError(query, menuMatches) {
      while (results.firstChild) results.removeChild(results.firstChild);

      if (menuMatches.length) {
        addGroupTitle(labels.menuGroup);
        menuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
      }

      var empty = createElement('div', 'oroza-search-empty');
      empty.appendChild(createElement('span', 'oroza-search-empty-icon', '⚠️'));
      empty.appendChild(createElement('p', 'oroza-search-empty-title', labels.apiErrorTitle));
      empty.appendChild(createElement('p', 'oroza-search-empty-text', labels.apiErrorText));
      results.appendChild(empty);
      addFooter(query);
      setOpen(true);
    }

    function renderResults(query, menuMatches, payload) {
      while (results.firstChild) results.removeChild(results.firstChild);

      var apiResults = payload && payload.items ? payload.items : [];
      var seen = Object.create(null);
      var visibleMenuMatches = [];
      var visibleApiResults = [];

      menuMatches.forEach(function (item) {
        var key = normalizeText(item.title);
        if (!key || seen[key]) return;
        seen[key] = true;
        visibleMenuMatches.push(item);
      });

      apiResults.forEach(function (item) {
        var key = normalizeText(item.title);
        if (!key || seen[key]) return;
        seen[key] = true;
        visibleApiResults.push(item);
      });

      if (!visibleMenuMatches.length && !visibleApiResults.length) {
        renderEmpty(query, []);
        return;
      }

      if (visibleMenuMatches.length) {
        addGroupTitle(labels.menuGroup);
        visibleMenuMatches.forEach(function (item) {
          addResultItem({
            title: item.title,
            label: item.label,
            href: item.href,
            category: item.category,
            snippet: ''
          });
        });
      }

      if (visibleApiResults.length) {
        addGroupTitle(labels.wikiGroup);
        visibleApiResults.forEach(addResultItem);
      }

      addSuggestion(payload.suggestion);
      addFooter(query, payload.totalHits);
      setOpen(true);
    }

    function transformApiData(data) {
      var query = data && data.query ? data.query : {};
      var searchInfo = query.searchinfo || {};
      var items = Array.isArray(query.search) ? query.search : [];

      return {
        totalHits: typeof searchInfo.totalhits === 'number' ? searchInfo.totalhits : items.length,
        suggestion: searchInfo.suggestion || '',
        items: items.map(function (item) {
          var category = categoryMap[normalizeText(item.title)] || labels.categoryFallback;
          var section = stripHtml(item.sectiontitle || '');
          var snippet = stripHtml(item.snippet || '');

          if (section) {
            snippet = labels.sectionPrefix + ': ' + section + (snippet ? ' — ' + snippet : '');
          }

          return {
            title: item.title,
            label: item.title,
            href: (window.mw && mw.util) ? mw.util.getUrl(item.title) : '/wiki/index.php?title=' + encodeURIComponent(item.title.replace(/ /g, '_')),
            category: category,
            snippet: snippet
          };
        })
      };
    }

    function runSearch(rawQuery) {
      var query = String(rawQuery || '').trim();
      var menuMatches = findMenuMatches(menuEntries, query);
      var normalizedQuery = normalizeText(query);

      if (query.length < MIN_QUERY_LENGTH) {
        clearResults();
        return;
      }

      renderLoading(menuMatches);

      if (cache[normalizedQuery]) {
        renderResults(query, menuMatches, cache[normalizedQuery]);
        return;
      }

      if (!api) {
        renderApiError(query, menuMatches);
        return;
      }

      var thisRequest = ++requestSequence;

      api.get({
        action: 'query',
        list: 'search',
        srsearch: query,
        srnamespace: 0,
        srlimit: API_LIMIT,
        srinfo: 'totalhits|suggestion|rewrittenquery',
        srprop: 'snippet|titlesnippet|sectiontitle|redirecttitle',
        srenablerewrites: 1,
        formatversion: 2,
        utf8: 1
      }).done(function (data) {
        if (thisRequest !== requestSequence) return;

        var payload = transformApiData(data);
        cache[normalizedQuery] = payload;
        renderResults(query, menuMatches, payload);
      }).fail(function () {
        if (thisRequest !== requestSequence) return;
        renderApiError(query, menuMatches);
      });
    }

    function getResultLinks() {
      return Array.prototype.slice.call(results.querySelectorAll('.oroza-search-result-link'));
    }

    function setActiveResult(index) {
      var links = getResultLinks();
      if (!links.length) return;

      links.forEach(function (link) {
        link.classList.remove('is-active');
        link.setAttribute('aria-selected', 'false');
      });

      if (index < 0) index = links.length - 1;
      if (index >= links.length) index = 0;

      activeIndex = index;
      links[activeIndex].classList.add('is-active');
      links[activeIndex].setAttribute('aria-selected', 'true');
      links[activeIndex].scrollIntoView({ block: 'nearest' });
    }

    input.addEventListener('input', function () {
      var query = input.value;
      clearButton.hidden = !query;
      activeIndex = -1;

      window.clearTimeout(debounceTimer);

      if (String(query || '').trim().length < MIN_QUERY_LENGTH) {
        requestSequence++;
        clearResults();
        return;
      }

      debounceTimer = window.setTimeout(function () {
        runSearch(query);
      }, DEBOUNCE_MS);
    });

    input.addEventListener('focus', function () {
      if (input.value.trim().length >= MIN_QUERY_LENGTH && results.childNodes.length) {
        setOpen(true);
      }
    });

    input.addEventListener('keydown', function (event) {
      if (event.key === 'ArrowDown') {
        event.preventDefault();
        if (results.hidden) runSearch(input.value);
        setActiveResult(activeIndex + 1);
      } else if (event.key === 'ArrowUp') {
        event.preventDefault();
        setActiveResult(activeIndex - 1);
      } else if (event.key === 'Enter' && activeIndex >= 0) {
        var links = getResultLinks();
        if (links[activeIndex]) {
          event.preventDefault();
          window.location.href = links[activeIndex].href;
        }
      } else if (event.key === 'Escape') {
        setOpen(false);
        input.blur();
      }
    });

    clearButton.addEventListener('click', function () {
      input.value = '';
      clearButton.hidden = true;
      requestSequence++;
      window.clearTimeout(debounceTimer);
      clearResults();
      input.focus();
    });

    document.addEventListener('click', function (event) {
      if (!widget.contains(event.target)) setOpen(false);
    });

    form.addEventListener('submit', function (event) {
      if (!input.value.trim()) {
        event.preventDefault();
        input.focus();
      }
    });
  }

  function boot(root) {
    root = root || document;

    var widgets = root.querySelectorAll ? root.querySelectorAll(SELECTOR) : [];
    Array.prototype.forEach.call(widgets, function (widget) {
      initSearch(widget.parentNode || document);
    });
  }

  function bootWithMediaWikiModules(root) {
    if (window.mw && mw.loader && mw.loader.using) {
      mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () {
        boot(root);
      }, function () {
        boot(root);
      });
    } else {
      boot(root);
    }
  }

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

  if (window.mw && mw.hook) {
    mw.hook('wikipage.content').add(function ($content) {
      var node = $content && $content[0] ? $content[0] : document;
      bootWithMediaWikiModules(node);
      bootWithMediaWikiModules(document);
    });
  }
})();