MediaWiki:Timeless.js

From Game Wiki - VortanMU
Revision as of 09:12, 16 January 2026 by Admin (talk | contribs)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/*
  VortanMU - Timeless sidebar behavior (v3)
  - Hover to open (desktop) + click to toggle (touch/keyboard)
  - Keeps original Timeless structure (portlets)
  - Avoids conflicts with any previously-injected mega-menu nodes

  Install: paste into MediaWiki:Timeless.js (replace your current sidebar script).
*/
(function () {
  'use strict';
  if (window.VM_TIMELESS_SIDEBAR_V3) return;
  window.VM_TIMELESS_SIDEBAR_V3 = true;

  function enhanceSidebar() {
    var nav = document.getElementById('mw-site-navigation');
    if (!nav) return;

    nav.classList.add('vm-nav');

    // Remove any old injected mega-menu nodes (common source of “applies then reverts”)
    Array.prototype.forEach.call(nav.querySelectorAll('ul.submenu'), function (el) {
      el.parentNode && el.parentNode.removeChild(el);
    });

    var portlets = Array.prototype.slice.call(
      nav.querySelectorAll('.mw-portlet[role="navigation"]')
    );

    var hoverCapable = !!(window.matchMedia && window.matchMedia('(hover:hover) and (pointer:fine)').matches);

    function setExpanded(p, expanded) {
      var h = p.querySelector('h3.vm-portlet__header') || p.querySelector('h3');
      if (h) h.setAttribute('aria-expanded', expanded ? 'true' : 'false');
    }

    function closePortlet(p) {
      p.classList.remove('is-open');
      setExpanded(p, false);
    }

    function openPortlet(p) {
      // close others (keeps sidebar clean), but never close Wiki tools
      portlets.forEach(function (other) {
        if (other !== p && other.id !== 'p-tb') closePortlet(other);
      });
      p.classList.add('is-open');
      setExpanded(p, true);
    }

    portlets.forEach(function (p) {
      var h3 = p.querySelector('h3');
      var body = p.querySelector('.mw-portlet-body');
      if (!h3 || !body) return;

      h3.classList.add('vm-portlet__header');
      body.classList.add('vm-portlet__body');

      // If something hid the original <ul> inline, bring it back
      var ul = body.querySelector('ul');
      if (ul && ul.style && ul.style.display === 'none') ul.style.display = '';

      // Wiki tools: always visible
      if (p.id === 'p-tb') {
        p.classList.add('is-open');
        h3.setAttribute('aria-expanded', 'true');
        return;
      }

      // default closed
      closePortlet(p);

      // accessibility + click toggle
      h3.setAttribute('role', 'button');
      h3.setAttribute('tabindex', '0');
      h3.setAttribute('aria-expanded', 'false');

      function toggle() {
        if (p.classList.contains('is-open')) closePortlet(p);
        else openPortlet(p);
      }

      h3.addEventListener('click', function (e) {
        e.preventDefault();
        toggle();
      });

      h3.addEventListener('keydown', function (e) {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          toggle();
        }
      });

      // Hover open (desktop)
      if (hoverCapable) {
        var t;
        p.addEventListener('mouseenter', function () {
          if (t) clearTimeout(t);
          openPortlet(p);
        });
        p.addEventListener('mouseleave', function () {
          if (t) clearTimeout(t);
          t = setTimeout(function () { closePortlet(p); }, 180);
        });
      }
    });
  }

  // Run on normal loads + after purge/navigation
  if (window.mw && mw.hook) {
    mw.hook('wikipage.content').add(enhanceSidebar);
  } else if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', enhanceSidebar);
  } else {
    enhanceSidebar();
  }
})();

/* ==============================
   VortanMU - Mega flyout (hover + colunas)
   Cole no FINAL do MediaWiki:Timeless.js
   ============================== */
(function () {
  function onReady(fn) {
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", fn);
    } else {
      fn();
    }
  }

  function clamp(n, min, max) {
    return Math.max(min, Math.min(max, n));
  }

  function calcColumns(portletId, itemCount) {
    // Regra principal: ~6 itens por coluna
    // Mapas é exceção: aceita mais por coluna pra não virar 5+ colunas
    var perCol = 6;
    if (portletId === "p-Mapas") perCol = 10;

    var cols = Math.ceil(itemCount / perCol);
    return clamp(cols, 1, 4);
  }

  onReady(function () {
    var nav = document.getElementById("site-navigation");
    if (!nav) return;

    var portlets = nav.querySelectorAll(".mw-portlet");
    portlets.forEach(function (portlet) {
      var h3 = portlet.querySelector("h3");
      var body = portlet.querySelector(".mw-portlet-body");
      if (!h3 || !body) return;

      var ul = body.querySelector("ul");
      if (!ul) return;

      var lis = Array.from(ul.querySelectorAll("li"));
      if (!lis.length) return;

      // Marca como “mega”
      h3.classList.add("has-mega");

      // Cria o submenu (se ainda não existe)
      var submenu = portlet.querySelector("ul.submenu");
      if (!submenu) {
        submenu = document.createElement("ul");
        submenu.className = "submenu";
        portlet.appendChild(submenu);
      }

      // Reconstrói os itens do flyout (mantém sempre consistente)
      submenu.innerHTML = "";
      lis.forEach(function (li) {
        var a = li.querySelector("a");
        if (!a) return;
        var li2 = document.createElement("li");
        li2.appendChild(a.cloneNode(true));
        submenu.appendChild(li2);
      });

      // Define quantidade de colunas por regra
      var count = submenu.querySelectorAll("li").length;
      var cols = calcColumns(portlet.id || "", count);

      submenu.classList.remove(
        "submenu-1-columns",
        "submenu-2-columns",
        "submenu-3-columns",
        "submenu-4-columns"
      );
      submenu.classList.add("submenu-" + cols + "-columns");

      // Esconde UL original (menu vira “botão” + flyout)
      ul.style.display = "none";
      body.style.display = "none";

      // Hover abre/fecha com pequeno delay (pra dar tempo de mover o mouse)
      var hideTimer = null;

      function show() {
        if (hideTimer) clearTimeout(hideTimer);
        submenu.style.display = "grid";
      }

      function hide() {
        hideTimer = setTimeout(function () {
          submenu.style.display = "none";
        }, 140);
      }

      // Hover desktop
      h3.addEventListener("mouseenter", show);
      portlet.addEventListener("mouseenter", show);
      portlet.addEventListener("mouseleave", hide);
      submenu.addEventListener("mouseenter", show);
      submenu.addEventListener("mouseleave", hide);

      // Clique (fallback)
      h3.addEventListener("click", function (e) {
        // em desktop isso não atrapalha, e em touch ajuda
        e.preventDefault();
        var isOpen = submenu.style.display === "grid";
        submenu.style.display = isOpen ? "none" : "grid";
      });
    });
  });
})();