MediaWiki:Common.js: Difference between revisions
Appearance
No edit summary |
No edit summary |
||
| Line 127: | Line 127: | ||
function initExclusiveCollapsibles() { | function initExclusiveCollapsibles() { | ||
const outerKey = 'lootcrate'; | const outerKey = 'lootcrate'; | ||
const | const outerSelector = '#mw-customcollapsible-' + outerKey; | ||
const innerSelector = outerSelector + ' [class*="mw-customtoggle-"]'; | |||
const | |||
let suppress = false; | let suppress = false; | ||
$( | // Event delegation: works regardless of when elements are added, | ||
// and re-discovers items inside the outer dropdown each click. | |||
$(document).on('click', innerSelector, function () { | |||
if (suppress) return; | if (suppress) return; | ||
const match = this.className.match(/mw-customtoggle-(\S+)/); | const match = this.className.match(/mw-customtoggle-(\S+)/); | ||
| Line 148: | Line 139: | ||
const clickedKey = match[1]; | const clickedKey = match[1]; | ||
setTimeout(() => { | setTimeout(() => { | ||
suppress = true; | suppress = true; | ||
// Close other item images | // Close any other open item images | ||
$(innerSelector).each(function () { | |||
if ( | const m = this.className.match(/mw-customtoggle-(\S+)/); | ||
const $ | if (!m || m[1] === clickedKey) return; | ||
if ($ | const $img = $('#mw-customcollapsible-' + m[1]); | ||
$( | if ($img.length && !$img.hasClass('mw-collapsed')) { | ||
$(this).trigger('click'); | |||
} | } | ||
}); | }); | ||
// Update | // Update outer dropdown label to show the picked item's name | ||
const pickedName = $('.mw-customtoggle-' + clickedKey).first().text(); | const pickedName = $('.mw-customtoggle-' + clickedKey).first().text(); | ||
$('.mw-customtoggle-' + outerKey + ' span:first-child').text(pickedName); | $('.mw-customtoggle-' + outerKey + ' span:first-child').text(pickedName); | ||
// Auto-close the outer dropdown after a pick | // Auto-close the outer dropdown after a pick | ||
const $ | const $outerCol = $(outerSelector); | ||
if ($ | if ($outerCol.length && !$outerCol.hasClass('mw-collapsed')) { | ||
$('.mw-customtoggle-' + outerKey).first().trigger('click'); | $('.mw-customtoggle-' + outerKey).first().trigger('click'); | ||
} | } | ||
Revision as of 13:35, 27 May 2026
/**
* MediaWiki:Common.js
* Once Human Guide
*/
mw.loader.using(['mediawiki.util']).then(function () {
$(function () {
/* ============================================================
* TAB SYSTEM
* Wires up .mw-tab-buttons groups so each .mw-tab-btn shows its
* matching .mw-tab-content sibling.
* ============================================================ */
function initTabs() {
document.querySelectorAll('.mw-tab-buttons').forEach(group => {
const buttons = group.querySelectorAll('.mw-tab-btn');
buttons.forEach(btn => {
btn.addEventListener('click', function () {
const tabId = this.getAttribute('data-tab');
// Deactivate buttons in this group only
buttons.forEach(b => b.classList.remove('active'));
this.classList.add('active');
// Hide only the direct-child tab panels
const container = group.parentElement;
container.querySelectorAll(':scope > .mw-tab-content').forEach(c => {
c.classList.remove('active');
});
// Show the target panel and init any dropdowns inside it
const target = container.querySelector('#' + tabId);
if (target) {
target.classList.add('active');
initDropdowns(target);
}
});
});
});
// Auto-click the first tab in each group on load
document.querySelectorAll('.mw-tab-buttons').forEach(group => {
const first = group.querySelector('.mw-tab-btn');
if (first) first.click();
});
}
/* ============================================================
* JSON-DRIVEN DROPDOWN UI
* Renders .mw-dropdown-ui[data-options] containers as a
* label button, sorted list, and detail output panel.
* Supports both the new "lines" array format and the old
* semicolon-separated "content" string format.
* ============================================================ */
function initDropdowns(scope) {
scope.querySelectorAll('.mw-dropdown-ui[data-options]').forEach(container => {
if (container.dataset.rendered === 'true') return;
let data;
try {
data = JSON.parse(container.dataset.options);
} catch (e) {
console.error('Bad dropdown JSON:', e);
return;
}
container.innerHTML = '';
const btn = document.createElement('div');
btn.className = 'mod-dropdown-btn';
btn.textContent = container.dataset.label || 'Select One';
const list = document.createElement('div');
list.className = 'mod-dropdown-list';
const output = document.createElement('div');
output.className = 'mw-dropdown-output';
output.innerHTML = '<div class="mw-ui-placeholder">Select something to see details</div>';
Object.values(data)
.sort((a, b) => a.label.localeCompare(b.label))
.forEach(item => {
const option = document.createElement('div');
option.className = 'mod-dropdown-item';
option.textContent = item.label;
option.onclick = () => {
btn.textContent = item.label;
list.style.display = 'none';
let lines = [];
if (item.lines && Array.isArray(item.lines)) {
lines = item.lines;
} else if (item.content) {
lines = item.content.split(';').map(s => s.trim()).filter(Boolean);
}
output.innerHTML =
`<div class="mw-ui-title">${item.label}</div>` +
lines.map(line => `<div class="mw-ui-line">${line}</div>`).join('');
};
list.appendChild(option);
});
// Toggle list open/closed
btn.onclick = (e) => {
e.stopPropagation();
list.style.display = list.style.display === 'block' ? 'none' : 'block';
};
// Close when clicking outside
document.addEventListener('click', () => {
list.style.display = 'none';
});
container.appendChild(btn);
container.appendChild(list);
container.appendChild(output);
container.dataset.rendered = 'true';
});
}
/* ============================================================
* EXCLUSIVE COLLAPSIBLE DROPDOWNS (Loot Crate pages)
* When one item's image is opened, all others in the group
* are forced to collapse so only one is visible at a time.
* ============================================================ */
function initExclusiveCollapsibles() {
const outerKey = 'lootcrate';
const outerSelector = '#mw-customcollapsible-' + outerKey;
const innerSelector = outerSelector + ' [class*="mw-customtoggle-"]';
let suppress = false;
// Event delegation: works regardless of when elements are added,
// and re-discovers items inside the outer dropdown each click.
$(document).on('click', innerSelector, function () {
if (suppress) return;
const match = this.className.match(/mw-customtoggle-(\S+)/);
if (!match) return;
const clickedKey = match[1];
setTimeout(() => {
suppress = true;
// Close any other open item images
$(innerSelector).each(function () {
const m = this.className.match(/mw-customtoggle-(\S+)/);
if (!m || m[1] === clickedKey) return;
const $img = $('#mw-customcollapsible-' + m[1]);
if ($img.length && !$img.hasClass('mw-collapsed')) {
$(this).trigger('click');
}
});
// Update outer dropdown label to show the picked item's name
const pickedName = $('.mw-customtoggle-' + clickedKey).first().text();
$('.mw-customtoggle-' + outerKey + ' span:first-child').text(pickedName);
// Auto-close the outer dropdown after a pick
const $outerCol = $(outerSelector);
if ($outerCol.length && !$outerCol.hasClass('mw-collapsed')) {
$('.mw-customtoggle-' + outerKey).first().trigger('click');
}
suppress = false;
}, 50);
});
}
/* ============================================================
* DISCORD TAB OVERRIDE
* Repurposes the page's "Discussion" tab as a Discord invite.
* ============================================================ */
function initDiscordTab() {
const discussionTab = document.querySelector('#ca-talk a');
if (discussionTab) {
discussionTab.href = 'https://discord.com/invite/FZtkXeGeUA';
discussionTab.target = '_blank';
discussionTab.textContent = 'Discord';
}
}
/* ============================================================
* BOOT
* ============================================================ */
initTabs();
initExclusiveCollapsibles();
initDiscordTab();
});
});