MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
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 = | 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- | 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 | // 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"); | ||
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"); | ||
slider.appendChild(bubble); | slider.appendChild(bubble); | ||
return bubble; | return bubble; | ||
} | } | ||
| Line 285: | Line 303: | ||
if (!bubble) return; | if (!bubble) return; | ||
var | var b = getCustomBounds(slider, 1); | ||
bubble.textContent = String(value); | bubble.textContent = String(value); | ||
bubble.style.left = | bubble.style.left = pctFromValue(b.min, b.max, value) * 100 + "%"; | ||
} | } | ||
| Line 302: | Line 313: | ||
if (!bubble) return; | if (!bubble) return; | ||
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 | var b = getCustomBounds(slider, 1); | ||
var | var left = pctFromValue(b.min, b.max, value) * 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 + "%"; | ||
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 | var b = getCustomBounds(slider, 1); | ||
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); | ||
return snapToStep(raw, b.min, b.max, getStep(slider)); | |||
return snapToStep(raw, min, max, | |||
} | } | ||
/* ================================================================== */ | |||
/* 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 ( | 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; | |||
} | |||
var | function setEndValueLabel(card, slider, value) { | ||
var out = ensureEndValueLabel(card, slider); | |||
if (out) out.textContent = String(value); | |||
} | |||
/* ================================================================== */ | |||
/* Core apply / init */ | |||
/* ================================================================== */ | |||
function scheduleApply(card, rawLevel) { | function scheduleApply(card, rawLevel) { | ||
| Line 414: | Line 416: | ||
if (card._svLevelRaf) return; | if (card._svLevelRaf) return; | ||
if (window.requestAnimationFrame) { | |||
card._svLevelRaf = requestAnimationFrame(function () { | |||
card._svLevelRaf = null; | |||
var v = card._svLevelNext; | |||
card._svLevelNext = null; | |||
applyLevelToCard(card, v); | |||
}); | |||
return; | |||
} | |||
card._svLevelRaf = setTimeout(function () { | |||
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); | ||
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: | ||
} | } | ||
showValueLabel(slider); | showValueLabel(slider); | ||
scheduleApply(card, valueFromClientX(slider, e.clientX)); | scheduleApply(card, valueFromClientX(slider, e.clientX)); | ||
}, | }, | ||
| Line 567: | Line 574: | ||
} | } | ||
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 | // 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 | var b = getCustomBounds(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; | ||
var tight = vw <= 520; | var tight = vw <= 520; | ||
var margin = tight ? 18 : 10; | var margin = tight ? 18 : 10; | ||