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
Tags: Mobile edit Mobile web edit
No edit summary
Tags: Mobile edit Mobile web edit
 
(One intermediate revision by the same user not shown)
Line 17: Line 17:


   // Versioned init guard (allows safe updates without stale blocks)
   // Versioned init guard (allows safe updates without stale blocks)
   var LEVELS_VERSION = 4; // bumped: mouse-only slider + tick/label polish
   var LEVELS_VERSION = 6; // neutral hooks + bubble styling owned by CSS + no tick/check row


   if (typeof COMMON.levelsInit === "number" && COMMON.levelsInit >= LEVELS_VERSION) return;
   if (typeof COMMON.levelsInit === "number" && COMMON.levelsInit >= LEVELS_VERSION) return;
   COMMON.levelsInit = LEVELS_VERSION;
   COMMON.levelsInit = LEVELS_VERSION;


   var CARD_SEL = ".sv-gi-card, .sv-skill-card, .sv-passive-card";
  // Prefer neutral shared hooks; keep legacy class fallbacks for compatibility.
   var CARD_SEL =
    "[data-sv-card='1'], .sv-gi-card, .sv-skill-card, .sv-passive-card";


   // Accept the native range input and the custom slider variants.
   // Accept the native range input and the custom slider variants.
Line 28: Line 30:
     "input.sv-level-range[type='range'], .sv-level-range--custom, .sv-level-range[data-sv-slider='1']";
     "input.sv-level-range[type='range'], .sv-level-range--custom, .sv-level-range[data-sv-slider='1']";


   var LEVEL_BOUNDARY_SEL = ".sv-skill-level, .sv-gi-level";
   var LEVEL_BOUNDARY_SEL =
   var LEVEL_SCOPE_SEL = ".sv-gi-bottom, .sv-skill-bottom";
    "[data-sv-level-boundary='1'], .sv-skill-level, .sv-gi-level";
 
   var LEVEL_SCOPE_SEL =
    "[data-sv-level-scope='1'], .sv-gi-bottom, .sv-skill-bottom";
 
   var SERIES_SEL = "[data-series]";
   var SERIES_SEL = "[data-series]";
   var LEVEL_INIT_ATTR = "data-gi-level-init";
   var LEVEL_INIT_ATTR = "data-sv-level-init";


   var _seriesCache = typeof WeakMap !== "undefined" ? new WeakMap() : null;
   var _seriesCache = typeof WeakMap !== "undefined" ? new WeakMap() : null;
Line 117: Line 123:


   function getCardMinLevel(card, slider, maxLevel) {
   function getCardMinLevel(card, slider, maxLevel) {
     // Prefer explicit attributes if ever present (future-proof), then slider attrs.
     // Prefer explicit neutral hooks, then legacy attrs, then slider attrs.
     var raw =
     var raw =
       card.getAttribute("data-min-level") ||
       card.getAttribute("data-min-level") ||
Line 126: Line 132:
       if (slider) {
       if (slider) {
         if (isRangeInput(slider)) raw = slider.getAttribute("min");
         if (isRangeInput(slider)) raw = slider.getAttribute("min");
         else if (isCustomSlider(slider)) raw = slider.getAttribute("aria-valuemin") || slider.getAttribute("data-min");
         else if (isCustomSlider(slider))
          raw = slider.getAttribute("aria-valuemin") || slider.getAttribute("data-min");
       }
       }
     }
     }
Line 149: Line 156:
     // Default = 1. Support future: data-step, or <input step="">
     // Default = 1. Support future: data-step, or <input step="">
     var raw = null;
     var raw = null;
     if (slider) {
     if (slider) raw = slider.getAttribute("data-step") || slider.getAttribute("step");
      raw = slider.getAttribute("data-step") || slider.getAttribute("step");
    }
     var s = parseInt(raw, 10);
     var s = parseInt(raw, 10);
     if (!s || isNaN(s) || s < 1) s = 1;
     if (!s || isNaN(s) || s < 1) s = 1;
