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
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 = 5; // bumped: end-of-bar value + numbered tick/check row


   if (typeof COMMON.levelsInit === "number" && COMMON.levelsInit >= LEVELS_VERSION) return;
   if (typeof COMMON.levelsInit === "number" && COMMON.levelsInit >= LEVELS_VERSION) return;
Line 149: Line 149:
     // 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 204:
     }
     }
   }
   }
  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 238:
       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 254:
       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 270:
     slider.setAttribute("data-max", String(max));
     slider.setAttribute("data-max", String(max));
   }
   }
  /* ================================================================== */
  /* Custom slider value bubble (existing behavior; kept)                */
  /* ================================================================== */


   function ensureValueLabel(slider) {
   function ensureValueLabel(slider) {
Line 252: Line 286:
     bubble.setAttribute("aria-hidden", "true");
     bubble.setAttribute("aria-hidden", "true");


     // Minimal layout-only styling (no color system changes yet).
     // Minimal layout-only styling (no color system changes here).
    // We'll fully theme this in CSS next.
     bubble.style.position = "absolute";
     bubble.style.position = "absolute";
     bubble.style.top = "-28px";
     bubble.style.top = "-28px";
Line 266: Line 299:
     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 305:
     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 315:
     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 336:


   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 345:
     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 372:
   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 379:
     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);
  }
 
  /* ================================================================== */
  /* NEW: End-of-bar value label (right-side value)                      */
  /* ================================================================== */
 
  function ensureEndValueLabel(card, slider) {
     var wrap = null;
    if (slider) wrap = closest(slider, ".sv-level-slider");
    if (!wrap && card && card.querySelector) wrap = card.querySelector(".sv-level-slider");
     if (!wrap) return null;
 
    var out = wrap.querySelector(".sv-level-endvalue");
    if (out) return out;
 
    out = document.createElement("span");
    out.className = "sv-level-endvalue";
    out.setAttribute("aria-hidden", "true"); // slider already exposes aria-valuetext
    wrap.appendChild(out);
    return out;
   }
   }


   function setTickLabels(boundary, min, max) {
  function setEndValueLabel(card, slider, value) {
    var out = ensureEndValueLabel(card, slider);
    if (out) out.textContent = String(value);
  }
 
  /* ================================================================== */
  /* Tick/check row (numbered checkmarks)                                */
  /* ================================================================== */
 
  function buildTickNode(level, extraClass) {
    var tick = document.createElement("span");
    tick.className = "sv-level-tick" + (extraClass ? " " + extraClass : "");
    tick.setAttribute("data-level", String(level));
 
    var mark = document.createElement("span");
    mark.className = "sv-level-tickmark";
    mark.textContent = "✓";
 
    var num = document.createElement("span");
    num.className = "sv-level-ticknum";
    num.textContent = String(level);
 
    tick.appendChild(mark);
    tick.appendChild(num);
    return tick;
  }
 
   function setTickLabels(boundary, min, max, value, step) {
     if (!boundary || !boundary.querySelector) return;
     if (!boundary || !boundary.querySelector) return;
     var ticks = boundary.querySelector(".sv-level-ticklabels");
     var ticks = boundary.querySelector(".sv-level-ticklabels");
     if (!ticks) return;
     if (!ticks) return;


     // Always show min/max (lightweight + useful). We can enhance into real ticks later.
     step = step && step > 0 ? step : 1;
    // Only rebuild if needed to avoid layout churn.
 
    var count = Math.floor((max - min) / step) + 1;
    if (count < 2) count = 2;
 
    // Full ticks for small ranges; min/max only for large ranges.
    var mode = count <= 25 ? "full" : "minmax";
    var nextKey = String(min) + "/" + String(max) + "/" + String(step) + "/" + mode;
     var curKey = ticks.getAttribute("data-sv-ticks") || "";
     var curKey = ticks.getAttribute("data-sv-ticks") || "";
    var nextKey = String(min) + "/" + String(max);
    if (curKey === nextKey) return;
    ticks.setAttribute("data-sv-ticks", nextKey);


     while (ticks.firstChild) ticks.removeChild(ticks.firstChild);
     if (curKey !== nextKey) {
      ticks.setAttribute("data-sv-ticks", nextKey);


    var a = document.createElement("span");
      while (ticks.firstChild) ticks.removeChild(ticks.firstChild);
    a.className = "sv-level-tick sv-level-tick--min";
    a.textContent = String(min);


    var b = document.createElement("span");
      if (mode === "full") {
    b.className = "sv-level-tick sv-level-tick--max";
        for (var lv = min; lv <= max; lv += step) {
    b.textContent = String(max);
          var cls = "";
          if (lv === min) cls = "sv-level-tick--min";
          else if (lv === max) cls = "sv-level-tick--max";
          ticks.appendChild(buildTickNode(lv, cls));
        }
      } else {
        ticks.appendChild(buildTickNode(min, "sv-level-tick--min"));
        ticks.appendChild(buildTickNode(max, "sv-level-tick--max"));
      }
    }


     ticks.appendChild(a);
     // Update checked/current state every time the level changes.
    ticks.appendChild(b);
    var children = ticks.children;
    for (var i = 0; i < children.length; i++) {
      var node = children[i];
      if (!node || !node.getAttribute) continue;
 
      var lvStr = node.getAttribute("data-level");
      var lvNum = parseInt(lvStr, 10);
 
      if (node.classList) {
        node.classList.remove("sv-level-tick--checked");
        node.classList.remove("sv-level-tick--current");
      }
 
      if (!isNaN(lvNum)) {
        if (lvNum <= value && node.classList) node.classList.add("sv-level-tick--checked");
        if (lvNum === value && node.classList) node.classList.add("sv-level-tick--current");
      }
    }
   }
   }
  /* ================================================================== */
  /* Core apply / init                                                    */
  /* ================================================================== */


   function scheduleApply(card, rawLevel) {
   function scheduleApply(card, rawLevel) {
Line 460: Line 540:
       }
       }
     }
     }
    // NEW: value readout at the end/right of the slider bar
    setEndValueLabel(card, slider, level);


     var boundary = getLevelBoundary(slider);
     var boundary = getLevelBoundary(slider);
     setTickLabels(boundary, minLevel, maxLevel);
     setTickLabels(boundary, minLevel, maxLevel, level, getStep(slider));


     var scope = getLevelScopeContainer(card, boundary);
     var scope = getLevelScopeContainer(card, boundary);
Line 493: Line 576:
     }
     }
   }
   }
  /* ================================================================== */
  /* Events                                                              */
  /* ================================================================== */


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


       // Show value label while interacting
       // Show value bubble while interacting (existing behavior)
       showValueLabel(slider);
       showValueLabel(slider);


Line 580: Line 667:
   // - 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 706:
   );
   );


   // 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 715:
       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);