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 347: Line 347:
  * SV Definitions v1 — tooltips for .sv-def
  * SV Definitions v1 — tooltips for .sv-def
  * - 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
  * - Only activates when data-sv-def-tip is non-empty
  * ========================================================================= */
  * ========================================================================== */
(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 point positioning
   var lastPoint = null; // {x,y}
  var lastTouchTime = 0;


   function qTipText(el) {
   function qTipText(el) {
Line 390: Line 391:
     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"; // toggled when pinned
     tipEl.style.pointerEvents = "none";


     tipInner = document.createElement("div");
     tipInner = document.createElement("div");
Line 421: Line 422:
   }
   }


  // Cursor/finger anchored positioning; prefers ABOVE the point.
   function positionTipPoint(clientX, clientY) {
   function positionTipPoint(clientX, clientY) {
     if (!tipEl) return;
     if (!tipEl) return;
Line 431: Line 433:
     var tr = tipEl.getBoundingClientRect();
     var tr = tipEl.getBoundingClientRect();
     var pad = 8;
     var pad = 8;
     var gap = 12;
     var gap = pinned ? 14 : 18; // larger gap on hover so cursor doesn't cover text


     var x = clientX - (tr.width / 2);
     var x = clientX - tr.width / 2;
     x = clamp(x, pad, window.innerWidth - tr.width - pad);
     x = clamp(x, pad, window.innerWidth - tr.width - pad);


     var y = clientY - tr.height - gap;
     var y = clientY - tr.height - gap; // ABOVE by default
     if (y < pad) y = clientY + gap;
     if (y < pad) y = clientY + gap;   // flip below if needed
     y = clamp(y, pad, window.innerHeight - tr.height - pad);
     y = clamp(y, pad, window.innerHeight - tr.height - pad);


Line 450: Line 452:


     var txt = qTipText(el);
     var txt = qTipText(el);
     if (!txt) return;
     if (!txt) return false;


     activeEl = el;
     activeEl = el;
Line 458: Line 460:


     suppressTitle(el);
     suppressTitle(el);
    return true;
   }
   }


Line 482: Line 485:
   }
   }


   // Hover in/out (desktop) — disable rollover if tip is blank
   // Desktop hover
   document.addEventListener(
   document.addEventListener("mouseover", function (e) {
    "mouseover",
    if (pinned) return;
    function (e) {
      if (pinned) return;


      var el = closestDef(e.target);
    var el = closestDef(e.target);


      // If we moved onto non-definition content, hide any visible hover tooltip
    if (!el) {
      if (!el) {
      if (activeEl) hide();
        if (activeEl) hide();
      return;
        return;
    }
      }
 
    if (isEntering(el, e.relatedTarget)) return;
    if (activeEl === el) return;


      if (isEntering(el, e.relatedTarget)) return;
    // Blank definition disables rollover AND clears any previous tooltip
       if (activeEl === el) return;
    if (!qTipText(el)) {
       if (activeEl) hide();
      return;
    }


      var txt = qTipText(el);
    if (show(el)) positionTipPoint(e.clientX, e.clientY);
      if (!txt) {
  }, true);
        // IMPORTANT: no tooltip data -> ensure the old tooltip is not left up
        if (activeEl) hide();
        return;
      }


      show(el);
  document.addEventListener("mousemove", function (e) {
      positionTipPoint(e.clientX, e.clientY);
     if (pinned) return;
     },
    true
  );


  document.addEventListener(
    var el = closestDef(e.target);
     "mousemove",
     if (!el) {
    function (e) {
       if (activeEl) hide();
       if (pinned) return;
      return;
    }


      var el = closestDef(e.target);
    if (activeEl && el !== activeEl) return;
      if (!el) {
        if (activeEl) hide();
        return;
      }


       if (activeEl && el !== activeEl) return;
    if (!qTipText(el)) {
       if (activeEl) hide();
      return;
    }


      var txt = qTipText(el);
    if (activeEl) positionTipPoint(e.clientX, e.clientY);
      if (!txt) {
  }, true);
        if (activeEl) hide();
        return;
      }


      // Track mouse so it never sits under the cursor
  document.addEventListener("mouseout", function (e) {
      if (activeEl) positionTipPoint(e.clientX, e.clientY);
    if (pinned) return;
     },
    var el = closestDef(e.target);
    true
    if (!el) return;
  );
    if (isEntering(el, e.relatedTarget)) return;
     if (activeEl === el) hide();
  }, true);


   document.addEventListener(
  // Touch pin (positions above finger)
    "mouseout",
   document.addEventListener("pointerdown", function (e) {
    function (e) {
    var el = closestDef(e.target);
      if (pinned) return;
    if (!el) 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.
     if (e.pointerType !== "touch") return;
  // If tip is blank, tap does nothing (and closes any pinned tooltip).
  document.addEventListener(
     "pointerdown",
    function (e) {
      var el = closestDef(e.target);
      if (!el) return;


      if (e.pointerType !== "touch") return;
    // Link = allow navigation
    if (qLink(el)) return;


      // If we have a link, let navigation happen.
    // Blank tip = do nothing (and close any pinned tooltip)
      if (qLink(el)) return;
    if (!qTipText(el)) {
      if (pinned) hide();
      return;
    }


      var txt = qTipText(el);
    lastTouchTime = Date.now();
      if (!txt) {
    pinned = true;
        if (pinned) hide();
        return;
      }


      pinned = true;
    if (show(el)) positionTipPoint(e.clientX, e.clientY);
      show(el);
      positionTipPoint(e.clientX, e.clientY);


      e.preventDefault();
    e.preventDefault();
      e.stopPropagation();
    e.stopPropagation();
    },
  }, true);
    true
  );


   // Click behavior:
   // Click behavior:
   // - If link exists, navigate.
   // - link -> navigate
   // - If tip exists, toggle pinned tooltip.
   // - tip -> toggle pinned
   // - If tip is blank, do nothing (and close pinned if clicking another def).
   // - blank tip -> close pinned
   document.addEventListener(
   document.addEventListener("click", function (e) {
    "click",
    // Ignore the synthetic click right after a touch pin
    function (e) {
    if (Date.now() - lastTouchTime < 500) return;
      var el = closestDef(e.target);


      if (!el) {
    var el = closestDef(e.target);
        if (pinned) hide();
        return;
      }


      var link = qLink(el);
    if (!el) {
       if (link) {
       if (pinned) hide();
        window.location.href = mw.util.getUrl(link);
      return;
        return;
    }
      }


      var txt = qTipText(el);
    var link = qLink(el);
      if (!txt) {
    if (link) {
        if (pinned) hide();
      window.location.href = mw.util.getUrl(link);
        return;
      return;
      }
    }


       e.preventDefault();
    if (!qTipText(el)) {
       e.stopPropagation();
       if (pinned) hide();
       return;
    }


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


      pinned = true;
    if (pinned && activeEl === el) {
       show(el);
       hide();
       positionTipPoint(e.clientX, e.clientY);
       return;
     },
     }
    true
  );


  document.addEventListener(
     pinned = true;
     "keydown",
     if (show(el)) positionTipPoint(e.clientX, e.clientY);
     function (e) {
  }, true);
      if (e.key === "Escape" && (pinned || activeEl)) hide();
    },
    true
  );


   window.addEventListener(
   document.addEventListener("keydown", function (e) {
    "resize",
    if (e.key === "Escape" && (pinned || activeEl)) hide();
    function () {
  }, true);
      if (tipEl && tipEl.style.display === "block" && lastPoint) {
        positionTipPoint(lastPoint.x, lastPoint.y);
      }
    },
    true
  );


   window.addEventListener(
   window.addEventListener("resize", function () {
     "scroll",
     if (tipEl && tipEl.style.display === "block" && lastPoint) {
     function () {
      positionTipPoint(lastPoint.x, lastPoint.y);
      if (tipEl && tipEl.style.display === "block" && lastPoint) {
     }
        positionTipPoint(lastPoint.x, lastPoint.y);
  }, true);
      }
 
    },
  window.addEventListener("scroll", function () {
    true
    if (tipEl && tipEl.style.display === "block" && lastPoint) {
  );
      positionTipPoint(lastPoint.x, lastPoint.y);
    }
  }, true);


  mw.hook("wikipage.content").add(function () {
    // No-op: event delegation already covers new content.
  });
})(window.mw);
})(window.mw);