Line 206: Line 211:
     }
     }
   }
   }
  function unhide(el) {
    if (!el) return;
    if (el.classList) el.classList.remove("sv-hidden");
    if (el.removeAttribute) el.removeAttribute("hidden");
    if (el.style && el.style.display === "none") el.style.display = "";
  }
  function hide(el) {
    if (!el) return;
    if (el.classList) el.classList.add("sv-hidden");
    if (el.setAttribute) el.setAttribute("hidden", "hidden");
  }
  /* ================================================================== */
  /* Custom slider helpers (bounds/percent)                              */
  /* ================================================================== */


   function getCustomMin(slider, fallback) {
   function getCustomMin(slider, fallback) {
Line 223: Line 245:
       fallback != null ? fallback : 1
       fallback != null ? fallback : 1
     );
     );
  }
  function getCustomBounds(slider, fallbackMin) {
    var min = getCustomMin(slider, fallbackMin != null ? fallbackMin : 1);
    var max = getCustomMax(slider, min);
    if (max < min) max = min;
    return { min: min, max: max };
   }
   }


Line 232: Line 261:
       fallback != null ? fallback : min
       fallback != null ? fallback : min
     );
     );
  }
  function pctFromValue(min, max, value) {
    if (max === min) return 0;
    var pct = (value - min) / (max - min);
    if (pct < 0) pct = 0;
    if (pct > 1) pct = 1;
    return pct;
   }
   }


Line 240: Line 277:
     slider.setAttribute("data-max", String(max));
     slider.setAttribute("data-max", String(max));
   }
   }
  /* ================================================================== */
  /* Custom slider value bubble                                          */
  /* - JS owns structure + text/position only                            */
  /* - CSS owns visuals                                                  */
  /* ================================================================== */


   function ensureValueLabel(slider) {
   function ensureValueLabel(slider) {
Line 251: Line 294:
     bubble.setAttribute("hidden", "hidden");
     bubble.setAttribute("hidden", "hidden");
     bubble.setAttribute("aria-hidden", "true");
     bubble.setAttribute("aria-hidden", "true");
    // Minimal layout-only styling (no color system changes yet).
    // We'll fully theme this in CSS next.
    bubble.style.position = "absolute";
    bubble.style.top = "-28px";
    bubble.style.left = "0%";
    bubble.style.transform = "translateX(-50%)";
    bubble.style.whiteSpace = "nowrap";
    bubble.style.fontWeight = "900";
    bubble.style.fontSize = "12px";
    bubble.style.lineHeight = "1";
    bubble.style.pointerEvents = "none";


     slider.appendChild(bubble);
     slider.appendChild(bubble);
     return bubble;
     return bubble;
  }
  function unhide(el) {
    if (!el) return;
    if (el.classList) el.classList.remove("sv-hidden");
    if (el.removeAttribute) el.removeAttribute("hidden");
    if (el.style && el.style.display === "none") el.style.display = "";
  }
  function hide(el) {
    if (!el) return;
    if (el.classList) el.classList.add("sv-hidden");
    if (el.setAttribute) el.setAttribute("hidden", "hidden");
   }
   }


Line 285: Line 303:
     if (!bubble) return;
     if (!bubble) return;


     var min = getCustomMin(slider, 1);
     var b = getCustomBounds(slider, 1);
    var max = getCustomMax(slider, min);
    if (max < min) max = min;
 
    var pct = max === min ? 0 : (value - min) / (max - min);
    if (pct < 0) pct = 0;
    if (pct > 1) pct = 1;
 
     bubble.textContent = String(value);
     bubble.textContent = String(value);
     bubble.style.left = pct * 100 + "%";
     bubble.style.left = pctFromValue(b.min, b.max, value) * 100 + "%";
   }
   }


