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 |
||
| Line 1: | Line 1: | ||
/* Common.js — Spirit Vale Wiki | /* Common.js — Spirit Vale Wiki | ||
Phase 4.1 | Phase 4.1 — Core client scripts | ||
Included (standalone modules): | Included (standalone modules): | ||
1 | 1) Level Selector / Descriptions (data-series) | ||
2) Universal Popups (tips, discloses, definitions) | |||
3) Tabs | |||
*/ | */ | ||
(function () { | (function () { | ||
| Line 382: | Line 16: | ||
var COMMON = (SV.common = SV.common || {}); | var COMMON = (SV.common = SV.common || {}); | ||
// | // Versioned init guard (allows safe updates without stale blocks) | ||
var LEVELS_VERSION = 2; | var LEVELS_VERSION = 2; | ||
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; | ||
| Line 390: | Line 23: | ||
var CARD_SEL = ".sv-gi-card, .sv-skill-card, .sv-passive-card"; | var CARD_SEL = ".sv-gi-card, .sv-skill-card, .sv-passive-card"; | ||
// | // Accept the native range input and the custom slider variants. | ||
var LEVEL_RANGE_SEL = | var LEVEL_RANGE_SEL = | ||
"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']"; | ||
| Line 461: | Line 94: | ||
if (!el || !el.getAttribute) return false; | if (!el || !el.getAttribute) return false; | ||
if (el.getAttribute("data-sv-slider") === "1") return true; | if (el.getAttribute("data-sv-slider") === "1") return true; | ||
return !!(el.classList && el.classList.contains("sv-level-range--custom")); | return !!(el.classList && el.classList.contains("sv-level-range--custom")); | ||
} | } | ||
function parseMaxFromUi(card) { | function parseMaxFromUi(card) { | ||
var ui = card && card.querySelector ? card.querySelector(".sv-level-ui") : null; | var ui = card && card.querySelector ? card.querySelector(".sv-level-ui") : null; | ||
if (!ui) return null; | if (!ui) return null; | ||
| Line 646: | Line 277: | ||
} | } | ||
} else if (slider && isCustomSlider(slider)) { | } else if (slider && isCustomSlider(slider)) { | ||
setCustomBounds(slider, 1, maxLevel); | setCustomBounds(slider, 1, maxLevel); | ||
setCustomValue(slider, level); | setCustomValue(slider, level); | ||
| Line 663: | Line 293: | ||
var card = cards[i]; | var card = cards[i]; | ||
var stamped = clampInt(card.getAttribute(LEVEL_INIT_ATTR), 0, 999, 0); | var stamped = clampInt(card.getAttribute(LEVEL_INIT_ATTR), 0, 999, 0); | ||
if (stamped >= LEVELS_VERSION) continue; | if (stamped >= LEVELS_VERSION) continue; | ||
| Line 771: | Line 400: | ||
if (e.key === "ArrowLeft" || e.key === "Left" || e.key === "ArrowDown") { | if (e.key === "ArrowLeft" || e.key === "Left" || e.key === "ArrowDown") { | ||
next = cur - 1; | next = cur - 1; | ||
} else if ( | } else if (e.key === "ArrowRight" || e.key === "Right" || e.key === "ArrowUp") { | ||
next = cur + 1; | next = cur + 1; | ||
} else if (e.key === "Home") { | } else if (e.key === "Home") { | ||
| Line 800: | Line 425: | ||
} | } | ||
COMMON.initAllLevels = initAllLevels; | COMMON.initAllLevels = initAllLevels; | ||
| Line 816: | Line 440: | ||
initAllLevels(document); | initAllLevels(document); | ||
} | } | ||
})(); | |||
/* ====================================================================== */ | |||
/* LEGACY POPUPS (disabled) */ | |||
/* */ | |||
/* This section is intentionally isolated so it can be deleted in one cut. */ | |||
/* To temporarily re-enable legacy behavior, set: */ | |||
/* SV.common.enableLegacyPopups = true */ | |||
/* ====================================================================== */ | |||
(function () { | |||
var SV = (window.SV = window.SV || {}); | |||
var COMMON = (SV.common = SV.common || {}); | |||
if (!COMMON.enableLegacyPopups) return; | |||
// Place any legacy popup code blocks here verbatim if needed during transition. | |||
})(); | })(); | ||
(function () { | (function () { | ||
/* ================================================================== */ | /* ================================================================== */ | ||
/* MODULE: Universal Popups | /* MODULE: Universal Popups */ | ||
/* ================================================================== */ | /* ================================================================== */ | ||
| Line 829: | Line 467: | ||
var COMMON = (SV.common = SV.common || {}); | var COMMON = (SV.common = SV.common || {}); | ||
var | var UPOP_VERSION = 31; // Universal Popups v3.1 | ||
if (typeof COMMON. | if (typeof COMMON.universalPopupsInit === "number" && COMMON.universalPopupsInit >= UPOP_VERSION) return; | ||
COMMON. | COMMON.universalPopupsInit = UPOP_VERSION; | ||
try { document.documentElement.classList.add("sv-uipop-v3"); } catch (e) {} | try { document.documentElement.classList.add("sv-uipop-v3"); } catch (e) {} | ||
| Line 842: | Line 480: | ||
var DISCLOSE_BTN_SEL = ".sv-disclose-btn[data-sv-toggle='1'], details.sv-disclose > summary"; | var DISCLOSE_BTN_SEL = ".sv-disclose-btn[data-sv-toggle='1'], details.sv-disclose > summary"; | ||
var | var WRAP_SEL = ".sv-tip, .sv-disclose"; | ||
var | var POP_SEL = ".sv-tip-pop, .sv-disclose-pop"; | ||
var HOVER_CAPABLE = false; | var HOVER_CAPABLE = false; | ||
| Line 856: | Line 493: | ||
open: false, | open: false, | ||
pinned: false, | pinned: false, | ||
trigger: null, | trigger: null, | ||
hideTimer: 0, | hideTimer: 0, | ||
| Line 883: | Line 518: | ||
function safeIdSelector(id) { | function safeIdSelector(id) { | ||
return "#" + String(id).replace(/([ !"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, "\\$1"); | return "#" + String(id).replace(/([ !"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~])/g, "\\$1"); | ||
} | |||
function isInsidePopup(target) { | |||
return !!(popEl && (target === popEl || (popEl.contains && popEl.contains(target)))); | |||
} | |||
function isSummaryTrigger(trigger) { | |||
return !!(trigger && trigger.tagName === "SUMMARY"); | |||
} | } | ||
| Line 892: | Line 535: | ||
el.setAttribute("role", "dialog"); | el.setAttribute("role", "dialog"); | ||
el.setAttribute("aria-hidden", "true"); | el.setAttribute("aria-hidden", "true"); | ||
document.body.appendChild(el); | |||
el.addEventListener("mouseenter", function () { | |||
if (!HOVER_CAPABLE) return; | |||
if (!state.open || state.pinned) return; | |||
clearTimeout(state.hideTimer); | |||
state.hideTimer = 0; | |||
}); | |||
el.addEventListener("mouseleave", function () { | |||
if (!HOVER_CAPABLE) return; | |||
if (!state.open || state.pinned) return; | |||
clearTimeout(state.hideTimer); | |||
state.hideTimer = setTimeout(function () { | |||
if (state.open && !state.pinned) hidePopup(); | |||
}, 80); | |||
}); | |||
popEl = el; | popEl = el; | ||
return popEl; | return popEl; | ||
| Line 912: | Line 571: | ||
function unhideTree(root) { | function unhideTree(root) { | ||
if (!root || root.nodeType !== 1) return; | if (!root || root.nodeType !== 1) return; | ||
if (root.hasAttribute && root.hasAttribute("hidden")) root.removeAttribute("hidden"); | if (root.hasAttribute && root.hasAttribute("hidden")) root.removeAttribute("hidden"); | ||
if (root.classList) root.classList.remove("sv-hidden"); | if (root.classList) root.classList.remove("sv-hidden"); | ||
if (root.style && root.style.display === "none") root.style.display = ""; | if (root.style && root.style.display === "none") root.style.display = ""; | ||
var kids = root.children || []; | var kids = root.children || []; | ||
for (var i = 0; i < kids.length; i++) unhideTree(kids[i]); | for (var i = 0; i < kids.length; i++) unhideTree(kids[i]); | ||
| Line 942: | Line 603: | ||
if (t == null) return ""; | if (t == null) return ""; | ||
return String(t).trim(); | return String(t).trim(); | ||
} | } | ||
| Line 956: | Line 609: | ||
if (trigger.tagName === "SUMMARY") { | if (trigger.tagName === "SUMMARY") { | ||
var det = closest(trigger, | var det = closest(trigger, "details.sv-tip, details.sv-disclose"); | ||
if (!det) return null; | if (!det) return null; | ||
var | var pop0 = det.querySelector(POP_SEL); | ||
if (! | if (!pop0) return null; | ||
return ( | |||
pop0.querySelector(".sv-tip-pop-body") || | |||
pop0.querySelector(".sv-disclose-pop-body") || | |||
pop0.querySelector(".sv-disclose-list") || | |||
pop0 | |||
); | |||
} | } | ||
var id = trigger.getAttribute ? trigger.getAttribute("aria-controls") : ""; | var id = trigger.getAttribute ? trigger.getAttribute("aria-controls") : ""; | ||
if (id) { | if (id) { | ||
var wrap = closest(trigger, | var wrap = closest(trigger, WRAP_SEL); | ||
if (wrap) { | if (wrap) { | ||
try { | try { | ||
var local = wrap.querySelector(safeIdSelector(id)); | var local = wrap.querySelector(safeIdSelector(id)); | ||
if (local) { | if (local) { | ||
return ( | |||
local.querySelector(".sv-tip-pop-body") || | local.querySelector(".sv-tip-pop-body") || | ||
local.querySelector(".sv-disclose-pop-body") || | local.querySelector(".sv-disclose-pop-body") || | ||
local.querySelector(".sv-disclose-list") | local.querySelector(".sv-disclose-list") || | ||
local | |||
); | |||
} | } | ||
} catch (e) {} | } catch (e) {} | ||
| Line 985: | Line 640: | ||
var global = document.getElementById(id); | var global = document.getElementById(id); | ||
if (global) { | if (global) { | ||
return ( | |||
global.querySelector(".sv-tip-pop-body") || | global.querySelector(".sv-tip-pop-body") || | ||
global.querySelector(".sv-disclose-pop-body") || | global.querySelector(".sv-disclose-pop-body") || | ||
global.querySelector(".sv-disclose-list") | global.querySelector(".sv-disclose-list") || | ||
global | |||
); | |||
} | } | ||
} | } | ||
var w = closest(trigger, | var w = closest(trigger, WRAP_SEL); | ||
if (w) { | if (w) { | ||
var | var pop = w.querySelector(POP_SEL); | ||
if ( | if (pop) { | ||
return ( | |||
pop.querySelector(".sv-tip-pop-body") || | |||
pop.querySelector(".sv-disclose-pop-body") || | |||
pop.querySelector(".sv-disclose-list") || | |||
pop | |||
); | |||
} | } | ||
} | } | ||
| Line 1,010: | Line 667: | ||
function guessTitle(trigger, sourceNode) { | function guessTitle(trigger, sourceNode) { | ||
var pop = null; | var pop = null; | ||
if (sourceNode && sourceNode.nodeType === 1) pop = closest(sourceNode, | if (sourceNode && sourceNode.nodeType === 1) pop = closest(sourceNode, POP_SEL); | ||
if (pop) { | if (pop) { | ||
var t = pop.querySelector(".sv-tip-pop-title") || pop.querySelector(".sv-disclose-pop-title"); | var t = pop.querySelector(".sv-tip-pop-title") || pop.querySelector(".sv-disclose-pop-title"); | ||
if (t && t.textContent) return String(t.textContent).trim(); | if (t && t.textContent) return String(t.textContent).trim(); | ||
} | } | ||
var aria = trigger && trigger.getAttribute ? trigger.getAttribute("aria-label") : ""; | var aria = trigger && trigger.getAttribute ? trigger.getAttribute("aria-label") : ""; | ||
if (aria) return String(aria).trim(); | if (aria) return String(aria).trim(); | ||
var txt = trigger && trigger.textContent ? String(trigger.textContent).trim() : ""; | var txt = trigger && trigger.textContent ? String(trigger.textContent).trim() : ""; | ||
if (txt) return txt.replace(/\s+/g, " ").trim(); | if (txt) return txt.replace(/\s+/g, " ").trim(); | ||
return "Info"; | return "Info"; | ||
} | } | ||
function | function buildHeaderLink(linkTitle, displayText) { | ||
if (!linkTitle) return null; | if (!linkTitle) return null; | ||
| Line 1,032: | Line 692: | ||
else a.href = String(linkTitle); | else a.href = String(linkTitle); | ||
a.addEventListener("click", function (e) { e.stopPropagation(); }); | a.addEventListener("click", function (e) { e.stopPropagation(); }); | ||
| Line 1,055: | Line 714: | ||
var head = document.createElement("div"); | var head = document.createElement("div"); | ||
head.className = "sv-uipop-head" + (opts. | head.className = "sv-uipop-head" + (opts.pinned ? " sv-uipop-head--clickable" : ""); | ||
var title = document.createElement("div"); | var title = document.createElement("div"); | ||
| Line 1,080: | Line 739: | ||
el.appendChild(body); | el.appendChild(body); | ||
if (opts. | if (opts.pinned) { | ||
head.addEventListener("click", function (e) { | head.addEventListener("click", function (e) { | ||
e.preventDefault(); | e.preventDefault(); | ||
hidePopup( | hidePopup(); | ||
}, { once: true }); | }, { once: true }); | ||
} | } | ||
fireContentHook(el); | fireContentHook(el); | ||
} | } | ||
| Line 1,155: | Line 813: | ||
state.open = false; | state.open = false; | ||
state.pinned = false; | state.pinned = false; | ||
state.trigger = null; | state.trigger = null; | ||
state.anchorKind = "trigger"; | state.anchorKind = "trigger"; | ||
setHidden(ensurePopEl(), true); | |||
} | } | ||
| Line 1,167: | Line 822: | ||
if (!trigger) return; | if (!trigger) return; | ||
if (state.open && state.trigger && state.trigger !== trigger) hidePopup( | if (state.open && state.trigger && state.trigger !== trigger) hidePopup(); | ||
state.open = true; | state.open = true; | ||
state.pinned = !!opts.pinned; | state.pinned = !!opts.pinned; | ||
state.trigger = trigger; | state.trigger = trigger; | ||
| Line 1,187: | Line 840: | ||
var defEl = closest(t, DEF_SEL); | var defEl = closest(t, DEF_SEL); | ||
if (defEl) return defEl; | if (defEl && defEl.getAttribute && defEl.getAttribute(DEF_TIP_ATTR) != null) return defEl; | ||
var tip = closest(t, TIP_BTN_SEL); | var tip = closest(t, TIP_BTN_SEL); | ||
| Line 1,198: | Line 851: | ||
} | } | ||
function | function resolveOpts(trigger) { | ||
var isDef = trigger && trigger.matches && trigger.matches(DEF_SEL); | var isDef = trigger && trigger.matches && trigger.matches(DEF_SEL); | ||
var isTip = trigger && trigger.matches && trigger.matches(TIP_BTN_SEL); | var isTip = trigger && trigger.matches && trigger.matches(TIP_BTN_SEL); | ||
| Line 1,215: | Line 868: | ||
if (isDisclose) mode = "click"; | if (isDisclose) mode = "click"; | ||
else if (isTip) mode = "hover"; | else if (isTip) mode = "hover"; | ||
else if (isDef) | else if (isDef) mode = getDefLinkTitle(trigger) ? "click" : "hover"; | ||
} | } | ||
| Line 1,236: | Line 886: | ||
return { | return { | ||
pinned: (mode === "click"), | |||
size: size, | size: size, | ||
title: titleText, | title: titleText, | ||
titleLink: linkTitle ? | titleLink: linkTitle ? buildHeaderLink(linkTitle, titleText) : null, | ||
bodyText: tipText, | bodyText: tipText, | ||
bodyPre: true | bodyPre: true, | ||
bodyNode: null | |||
}; | }; | ||
} | } | ||
var | var source = getContentNodeFromTrigger(trigger); | ||
if (isBlankContent( | if (isBlankContent(source)) return null; | ||
return { | return { | ||
pinned: (mode === "click"), | |||
size: size, | size: size, | ||
title: guessTitle(trigger, source), | |||
title: guessTitle(trigger, | |||
titleLink: null, | titleLink: null, | ||
bodyNode: | bodyNode: source, | ||
bodyPre: false | bodyPre: false, | ||
bodyText: null | |||
}; | }; | ||
} | } | ||
| Line 1,262: | Line 912: | ||
window.addEventListener("pointerdown", function (e) { | window.addEventListener("pointerdown", function (e) { | ||
if (!e) return; | if (!e) return; | ||
if (isInsidePopup(e.target)) return; | if (isInsidePopup(e.target)) return; | ||
| Line 1,267: | Line 918: | ||
if (!trigger) return; | if (!trigger) return; | ||
var opts = | var opts = resolveOpts(trigger); | ||
if (!opts) return; | if (!opts) return; | ||
| Line 1,276: | Line 927: | ||
state.anchorY = (typeof e.clientY === "number") ? e.clientY : 0; | state.anchorY = (typeof e.clientY === "number") ? e.clientY : 0; | ||
if (opts. | if (HOVER_CAPABLE && !opts.pinned && (e.pointerType === "mouse" || !e.pointerType)) { | ||
e.stopPropagation(); | e.stopPropagation(); | ||
return; | return; | ||
| Line 1,285: | Line 936: | ||
if (state.open && state.pinned && state.trigger === trigger) { | if (state.open && state.pinned && state.trigger === trigger) { | ||
hidePopup( | hidePopup(); | ||
return; | return; | ||
} | } | ||
| Line 1,298: | Line 949: | ||
var trigger = findTrigger(e.target); | var trigger = findTrigger(e.target); | ||
if (!trigger) { | if (!trigger) { | ||
if (state.open && !state.pinned) hidePopup( | if (state.open && !state.pinned) hidePopup(); | ||
return; | return; | ||
} | } | ||
var opts = | var opts = resolveOpts(trigger); | ||
if (!opts || opts. | if (!opts || opts.pinned) return; | ||
state.anchorKind = "trigger"; | state.anchorKind = "trigger"; | ||
e.stopPropagation(); | e.stopPropagation(); | ||
clearTimeout(state.hideTimer); | clearTimeout(state.hideTimer); | ||
| Line 1,321: | Line 973: | ||
e.stopPropagation(); | e.stopPropagation(); | ||
clearTimeout(state.hideTimer); | clearTimeout(state.hideTimer); | ||
state.hideTimer = setTimeout(function () { | state.hideTimer = setTimeout(function () { | ||
if (state.open && !state.pinned) hidePopup( | if (state.open && !state.pinned) hidePopup(); | ||
}, | }, 80); | ||
}, true); | }, true); | ||
| Line 1,338: | Line 991: | ||
if (state.pinned || !HOVER_CAPABLE) { | if (state.pinned || !HOVER_CAPABLE) { | ||
hidePopup( | hidePopup(); | ||
e.stopPropagation(); | e.stopPropagation(); | ||
} | } | ||
| Line 1,346: | Line 999: | ||
if (e.key !== "Escape") return; | if (e.key !== "Escape") return; | ||
if (state.open) { | if (state.open) { | ||
hidePopup( | hidePopup(); | ||
e.stopPropagation(); | e.stopPropagation(); | ||
} | } | ||
| Line 1,377: | Line 1,030: | ||
} else { | } else { | ||
stripDefTitles(document); | stripDefTitles(document); | ||
} | |||
})(); | |||
(function () { | |||
/* ================================================================== */ | |||
/* MODULE: Tabs */ | |||
/* ================================================================== */ | |||
var SV = (window.SV = window.SV || {}); | |||
var COMMON = (SV.common = SV.common || {}); | |||
if (COMMON.tabsInit) return; | |||
COMMON.tabsInit = 1; | |||
var TABS_ROOT_SEL = ".sv-tabs[data-tabs='1']"; | |||
var TAB_SEL = ".sv-tab"; | |||
var PANEL_SEL = ".sv-tabpanel"; | |||
var _tabsUidCounter = 0; | |||
function closest(el, sel) { | |||
if (!el || el.nodeType !== 1) return null; | |||
if (el.closest) return el.closest(sel); | |||
while (el && el.nodeType === 1) { | |||
if (el.matches && el.matches(sel)) return el; | |||
el = el.parentElement; | |||
} | |||
return null; | |||
} | |||
function getTabs(root) { | |||
return root.querySelectorAll(TAB_SEL); | |||
} | |||
function getPanels(root) { | |||
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; | |||
} | |||
function ensureUniqueTabIds(root) { | |||
var uid = root.getAttribute("data-tabs-uid"); | |||
if (!uid) { | |||
_tabsUidCounter++; | |||
uid = (root.getAttribute("data-tabs-root") || "sv-tabs") + "-" + _tabsUidCounter; | |||
root.setAttribute("data-tabs-uid", uid); | |||
} | |||
var tabs = getTabs(root); | |||
for (var i = 0; i < tabs.length; i++) { | |||
var tab = tabs[i]; | |||
var key = tab.getAttribute("data-tab") || String(i + 1); | |||
var panel = getPanelByKey(root, key); | |||
var tabId = uid + "-tab-" + key; | |||
tab.id = tabId; | |||
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"); | |||
} | |||
function activateTab(root, btn) { | |||
if (!root || !btn) return; | |||
var tabs = getTabs(root); | |||
var key = btn.getAttribute("data-tab") || ""; | |||
for (var i = 0; i < tabs.length; i++) { | |||
var t = tabs[i]; | |||
var active = t === btn; | |||
t.setAttribute("aria-selected", active ? "true" : "false"); | |||
t.setAttribute("tabindex", active ? "0" : "-1"); | |||
if (t.classList) t.classList.toggle("sv-tab--active", active); | |||
} | |||
var panels = getPanels(root); | |||
for (var j = 0; j < panels.length; j++) { | |||
var p = panels[j]; | |||
var isTarget = p.getAttribute("data-panel") === key; | |||
if (isTarget) showPanel(p); | |||
else hidePanel(p); | |||
} | |||
root.setAttribute("data-active-tab", key); | |||
} | |||
function focusTab(tabs, idx) { | |||
if (idx < 0) idx = 0; | |||
if (idx >= tabs.length) idx = tabs.length - 1; | |||
var t = tabs[idx]; | |||
if (t && t.focus) t.focus(); | |||
return t; | |||
} | |||
function indexOfTab(tabs, btn) { | |||
for (var i = 0; i < tabs.length; i++) { | |||
if (tabs[i] === btn) return i; | |||
} | |||
return -1; | |||
} | |||
function normalizeTabsRoot(root) { | |||
if (!root) return; | |||
if (root.getAttribute("data-gi-tabs-init") === "1") return; | |||
ensureUniqueTabIds(root); | |||
var tabs = getTabs(root); | |||
var panels = getPanels(root); | |||
if (!tabs.length || !panels.length) { | |||
root.setAttribute("data-gi-tabs-init", "1"); | |||
return; | |||
} | |||
var activeBtn = null; | |||
for (var i = 0; i < tabs.length; i++) { | |||
if (tabs[i].getAttribute("aria-selected") === "true") { | |||
activeBtn = tabs[i]; | |||
break; | |||
} | |||
} | |||
if (!activeBtn) activeBtn = tabs[0]; | |||
activateTab(root, activeBtn); | |||
root.setAttribute("data-gi-tabs-init", "1"); | |||
} | |||
function initTabs(root) { | |||
var container = root || document; | |||
var roots = container.querySelectorAll(TABS_ROOT_SEL); | |||
for (var i = 0; i < roots.length; i++) normalizeTabsRoot(roots[i]); | |||
} | |||
document.addEventListener( | |||
"click", | |||
function (e) { | |||
var btn = e.target && e.target.closest ? e.target.closest(TAB_SEL) : null; | |||
if (!btn) return; | |||
var root = closest(btn, ".sv-tabs"); | |||
if (!root || root.getAttribute("data-tabs") !== "1") return; | |||
e.preventDefault(); | |||
activateTab(root, btn); | |||
}, | |||
true | |||
); | |||
document.addEventListener( | |||
"keydown", | |||
function (e) { | |||
var btn = | |||
document.activeElement && document.activeElement.closest | |||
? document.activeElement.closest(TAB_SEL) | |||
: null; | |||
if (!btn) return; | |||
var root = closest(btn, ".sv-tabs"); | |||
if (!root || root.getAttribute("data-tabs") !== "1") return; | |||
var tabs = getTabs(root); | |||
if (!tabs || !tabs.length) return; | |||
var idx = indexOfTab(tabs, btn); | |||
if (idx < 0) return; | |||
if (e.key === "ArrowLeft" || e.key === "Left") { | |||
e.preventDefault(); | |||
activateTab(root, focusTab(tabs, idx - 1)); | |||
return; | |||
} | |||
if (e.key === "ArrowRight" || e.key === "Right") { | |||
e.preventDefault(); | |||
activateTab(root, focusTab(tabs, idx + 1)); | |||
return; | |||
} | |||
if (e.key === "Home") { | |||
e.preventDefault(); | |||
activateTab(root, focusTab(tabs, 0)); | |||
return; | |||
} | |||
if (e.key === "End") { | |||
e.preventDefault(); | |||
activateTab(root, focusTab(tabs, tabs.length - 1)); | |||
return; | |||
} | |||
if (e.key === "Enter" || e.key === " ") { | |||
e.preventDefault(); | |||
activateTab(root, btn); | |||
return; | |||
} | |||
}, | |||
true | |||
); | |||
function initAllTabs(root) { | |||
initTabs(root); | |||
} | |||
if (window.mw && mw.hook) { | |||
mw.hook("wikipage.content").add(function ($content) { | |||
initAllTabs($content && $content[0]); | |||
}); | |||
} | |||
if (document.readyState === "loading") { | |||
document.addEventListener("DOMContentLoaded", function () { | |||
initAllTabs(document); | |||
}); | |||
} else { | |||
initAllTabs(document); | |||
} | } | ||
})(); | })(); | ||