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 348: Line 348:
  * - Reads: data-sv-def-tip, data-sv-def-link
  * - Reads: data-sv-def-tip, data-sv-def-link
  * - Works with spans output by Module:Definitions
  * - Works with spans output by Module:Definitions
  * ========================================================================== */
  * ========================================================================= */
(function (mw) {
(function (mw) {
   "use strict";
   "use strict";
Line 358: Line 358:
   var activeEl = null;
   var activeEl = null;
   var pinned = false;
   var pinned = false;
  var lastPoint = null; // {x,y} for touch positioning


   function qTipText(el) {
   function qTipText(el) {
Line 391: Line 392:
     tipEl.style.lineHeight = "1.25";
     tipEl.style.lineHeight = "1.25";
     tipEl.style.boxShadow = "0 8px 24px rgba(0,0,0,0.35)";
     tipEl.style.boxShadow = "0 8px 24px rgba(0,0,0,0.35)";
     tipEl.style.pointerEvents = "none";
     tipEl.style.pointerEvents = "none"; // toggled in position/show


     tipInner = document.createElement("div");
     tipInner = document.createElement("div");
Line 432: Line 433:
     tipEl.style.top = "0px";
     tipEl.style.top = "0px";
     tipEl.style.display = "block";
     tipEl.style.display = "block";
    tipEl.style.pointerEvents = pinned ? "auto" : "none";


     var tr = tipEl.getBoundingClientRect();
     var tr = tipEl.getBoundingClientRect();
Line 448: Line 450:
     tipEl.style.left = Math.round(x) + "px";
     tipEl.style.left = Math.round(x) + "px";
     tipEl.style.top = Math.round(y) + "px";
     tipEl.style.top = Math.round(y) + "px";
  }
  // Position relative to a screen point (used for touch so the finger doesn't cover it)
  function positionTipPoint(clientX, clientY) {
    if (!tipEl) return;
    // Ensure we can measure tip size
    tipEl.style.left = "0px";
    tipEl.style.top = "0px";
    tipEl.style.display = "block";
    tipEl.style.pointerEvents = pinned ? "auto" : "none";
    var tr = tipEl.getBoundingClientRect();
    var pad = 8;
    var gap = 12;
    var x = clientX - (tr.width / 2);
    x = clamp(x, pad, window.innerWidth - tr.width - pad);
    // Prefer ABOVE the finger/cursor
    var y = clientY - tr.height - gap;
    // If not enough room above, drop below
    if (y < pad) {
      y = clientY + gap;
    }
    y = clamp(y, pad, window.innerHeight - tr.height - pad);
    tipEl.style.left = Math.round(x) + "px";
    tipEl.style.top = Math.round(y) + "px";
    lastPoint = { x: clientX, y: clientY };
   }
   }


Line 462: Line 495:


     suppressTitle(el);
     suppressTitle(el);
    // Default show behavior anchors to the element
     positionTip(el);
     positionTip(el);
   }
   }
Line 475: Line 510:
     activeEl = null;
     activeEl = null;
     pinned = false;
     pinned = false;
    lastPoint = null;
   }
   }


Line 487: Line 523:


   // Hover in/out (desktop)
   // Hover in/out (desktop)
   document.addEventListener("mouseover", function (e) {
   document.addEventListener(
    if (pinned) return;
    "mouseover",
    var el = closestDef(e.target);
    function (e) {
    if (!el) return;
      if (pinned) return;
    if (isEntering(el, e.relatedTarget)) return;
      var el = closestDef(e.target);
    if (activeEl === el) return;
      if (!el) return;
    show(el);
      if (isEntering(el, e.relatedTarget)) return;
   }, true);
      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
  );
 
  // Touch: pin tooltip and position ABOVE the finger press point.
  // We do this on pointerdown to avoid the finger hiding the tooltip.
  document.addEventListener(
    "pointerdown",
    function (e) {
      var el = closestDef(e.target);
      if (!el) return;
 
      if (e.pointerType !== "touch") return;
 
      // If we have a future link, let normal navigation happen.
      if (qLink(el)) return;
 
      var txt = qTipText(el);
      if (!txt) return;
 
      pinned = true;
      show(el);
      positionTipPoint(e.clientX, e.clientY);


  document.addEventListener("mouseout", function (e) {
      // Prevent the follow-up click from immediately toggling/closing.
    if (pinned) return;
      e.preventDefault();
    var el = closestDef(e.target);
      e.stopPropagation();
    if (!el) return;
     },
     if (isEntering(el, e.relatedTarget)) return;
     true
     if (activeEl === el) hide();
   );
   }, true);


   // Tap/click behavior:
   // Tap/click behavior:
   // - If data-sv-def-link is set, navigate (future-ready).
   // - If data-sv-def-link is set, navigate (future-ready).
   // - Otherwise toggle a pinned tooltip (mobile-friendly).
   // - Otherwise toggle a pinned tooltip (mobile-friendly).
   document.addEventListener("click", function (e) {
   document.addEventListener(
    var el = closestDef(e.target);
    "click",
    function (e) {
      var el = closestDef(e.target);


    // Click outside closes a pinned tooltip
      // Click outside closes a pinned tooltip
    if (!el) {
      if (!el) {
      if (pinned) hide();
        if (pinned) hide();
       return;
        return;
    }
       }
 
      var link = qLink(el);
      if (link) {
        window.location.href = mw.util.getUrl(link);
        return;
      }


    var link = qLink(el);
      var txt = qTipText(el);
    if (link) {
      if (!txt) return;
      // Let it act like a link to a page title
      window.location.href = mw.util.getUrl(link);
      return;
    }


    var txt = qTipText(el);
      // Toggle pin
    if (!txt) return;
      e.preventDefault();
      e.stopPropagation();


    // Toggle pin
      if (pinned && activeEl === el) {
    e.preventDefault();
        hide();
    e.stopPropagation();
        return;
      }


    if (pinned && activeEl === el) {
      pinned = true;
       hide();
       show(el);
      return;
    }


    pinned = true;
      // For non-touch click, keep element-anchored positioning.
    show(el);
      // (Touch uses pointerdown positioning already.)
  }, true);
      lastPoint = null;
    },
    true
  );


   // Close on Escape
   // Close on Escape
   document.addEventListener("keydown", function (e) {
   document.addEventListener(
    if (e.key === "Escape" && (pinned || activeEl)) {
    "keydown",
      hide();
    function (e) {
    }
      if (e.key === "Escape" && (pinned || activeEl)) {
  }, true);
        hide();
      }
    },
    true
  );


   // Reposition if visible
   // Reposition if visible
   window.addEventListener("resize", function () {
   window.addEventListener(
    if (activeEl && tipEl && tipEl.style.display === "block") {
    "resize",
      positionTip(activeEl);
    function () {
    }
      if (tipEl && tipEl.style.display === "block") {
  }, true);
        if (pinned && lastPoint) positionTipPoint(lastPoint.x, lastPoint.y);
        else if (activeEl) positionTip(activeEl);
      }
    },
    true
  );


   // Capture scroll events from containers too
   // Capture scroll events from containers too
   window.addEventListener("scroll", function () {
   window.addEventListener(
    if (activeEl && tipEl && tipEl.style.display === "block") {
    "scroll",
      positionTip(activeEl);
    function () {
    }
      if (tipEl && tipEl.style.display === "block") {
  }, true);
        if (pinned && lastPoint) positionTipPoint(lastPoint.x, lastPoint.y);
        else if (activeEl) positionTip(activeEl);
      }
    },
    true
  );


   // Support dynamic page content (VisualEditor, etc.)
   // Support dynamic page content (VisualEditor, etc.)