Line 302: Line 313:
     if (!bubble) return;
     if (!bubble) return;


    // Cancel any pending hide timers.
     if (slider._svBubbleTimer) {
     if (slider._svBubbleTimer) {
       clearTimeout(slider._svBubbleTimer);
       clearTimeout(slider._svBubbleTimer);
Line 324: Line 334:


   function updateCustomVisual(slider, value) {
   function updateCustomVisual(slider, value) {
     var min = getCustomMin(slider, 1);
     var b = getCustomBounds(slider, 1);
     var max = getCustomMax(slider, min);
     var left = pctFromValue(b.min, b.max, value) * 100;
    if (max < min) max = min;
 
    var pct = max === min ? 0 : (value - min) / (max - min);
    if (pct < 0) pct = 0;
    if (pct > 1) pct = 1;
 
    var left = pct * 100;


     var thumb = slider.querySelector(".sv-level-thumb");
     var thumb = slider.querySelector(".sv-level-thumb");
Line 340: Line 343:
     if (fill && fill.style) fill.style.width = left + "%";
     if (fill && fill.style) fill.style.width = left + "%";


    // Keep the value label aligned, if present/visible.
     setValueLabel(slider, value);
     setValueLabel(slider, value);
   }
   }
Line 368: Line 370:
   function valueFromClientX(slider, clientX) {
   function valueFromClientX(slider, clientX) {
     var rect = slider.getBoundingClientRect();
     var rect = slider.getBoundingClientRect();
     var min = getCustomMin(slider, 1);
     var b = getCustomBounds(slider, 1);
    var max = getCustomMax(slider, min);
    if (max < min) max = min;


     var w = rect.width || 1;
     var w = rect.width || 1;
Line 377: Line 377:
     if (x > 1) x = 1;
     if (x > 1) x = 1;


     var raw = min + x * (max - min);
     var raw = b.min + x * (b.max - b.min);
    var step = getStep(slider);
     return snapToStep(raw, b.min, b.max, getStep(slider));
     return snapToStep(raw, min, max, step);
   }
   }


   function setTickLabels(boundary, min, max) {
   /* ================================================================== */
    if (!boundary || !boundary.querySelector) return;
  /* End-of-bar value label (right-side value)                           */
    var ticks = boundary.querySelector(".sv-level-ticklabels");
  /* ================================================================== */
    if (!ticks) return;


    // Always show min/max (lightweight + useful). We can enhance into real ticks later.
  function ensureEndValueLabel(card, slider) {
     // Only rebuild if needed to avoid layout churn.
     var wrap = null;
     var curKey = ticks.getAttribute("data-sv-ticks") || "";
     if (slider) wrap = closest(slider, ".sv-level-slider");
     var nextKey = String(min) + "/" + String(max);
     if (!wrap && card && card.querySelector) wrap = card.querySelector(".sv-level-slider");
     if (curKey === nextKey) return;
     if (!wrap) return null;
    ticks.setAttribute("data-sv-ticks", nextKey);


     while (ticks.firstChild) ticks.removeChild(ticks.firstChild);
     var out = wrap.querySelector(".sv-level-endvalue");
    if (out) return out;


     var a = document.createElement("span");
     out = document.createElement("span");
     a.className = "sv-level-tick sv-level-tick--min";
     out.className = "sv-level-endvalue";
     a.textContent = String(min);
    out.setAttribute("aria-hidden", "true"); // slider already exposes aria-valuetext
     wrap.appendChild(out);
    return out;
  }


     var b = document.createElement("span");
  function setEndValueLabel(card, slider, value) {
     b.className = "sv-level-tick sv-level-tick--max";
     var out = ensureEndValueLabel(card, slider);
    b.textContent = String(max);
     if (out) out.textContent = String(value);
  }


    ticks.appendChild(a);
  /* ================================================================== */
    ticks.appendChild(b);
  /* Core apply / init                                                    */
   }
   /* ================================================================== */


   function scheduleApply(card, rawLevel) {
   function scheduleApply(card, rawLevel) {
Line 414: Line 416:
     if (card._svLevelRaf) return;
     if (card._svLevelRaf) return;


     card._svLevelRaf = window.requestAnimationFrame
     if (window.requestAnimationFrame) {
       ? requestAnimationFrame(function () {
       card._svLevelRaf = requestAnimationFrame(function () {
          card._svLevelRaf = null;
        card._svLevelRaf = null;
          var v = card._svLevelNext;
        var v = card._svLevelNext;
          card._svLevelNext = null;
        card._svLevelNext = null;
          applyLevelToCard(card, v);
        applyLevelToCard(card, v);
        })
      });
       : (function () {
       return;
          card._svLevelRaf = null;
    }
          var v = card._svLevelNext;
 
          card._svLevelNext = null;
    card._svLevelRaf = setTimeout(function () {
          applyLevelToCard(card, v);
      card._svLevelRaf = null;
        })();
      var v = card._svLevelNext;
      card._svLevelNext = null;
      applyLevelToCard(card, v);
    }, 0);
   }
   }


Line 460: Line 465:
       }
       }
     }
     }
    setEndValueLabel(card, slider, level);


     var boundary = getLevelBoundary(slider);
     var boundary = getLevelBoundary(slider);
    setTickLabels(boundary, minLevel, maxLevel);
     var scope = getLevelScopeContainer(card, boundary);
     var scope = getLevelScopeContainer(card, boundary);
     applySeriesToScope(scope, boundary, level);
     applySeriesToScope(scope, boundary, level);
