Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Join the Playtest on Steam Now: SpiritVale

MediaWiki:Common.js: Difference between revisions

MediaWiki interface page
No edit summary
No edit summary
Line 343: Line 343:
     window.addEventListener("scroll",  function () { if (activeBtn) positionPop(activeBtn); }, true);
     window.addEventListener("scroll",  function () { if (activeBtn) positionPop(activeBtn); }, true);
})();
})();
/* ============================================================================
* SV Definitions v1 — tooltips for .sv-def
* - Reads: data-sv-def-tip, data-sv-def-link
* - Works with spans output by Module:Definitions
* ========================================================================== */
(function (mw) {
  "use strict";
  if (!mw || window.SV_DEF_INIT) return;
  window.SV_DEF_INIT = true;
  var tipEl = null;
  var tipInner = null;
  var activeEl = null;
  var pinned = false;
  function qTipText(el) {
    var s = (el && el.getAttribute("data-sv-def-tip")) || "";
    return (s || "").trim();
  }
  function qLink(el) {
    var s = (el && el.getAttribute("data-sv-def-link")) || "";
    return (s || "").trim();
  }
  function ensureTip() {
    if (tipEl) return;
    tipEl = document.createElement("div");
    tipEl.id = "sv-def-tip";
    tipEl.className = "sv-def-tip";
    tipEl.setAttribute("role", "tooltip");
    tipEl.setAttribute("aria-hidden", "true");
    // Minimal inline styling so it works even without CSS.
    // You can override in CSS later using #sv-def-tip / .sv-def-tip.
    tipEl.style.position = "fixed";
    tipEl.style.zIndex = "99999";
    tipEl.style.display = "none";
    tipEl.style.maxWidth = "340px";
    tipEl.style.padding = "8px 10px";
    tipEl.style.borderRadius = "8px";
    tipEl.style.background = "rgba(15, 18, 24, 0.96)";
    tipEl.style.color = "#fff";
    tipEl.style.fontSize = "13px";
    tipEl.style.lineHeight = "1.25";
    tipEl.style.boxShadow = "0 8px 24px rgba(0,0,0,0.35)";
    tipEl.style.pointerEvents = "none";
    tipInner = document.createElement("div");
    tipInner.className = "sv-def-tip-inner";
    tipEl.appendChild(tipInner);
    document.body.appendChild(tipEl);
  }
  function suppressTitle(el) {
    // Prevent double-tooltips (browser title tooltip + our custom tooltip).
    if (!el) return;
    var t = el.getAttribute("title");
    if (t && !el.getAttribute("data-sv-def-title")) {
      el.setAttribute("data-sv-def-title", t);
      el.removeAttribute("title");
    }
  }
  function restoreTitle(el) {
    if (!el) return;
    var t = el.getAttribute("data-sv-def-title");
    if (t) {
      el.setAttribute("title", t);
      el.removeAttribute("data-sv-def-title");
    }
  }
  function clamp(n, lo, hi) {
    return Math.max(lo, Math.min(hi, n));
  }
  function positionTip(el) {
    if (!tipEl || !el) return;
    var r = el.getBoundingClientRect();
    // Ensure we can measure tip size
    tipEl.style.left = "0px";
    tipEl.style.top = "0px";
    tipEl.style.display = "block";
    var tr = tipEl.getBoundingClientRect();
    var pad = 8;
    var gap = 8;
    var x = r.left + (r.width / 2) - (tr.width / 2);
    x = clamp(x, pad, window.innerWidth - tr.width - pad);
    var y = r.bottom + gap;
    if (y + tr.height > window.innerHeight - pad) {
      y = r.top - tr.height - gap;
    }
    y = clamp(y, pad, window.innerHeight - tr.height - pad);
    tipEl.style.left = Math.round(x) + "px";
    tipEl.style.top = Math.round(y) + "px";
  }
  function show(el) {
    ensureTip();
    var txt = qTipText(el);
    if (!txt) return;
    activeEl = el;
    tipInner.textContent = txt;
    tipEl.setAttribute("aria-hidden", "false");
    tipEl.style.display = "block";
    suppressTitle(el);
    positionTip(el);
  }
  function hide() {
    if (!tipEl) return;
    tipEl.style.display = "none";
    tipEl.setAttribute("aria-hidden", "true");
    if (activeEl) restoreTitle(activeEl);
    activeEl = null;
    pinned = false;
  }
  function isEntering(container, relatedTarget) {
    return relatedTarget && container && container.contains(relatedTarget);
  }
  function closestDef(target) {
    if (!target || !target.closest) return null;
    return target.closest(".sv-def");
  }
  // Hover in/out (desktop)
  document.addEventListener("mouseover", function (e) {
    if (pinned) return;
    var el = closestDef(e.target);
    if (!el) return;
    if (isEntering(el, e.relatedTarget)) return;
    if (activeEl === el) return;
    show(el);
  }, true);
  document.addEventListener("mouseout", function (e) {
    if (pinned) return;
    var el = closestDef(e.target);
    if (!el) return;
    if (isEntering(el, e.relatedTarget)) return;
    if (activeEl === el) hide();
  }, true);
  // Tap/click behavior:
  // - If data-sv-def-link is set, navigate (future-ready).
  // - Otherwise toggle a pinned tooltip (mobile-friendly).
  document.addEventListener("click", function (e) {
    var el = closestDef(e.target);
    // Click outside closes a pinned tooltip
    if (!el) {
      if (pinned) hide();
      return;
    }
    var link = qLink(el);
    if (link) {
      // Let it act like a link to a page title
      window.location.href = mw.util.getUrl(link);
      return;
    }
    var txt = qTipText(el);
    if (!txt) return;
    // Toggle pin
    e.preventDefault();
    e.stopPropagation();
    if (pinned && activeEl === el) {
      hide();
      return;
    }
    pinned = true;
    show(el);
  }, true);
  // Close on Escape
  document.addEventListener("keydown", function (e) {
    if (e.key === "Escape" && (pinned || activeEl)) {
      hide();
    }
  }, true);
  // Reposition if visible
  window.addEventListener("resize", function () {
    if (activeEl && tipEl && tipEl.style.display === "block") {
      positionTip(activeEl);
    }
  }, true);
  // Capture scroll events from containers too
  window.addEventListener("scroll", function () {
    if (activeEl && tipEl && tipEl.style.display === "block") {
      positionTip(activeEl);
    }
  }, true);
  // Support dynamic page content (VisualEditor, etc.)
  mw.hook("wikipage.content").add(function () {
    // No-op: event delegation already covers new content.
  });
})(window.mw);