MediaWiki:Common.js: Difference between revisions
MediaWiki interface page
More actions
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
/* GameInfo JS (Phase 4.1 | /* GameInfo JS (Phase 4.1) | ||
Shared behaviors for all GameInfo modules | Shared, reusable behaviors for all GameInfo modules. */ | ||
(function () { | (function () { | ||
| Line 9: | Line 6: | ||
window.SV_GAMEINFO_41_INIT = 1; | window.SV_GAMEINFO_41_INIT = 1; | ||
/ | /* ------------------------------------------------------------------ */ | ||
// | /* Selectors */ | ||
/ | /* ------------------------------------------------------------------ */ | ||
var CARD_SEL = ".sv-gi-card | var CARD_SEL = ".sv-gi-card, .sv-skill-card, .sv-passive-card"; | ||
var LEVEL_RANGE_SEL = "input.sv-level-range[type='range']"; | // Level (supports legacy <input type="range"> AND Phase 4.1 custom slider) | ||
var LEVEL_RANGE_SEL = | |||
"input.sv-level-range[type='range'], .sv-level-range--custom[data-sv-slider='1']"; | |||
var LEVEL_BOUNDARY_SEL = ".sv-skill-level, .sv-gi-level"; | var LEVEL_BOUNDARY_SEL = ".sv-skill-level, .sv-gi-level"; | ||
var LEVEL_SCOPE_SEL = ".sv-gi-bottom, .sv-skill-bottom"; | var LEVEL_SCOPE_SEL = ".sv-gi-bottom, .sv-skill-bottom"; | ||
var SERIES_SEL = "[data-series]"; | var SERIES_SEL = "[data-series]"; | ||
var | // Popups (Phase 4.1 markup: span toggles + div popovers; legacy <details> supported) | ||
var | var POPUP_DETAILS_SEL = "details.sv-tip, details.sv-disclose"; // legacy | ||
var | var POPUP_WRAP_SEL = ".sv-tip, .sv-disclose"; | ||
var POPUP_BTN_SEL = | |||
".sv-tip-btn[data-sv-toggle='1'], .sv-disclose-btn[data-sv-toggle='1']"; | |||
var POPUP_POP_SEL = ".sv-tip-pop, .sv-disclose-pop"; | |||
var POPUP_HIDDEN_CLASS = "sv-hidden"; | |||
// Tabs | |||
var TABS_ROOT_SEL = ".sv-tabs[data-tabs='1']"; | var TABS_ROOT_SEL = ".sv-tabs[data-tabs='1']"; | ||
var TAB_SEL = " | var TAB_SEL = ".sv-tab"; | ||
var PANEL_SEL = ".sv-tabpanel"; | var PANEL_SEL = ".sv-tabpanel"; | ||
// Internals | /* ------------------------------------------------------------------ */ | ||
/* Internals */ | |||
/* ------------------------------------------------------------------ */ | |||
var _seriesCache = typeof WeakMap !== "undefined" ? new WeakMap() : null; | var _seriesCache = typeof WeakMap !== "undefined" ? new WeakMap() : null; | ||
var _tabsUidCounter = 0; | var _tabsUidCounter = 0; | ||
// ------------------------------------------------------------------ | // custom slider drag state | ||
// | var _drag = null; | ||
/ | |||
/* ------------------------------------------------------------------ */ | |||
/* Utilities */ | |||
/* ------------------------------------------------------------------ */ | |||
function clampInt(value, min, max, fallback) { | function clampInt(value, min, max, fallback) { | ||
| Line 52: | Line 62: | ||
} | } | ||
return null; | return null; | ||
} | |||
function hasClass(el, cls) { | |||
return !!(el && el.classList && el.classList.contains(cls)); | |||
} | } | ||
| Line 88: | Line 102: | ||
} | } | ||
function isRangeInput(el) { | |||
/ | return !!(el && el.tagName === "INPUT" && el.type === "range"); | ||
/ | } | ||
function isCustomSlider(el) { | |||
return !!(el && el.getAttribute && el.getAttribute("data-sv-slider") === "1"); | |||
} | |||
/* ------------------------------------------------------------------ */ | |||
/* Level Selector */ | |||
/* ------------------------------------------------------------------ */ | |||
function getCardMaxLevel(card) { | function getCardMaxLevel(card) { | ||
| Line 126: | Line 148: | ||
var el = nodes[i]; | var el = nodes[i]; | ||
// | // Phase 4.1 rule: only update fields below the level boundary. | ||
if (!isAfter(boundary, el)) continue; | if (!isAfter(boundary, el)) continue; | ||
| Line 132: | Line 154: | ||
if (!series || series.length === 0) continue; | if (!series || series.length === 0) continue; | ||
// Clamp if series shorter than expected. | |||
var safeIndex = index; | var safeIndex = index; | ||
if (safeIndex >= series.length) safeIndex = series.length - 1; | if (safeIndex >= series.length) safeIndex = series.length - 1; | ||
| Line 139: | Line 162: | ||
if (value == null) value = ""; | if (value == null) value = ""; | ||
// Text-only (no HTML mode). | |||
el.textContent = String(value); | el.textContent = String(value); | ||
} | } | ||
} | |||
function getCustomMin(slider) { | |||
return clampInt( | |||
slider.getAttribute("aria-valuemin") || slider.getAttribute("data-min"), | |||
1, | |||
999, | |||
1 | |||
); | |||
} | |||
function getCustomMax(slider) { | |||
return clampInt( | |||
slider.getAttribute("aria-valuemax") || slider.getAttribute("data-max"), | |||
1, | |||
999, | |||
1 | |||
); | |||
} | |||
function getCustomValue(slider, fallback) { | |||
return clampInt( | |||
slider.getAttribute("aria-valuenow") || slider.getAttribute("data-value"), | |||
1, | |||
999, | |||
fallback != null ? fallback : 1 | |||
); | |||
} | |||
function setCustomBounds(slider, min, max) { | |||
slider.setAttribute("aria-valuemin", String(min)); | |||
slider.setAttribute("aria-valuemax", String(max)); | |||
slider.setAttribute("data-min", String(min)); | |||
slider.setAttribute("data-max", String(max)); | |||
} | |||
function updateCustomVisual(slider, value) { | |||
var min = getCustomMin(slider); | |||
var max = getCustomMax(slider); | |||
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 fill = slider.querySelector(".sv-level-fill"); | |||
if (thumb && thumb.style) thumb.style.left = left + "%"; | |||
if (fill && fill.style) fill.style.width = left + "%"; | |||
} | |||
function setCustomValue(slider, value) { | |||
slider.setAttribute("aria-valuenow", String(value)); | |||
slider.setAttribute("data-value", String(value)); | |||
updateCustomVisual(slider, value); | |||
} | |||
function valueFromClientX(slider, clientX) { | |||
var rect = slider.getBoundingClientRect(); | |||
var min = getCustomMin(slider); | |||
var max = getCustomMax(slider); | |||
if (max < min) max = min; | |||
var w = rect.width || 1; | |||
var x = (clientX - rect.left) / w; | |||
if (x < 0) x = 0; | |||
if (x > 1) x = 1; | |||
var raw = min + x * (max - min); | |||
var rounded = Math.round(raw); | |||
return clampInt(rounded, min, max, min); | |||
} | } | ||
| Line 156: | Line 254: | ||
var level = clampInt(rawLevel, 1, maxLevel, getCardLevel(card, maxLevel)); | var level = clampInt(rawLevel, 1, maxLevel, getCardLevel(card, maxLevel)); | ||
// Store on card for other systems / debugging. | |||
card.setAttribute("data-level", String(level)); | card.setAttribute("data-level", String(level)); | ||
// Keep slider in sync. | |||
if (slider) { | if (slider) { | ||
slider.setAttribute("min", "1"); | if (isRangeInput(slider)) { | ||
slider.setAttribute("min", "1"); | |||
slider.setAttribute("max", String(maxLevel)); | |||
if (String(slider.value) !== String(level)) slider.value = String(level); | |||
} else if (isCustomSlider(slider)) { | |||
setCustomBounds(slider, 1, maxLevel); | |||
setCustomValue(slider, level); | |||
} | |||
} | } | ||
setLevelNumber(card, level); | setLevelNumber(card, level); | ||
// Apply dynamic series values (scoped below boundary). | |||
if (slider) { | if (slider) { | ||
var boundary = getLevelBoundary(slider); | var boundary = getLevelBoundary(slider); | ||
| Line 184: | Line 290: | ||
var slider = getLevelSlider(card); | var slider = getLevelSlider(card); | ||
// No slider (or max <= 1) => this card doesn't participate. | |||
if (!slider || maxLevel <= 1) { | if (!slider || maxLevel <= 1) { | ||
card.setAttribute("data-gi-level-init", "1"); | card.setAttribute("data-gi-level-init", "1"); | ||
| Line 189: | Line 296: | ||
} | } | ||
var start = slider.value; | var start; | ||
if (isRangeInput(slider)) { | |||
start = slider.value; | |||
} else if (isCustomSlider(slider)) { | |||
start = getCustomValue(slider, getCardLevel(card, maxLevel)); | |||
} else { | |||
start = getCardLevel(card, maxLevel); | |||
} | |||
if (start == null || start === "") start = getCardLevel(card, maxLevel); | if (start == null || start === "") start = getCardLevel(card, maxLevel); | ||
| Line 197: | Line 312: | ||
} | } | ||
// Slider input (delegated) — range input only | |||
document.addEventListener( | document.addEventListener( | ||
"input", | "input", | ||
| Line 202: | Line 318: | ||
var t = e.target; | var t = e.target; | ||
if (!t || t.nodeType !== 1) return; | if (!t || t.nodeType !== 1) return; | ||
if (!t.matches || !t.matches( | if (!t.matches || !t.matches("input.sv-level-range[type='range']")) return; | ||
var card = closest(t, CARD_SEL); | var card = closest(t, CARD_SEL); | ||
| Line 212: | Line 328: | ||
); | ); | ||
// ------- | // Custom slider pointer drag (delegated) | ||
document.addEventListener( | |||
"pointerdown", | |||
function (e) { | |||
var slider = | |||
e.target && e.target.closest | |||
? e.target.closest(".sv-level-range--custom[data-sv-slider='1']") | |||
: null; | |||
if (!slider) return; | |||
var card = closest(slider, CARD_SEL); | |||
if (!card) return; | |||
e.preventDefault(); | |||
// focus for keyboard follow-up | |||
if (slider.focus) slider.focus(); | |||
_drag = { | |||
slider: slider, | |||
card: card, | |||
pointerId: e.pointerId, | |||
}; | |||
if (slider.setPointerCapture) { | |||
try { | |||
slider.setPointerCapture(e.pointerId); | |||
} catch (err) {} | |||
} | |||
var v = valueFromClientX(slider, e.clientX); | |||
applyLevelToCard(card, v); | |||
}, | |||
true | |||
); | |||
document.addEventListener( | |||
"pointermove", | |||
function (e) { | |||
if (!_drag) return; | |||
if (e.pointerId !== _drag.pointerId) return; | |||
e.preventDefault(); | |||
var v = valueFromClientX(_drag.slider, e.clientX); | |||
applyLevelToCard(_drag.card, v); | |||
}, | |||
true | |||
); | |||
function | function endDrag(e) { | ||
if (!_drag) return; | |||
if (e && e.pointerId != null && e.pointerId !== _drag.pointerId) return; | |||
if (_drag.slider && _drag.slider.releasePointerCapture) { | |||
try { | |||
_drag.slider.releasePointerCapture(_drag.pointerId); | |||
} catch (err) {} | |||
} | |||
_drag = null; | |||
} | |||
document.addEventListener("pointerup", endDrag, true); | |||
document.addEventListener("pointercancel", endDrag, true); | |||
// Custom slider keyboard support | |||
document.addEventListener( | |||
"keydown", | |||
function (e) { | |||
var el = document.activeElement; | |||
if (!el || !el.closest) return; | |||
var slider = el.closest(".sv-level-range--custom[data-sv-slider='1']"); | |||
if (!slider) return; | |||
var card = closest(slider, CARD_SEL); | |||
if (!card) return; | |||
var min = getCustomMin(slider); | |||
var max = getCustomMax(slider); | |||
if (max < min) max = min; | |||
var cur = getCustomValue(slider, getCardLevel(card, max)); | |||
var next = cur; | |||
if (e.key === "ArrowLeft" || e.key === "Left" || e.key === "ArrowDown") { | |||
next = cur - 1; | |||
} else if ( | |||
e.key === "ArrowRight" || | |||
e.key === "Right" || | |||
e.key === "ArrowUp" | |||
) { | |||
next = cur + 1; | |||
} else if (e.key === "Home") { | |||
next = min; | |||
} else if (e.key === "End") { | |||
next = max; | |||
} else if (e.key === "PageDown") { | |||
next = cur - 5; | |||
} else if (e.key === "PageUp") { | |||
next = cur + 5; | |||
} else { | |||
return; | |||
} | |||
e.preventDefault(); | |||
next = clampInt(next, min, max, cur); | |||
applyLevelToCard(card, next); | |||
}, | |||
true | |||
); | |||
/* ------------------------------------------------------------------ */ | |||
/* Popup (Phase 4.1: span toggles + legacy <details> support) */ | |||
/* ------------------------------------------------------------------ */ | |||
function hidePop(pop) { | |||
if (!pop) return; | |||
if (pop.classList) pop.classList.add(POPUP_HIDDEN_CLASS); | |||
pop.setAttribute("hidden", "hidden"); | |||
} | |||
function showPop(pop) { | |||
if (!pop) return; | |||
if (pop.classList) pop.classList.remove(POPUP_HIDDEN_CLASS); | |||
pop.removeAttribute("hidden"); | |||
} | |||
function getPopFromBtn(btn) { | |||
if (!btn) return null; | if (!btn) return null; | ||
var id = btn.getAttribute("aria-controls"); | var id = btn.getAttribute("aria-controls"); | ||
if (!id) return null; | if (!id) return null; | ||
var wrap = closest(btn, POPUP_WRAP_SEL); | |||
if (wrap) { | |||
var local = wrap.querySelector("#" + id); | |||
if (local) return local; | |||
} | |||
return document.getElementById(id); | return document.getElementById(id); | ||
} | } | ||
function | function isBtnExpanded(btn) { | ||
return | return btn && btn.getAttribute("aria-expanded") === "true"; | ||
} | } | ||
function | function setBtnExpanded(btn, expanded) { | ||
if (! | if (!btn) return; | ||
btn.setAttribute("aria-expanded", expanded ? "true" : "false"); | |||
} | |||
function closeBtnPopup(btn) { | |||
var pop = getPopFromBtn(btn); | |||
hidePop(pop); | |||
setBtnExpanded(btn, false); | |||
} | |||
function openBtnPopup(btn) { | |||
var pop = getPopFromBtn(btn); | |||
showPop(pop); | |||
setBtnExpanded(btn, true); | |||
} | } | ||
function | function closeAllBtnPopups(scope) { | ||
var root = scope || document; | var root = scope || document; | ||
var | var openBtns = root.querySelectorAll( | ||
for (var i = 0; i < | POPUP_BTN_SEL + "[aria-expanded='true']" | ||
); | |||
for (var i = 0; i < openBtns.length; i++) closeBtnPopup(openBtns[i]); | |||
if ( | } | ||
function closeOtherBtnPopupsWithinCard(btn) { | |||
var card = closest(btn, CARD_SEL); | |||
var scope = card || document; | |||
var openBtns = scope.querySelectorAll( | |||
POPUP_BTN_SEL + "[aria-expanded='true']" | |||
); | |||
for (var i = 0; i < openBtns.length; i++) { | |||
if (openBtns[i] !== btn) closeBtnPopup(openBtns[i]); | |||
} | } | ||
} | } | ||
function | function closeDetails(d) { | ||
if (d && d.open) d.open = false; | |||
var scope | } | ||
function closeAllDetails(scope) { | |||
var root = scope || document; | |||
var openOnes = root.querySelectorAll(POPUP_DETAILS_SEL + "[open]"); | |||
for (var i = 0; i < openOnes.length; i++) closeDetails(openOnes[i]); | |||
} | |||
function syncPopupAria(detailsEl) { | |||
var summary = detailsEl.querySelector("summary"); | |||
if (!summary) return; | |||
summary.setAttribute("aria-expanded", detailsEl.open ? "true" : "false"); | |||
var | var pop = detailsEl.querySelector(".sv-tip-pop, .sv-disclose-pop"); | ||
if (pop) { | |||
if (!pop.id) pop.id = "svgi-pop-" + Math.random().toString(36).slice(2); | |||
if ( | summary.setAttribute("aria-controls", pop.id); | ||
} | } | ||
} | } | ||
function | function normalizeBtnPopups(container) { | ||
var | var root = container || document; | ||
if (! | |||
var pops = root.querySelectorAll(POPUP_POP_SEL); | |||
for (var i = 0; i < pops.length; i++) { | |||
var p = pops[i]; | |||
if (hasClass(p, POPUP_HIDDEN_CLASS) && !p.hasAttribute("hidden")) { | |||
p.setAttribute("hidden", "hidden"); | |||
} | |||
} | |||
var | var btns = root.querySelectorAll(POPUP_BTN_SEL); | ||
for (var j = 0; j < btns.length; j++) { | |||
var b = btns[j]; | |||
var pop = getPopFromBtn(b); | |||
var expanded = pop | |||
? !hasClass(pop, POPUP_HIDDEN_CLASS) && !pop.hasAttribute("hidden") | |||
: false; | |||
setBtnExpanded(b, expanded); | |||
} | |||
} | } | ||
document.addEventListener( | document.addEventListener( | ||
"click", | "click", | ||
function (e) { | function (e) { | ||
var btn = e.target && e.target.closest ? e.target.closest( | var btn = | ||
if (btn) | e.target && e.target.closest ? e.target.closest(POPUP_BTN_SEL) : null; | ||
if (!btn) return; | |||
e.preventDefault(); | |||
if (isBtnExpanded(btn)) { | |||
closeBtnPopup(btn); | |||
return; | return; | ||
} | } | ||
closeOtherBtnPopupsWithinCard(btn); | |||
openBtnPopup(btn); | |||
}, | }, | ||
true | true | ||
); | ); | ||
document.addEventListener( | document.addEventListener( | ||
"keydown", | "keydown", | ||
function (e) { | function (e) { | ||
var | if (!(e.key === "Enter" || e.key === " " || e.key === "Spacebar")) | ||
if (! | return; | ||
var btn = | |||
document.activeElement && document.activeElement.closest | |||
? document.activeElement.closest(POPUP_BTN_SEL) | |||
: null; | |||
if (!btn) return; | |||
e.preventDefault(); | |||
if ( | if (isBtnExpanded(btn)) closeBtnPopup(btn); | ||
else { | |||
closeOtherBtnPopupsWithinCard(btn); | |||
openBtnPopup(btn); | |||
} | } | ||
}, | |||
true | |||
); | |||
document.addEventListener( | |||
"toggle", | |||
function (e) { | |||
var d = e.target; | |||
if (!d || d.nodeType !== 1) return; | |||
if (!d.matches || !d.matches(POPUP_DETAILS_SEL)) return; | |||
var | if (d.open) { | ||
var card = closest(d, CARD_SEL); | |||
var scope = card || document; | |||
var openOnes = scope.querySelectorAll(POPUP_DETAILS_SEL + "[open]"); | |||
for (var i = 0; i < openOnes.length; i++) { | |||
if (openOnes[i] !== d) closeDetails(openOnes[i]); | |||
} | |||
} | } | ||
syncPopupAria(d); | |||
}, | |||
true | |||
); | |||
document.addEventListener( | |||
"click", | |||
function (e) { | |||
var t = e.target; | |||
if (!t) return; | |||
if (closest(t, POPUP_WRAP_SEL)) return; | |||
closeAllBtnPopups(document); | |||
closeAllDetails(document); | |||
}, | }, | ||
true | true | ||
); | ); | ||
/ | document.addEventListener( | ||
/ | "keydown", | ||
/ | function (e) { | ||
if (e.key !== "Escape") return; | |||
closeAllBtnPopups(document); | |||
closeAllDetails(document); | |||
}, | |||
true | |||
); | |||
/* ------------------------------------------------------------------ */ | |||
/* Tabs (Phase 4.1: key-based, uses .sv-hidden + [hidden]) */ | |||
/* ------------------------------------------------------------------ */ | |||
function getTabs(root) { | function getTabs(root) { | ||
| Line 318: | Line 652: | ||
function getPanels(root) { | function getPanels(root) { | ||
return root.querySelectorAll(PANEL_SEL); | return root.querySelectorAll(PANEL_SEL); | ||
} | |||
function getPanelByKey(root, key) { | |||
var panels = getPanels(root); | |||
for (var i = 0; i < panels.length; i++) { | |||
if (panels[i].getAttribute("data-panel") === key) return panels[i]; | |||
} | |||
return null; | |||
} | } | ||
| Line 324: | Line 666: | ||
if (!uid) { | if (!uid) { | ||
_tabsUidCounter++; | _tabsUidCounter++; | ||
uid = (root.getAttribute("data-tabs-root") || "svgi-tabs") + "-" + _tabsUidCounter; | uid = | ||
(root.getAttribute("data-tabs-root") || "svgi-tabs") + | |||
"-" + | |||
_tabsUidCounter; | |||
root.setAttribute("data-tabs-uid", uid); | root.setAttribute("data-tabs-uid", uid); | ||
} | } | ||
var tabs = getTabs(root); | var tabs = getTabs(root); | ||
for (var i = 0; i < tabs.length; i++) { | |||
for (var i = 0; i < | |||
var tab = tabs[i]; | var tab = tabs[i]; | ||
var panel = | var key = tab.getAttribute("data-tab") || String(i + 1); | ||
var panel = getPanelByKey(root, key); | |||
var tabId = uid + "-tab-" + key; | |||
tab.id = tabId; | tab.id = tabId; | ||
panel.id = panelId; | if (panel) { | ||
var panelId = uid + "-panel-" + key; | |||
panel.id = panelId; | |||
tab.setAttribute("aria-controls", panelId); | |||
panel.setAttribute("aria-labelledby", tabId); | |||
} | |||
} | } | ||
} | |||
function showPanel(panel) { | |||
if (!panel) return; | |||
panel.setAttribute("data-active", "1"); | |||
if (panel.classList) panel.classList.remove("sv-hidden"); | |||
panel.removeAttribute("hidden"); | |||
} | |||
function hidePanel(panel) { | |||
if (!panel) return; | |||
panel.setAttribute("data-active", "0"); | |||
if (panel.classList) panel.classList.add("sv-hidden"); | |||
panel.setAttribute("hidden", "hidden"); | |||
} | } | ||
| Line 351: | Line 709: | ||
var tabs = getTabs(root); | var tabs = getTabs(root); | ||
var | var key = btn.getAttribute("data-tab") || ""; | ||
for (var i = 0; i < tabs.length; i++) { | for (var i = 0; i < tabs.length; i++) { | ||
| Line 358: | Line 716: | ||
t.setAttribute("aria-selected", active ? "true" : "false"); | t.setAttribute("aria-selected", active ? "true" : "false"); | ||
t.setAttribute("tabindex", active ? "0" : "-1"); | t.setAttribute("tabindex", active ? "0" : "-1"); | ||
if (t.classList | if (t.classList) t.classList.toggle("sv-tab--active", active); | ||
} | } | ||
var | var panels = getPanels(root); | ||
for (var j = 0; j < panels.length; j++) { | for (var j = 0; j < panels.length; j++) { | ||
var p = panels[j]; | var p = panels[j]; | ||
var isTarget = p. | var isTarget = p.getAttribute("data-panel") === key; | ||
if (isTarget) showPanel(p); | |||
if (isTarget) p | else hidePanel(p); | ||
else p | |||
} | } | ||
root.setAttribute("data-active-tab", | root.setAttribute("data-active-tab", key); | ||
} | } | ||
| Line 385: | Line 739: | ||
function indexOfTab(tabs, btn) { | function indexOfTab(tabs, btn) { | ||
for (var i = 0; i < tabs.length; i++) if (tabs[i] === btn) return i; | for (var i = 0; i < tabs.length; i++) { | ||
if (tabs[i] === btn) return i; | |||
} | |||
return -1; | return -1; | ||
} | } | ||
| Line 421: | Line 777: | ||
} | } | ||
document.addEventListener("click", function (e) { | document.addEventListener("click", function (e) { | ||
var btn = e.target && e.target.closest ? e.target.closest(TAB_SEL) : null; | var btn = e.target && e.target.closest ? e.target.closest(TAB_SEL) : null; | ||
| Line 432: | Line 787: | ||
}); | }); | ||
document.addEventListener("keydown", function (e) { | document.addEventListener("keydown", function (e) { | ||
var btn = | var btn = | ||
| Line 476: | Line 830: | ||
}); | }); | ||
/ | /* ------------------------------------------------------------------ */ | ||
// | /* Init */ | ||
/ | /* ------------------------------------------------------------------ */ | ||
function initAll(root) { | function initAll(root) { | ||
initLevels(root); | initLevels(root); | ||
initTabs(root); | initTabs(root); | ||
var container = root || document; | |||
normalizeBtnPopups(container); | |||
var ds = container.querySelectorAll(POPUP_DETAILS_SEL); | |||
for (var i = 0; i < ds.length; i++) syncPopupAria(ds[i]); | |||
} | } | ||