Line 493: Line 498:
     }
     }
   }
   }
  /* ================================================================== */
  /* Events                                                              */
  /* ================================================================== */


   // Native range inputs
   // Native range inputs
Line 536: Line 545:
       }
       }


      // Show value label while interacting
       showValueLabel(slider);
       showValueLabel(slider);
       scheduleApply(card, valueFromClientX(slider, e.clientX));
       scheduleApply(card, valueFromClientX(slider, e.clientX));
     },
     },
Line 567: Line 574:
     }
     }


    // Let the value label linger briefly, then hide.
     hideValueLabelSoon(_drag.slider, 650);
     hideValueLabelSoon(_drag.slider, 650);
     _drag = null;
     _drag = null;
   }
   }
Line 580: Line 585:
   // - Block native <input type="range"> from changing via keyboard (if present)
   // - Block native <input type="range"> from changing via keyboard (if present)
   var _SV_BLOCK_KEYS = {
   var _SV_BLOCK_KEYS = {
     ArrowLeft: 1, ArrowRight: 1, ArrowUp: 1, ArrowDown: 1,
     ArrowLeft: 1,
     Left: 1, Right: 1, Up: 1, Down: 1,
    ArrowRight: 1,
     Home: 1, End: 1, PageUp: 1, PageDown: 1
    ArrowUp: 1,
    ArrowDown: 1,
     Left: 1,
    Right: 1,
    Up: 1,
    Down: 1,
     Home: 1,
    End: 1,
    PageUp: 1,
    PageDown: 1
   };
   };


Line 610: Line 624:
   );
   );


   // Show label on focus (keyboard discoverability), hide on blur
   // Show bubble on focus (discoverability), hide on blur
   document.addEventListener(
   document.addEventListener(
     "focusin",
     "focusin",
Line 619: Line 633:
       if (!slider) return;
       if (!slider) return;


       var max = getCustomMax(slider, 1);
       var b = getCustomBounds(slider, 1);
      var min = getCustomMin(slider, 1);
       var v = getCustomValue(slider, b.min, b.max, b.min);
       var v = getCustomValue(slider, min, max, min);


       setValueLabel(slider, v);
       setValueLabel(slider, v);
Line 972: Line 985:
       var vh = window.innerHeight || document.documentElement.clientHeight || 0;
       var vh = window.innerHeight || document.documentElement.clientHeight || 0;


      // CHANGE (safe): stricter edge padding on small screens
       var tight = vw <= 520;
       var tight = vw <= 520;
       var margin = tight ? 18 : 10;
       var margin = tight ? 18 : 10;