MediaWiki:Common.js: Difference between revisions
imported>Dawning Im Wiking It |
m sound maybe |
||
| (19 intermediate revisions by 2 users not shown) | |||
| Line 3: | Line 3: | ||
/* DRUID */ | /* DRUID */ | ||
$(function () { | $(function () { | ||
$(".druid-main-images-label").off("click"); | |||
$(".druid-main-images-label").click(function () { | |||
var $parent = $(this).closest(".druid-container"); | |||
$parent.find(".druid-toggleable").removeClass("focused"); | |||
var i = $(this).attr("data-druid"); | |||
$parent.find(".druid-toggleable[data-druid=" + i + "]").addClass("focused"); | |||
}); | }); | ||
$(".druid-collapsible").off("click"); | |||
$(".druid-collapsible").click(function () { | |||
var kind = $(this).attr("data-druid-section"); | |||
$(this).toggleClass("druid-collapsible-collapsed"); | |||
$(this) | |||
.closest(".druid-container") | |||
.find("[data-druid-section-row=" + kind + "]") | |||
.toggleClass("druid-collapsed"); | |||
}); | |||
$(".druid-container").on("click", ".worn-toggle-button", function (e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
var $btn = $(this); | |||
var $container = $btn.closest('.has-worn-variant'); | |||
var $base = $container.find('.image-base'); | |||
var $worn = $container.find('.image-worn'); | |||
if ($container.hasClass('is-worn-active')) { | |||
$container.removeClass('is-worn-active'); | |||
$worn.hide(); | |||
$base.show(); | |||
$btn.text('Show Worn'); | |||
} else { | |||
$container.addClass('is-worn-active'); | |||
$base.hide(); | |||
$worn.show(); | |||
$btn.text('Show Normal'); | |||
} | |||
console.log("Toggle state: " + ($container.hasClass('is-worn-active') ? "Worn" : "Normal")); | |||
}); | |||
}); | }); | ||
/* End DRUID */ | |||
/* End DRUID */ | |||
/* Sound */ | |||
(function () { | |||
let currentAudio = null; | |||
let currentBtn = null; | |||
$(document).on('click', '.sound-button', function () { | |||
$( | const $btn = $(this); | ||
const soundUrl = $btn.data('sound-file'); | |||
if (!soundUrl) return; | |||
if (currentAudio && currentBtn && currentBtn[0] === $btn[0]) { | |||
if (!currentAudio.paused) { | |||
currentAudio.pause(); | |||
currentAudio.currentTime = 0; | |||
$btn.removeClass('playing'); | |||
return; // Stop here | |||
} | } | ||
} | } | ||
if (currentAudio) { | |||
currentAudio.pause(); | |||
currentAudio.currentTime = 0; | |||
$('.sound-button').removeClass('playing'); | |||
} | |||
const audio = new Audio(soundUrl); | |||
currentAudio = audio; | |||
currentBtn = $btn; | |||
const | |||
$btn.addClass('playing'); | |||
console. | audio.play().catch(function(error) { | ||
console.error("Playback failed:", error); | |||
$btn.removeClass('playing'); | |||
}); | }); | ||
} | |||
audio.onended = function() { | |||
$btn.removeClass('playing'); | |||
currentAudio = null; | |||
currentBtn = null; | |||
}; | |||
}); | |||
})(); | |||
/* End Sound */ | |||
/* [[Template:Spoiler]] */ | |||
$(function () { | |||
$('.spoiler-content') | |||
.off('click') // in case this code is loaded twice | |||
.on('click', function(e){ | |||
$(this).toggleClass('show'); | |||
}).find('a').on('click', function(e){ | |||
e.stopPropagation(); | |||
}); | |||
}); | |||
/* End Template:Spoiler */ | |||
/* Link to imported modules from Lua code */ | |||
$(function() { | |||
var config = mw.config.get([ | |||
'wgCanonicalNamespace', | |||
'wgFormattedNamespaces' | |||
]); | |||
if (config.wgCanonicalNamespace !== 'Module') { | |||
return; | |||
} | |||
var localizedNamespace = config.wgFormattedNamespaces[828]; | |||
$('.s1, .s2, .s').each(function() { | |||
var $this = $(this); | |||
var html = $this.html(); | |||
var quote = html[0]; | |||
var isLongStringQuote = quote === '['; | |||
var quoteRE = new RegExp('^\\' + quote + '|\\' + quote + '$', 'g'); | |||
if (isLongStringQuote) { | |||
quoteRE = /^\[\[|\]\]$/g; | |||
} | |||
var name = html.replace(quoteRE, ''); | |||
var isEnglishPrefix = name.startsWith('Module:'); | |||
var isLocalizedPrefix = name.startsWith(localizedNamespace + ':'); | |||
var isDevPrefix = name.startsWith('Dev:'); | |||
if (isEnglishPrefix || isLocalizedPrefix || isDevPrefix) { | |||
var attrs = { | |||
href: mw.util.getUrl(name) | |||
}; | |||
if (isDevPrefix) { | |||
attrs.href = 'https://commons.wiki.gg/wiki/Module:' + mw.util.wikiUrlencode(name.replace('Dev:', '')); | |||
attrs.target = '_blank'; | |||
attrs.rel = 'noopener'; | |||
} | |||
var link = mw.html.element('a', attrs, name); | |||
var str = quote + link + quote; | |||
if (isLongStringQuote) { | |||
str = '[[' + link + ']]'; | |||
} | |||
$this.html(str); | |||
} | |||
}); | |||
}); | |||
/* dynamic gif displayer ONLY TOUCH IF YOU KNOW WHAT YOU ARE DOING */ | |||
(function() { | |||
'use strict'; | |||
console.log('Dynamic GIF Displayer: Script loading...'); | |||
// Configuration | |||
const CONFIG = { | |||
containerClass: 'dynamic-gif-container', | |||
displayClass: 'gif-display-area', | |||
autoExpand: true, | |||
expandDelay: 200, | |||
collapseDelay: 1000 | |||
}; | |||
let expandTimer = null; | |||
let collapseTimer = null; | |||
let currentGif = null; | |||
function safeCreateError(message) { | |||
const div = document.createElement('div'); | |||
div.className = 'gif-error'; | |||
div.textContent = message; | |||
return div; | |||
} | |||
function safeCreatePlaceholder(message) { | |||
const div = document.createElement('div'); | |||
div.className = 'gif-placeholder'; | |||
div.textContent = message; | |||
return div; | |||
} | |||
function init() { | |||
console.log('Dynamic GIF Displayer: Initializing...'); | |||
const containers = document.querySelectorAll('.' + CONFIG.containerClass); | |||
console.log('Dynamic GIF Displayer: Found', containers.length, 'containers'); | |||
containers.forEach(container => { | |||
console.log('Dynamic GIF Displayer: Setting up container', container.id); | |||
setupContainer(container); | |||
attachTooltipListeners(container); | |||
}); | |||
} | |||
function setupContainer(container) { | |||
const displayArea = container.querySelector('.' + CONFIG.displayClass); | |||
if (!displayArea) { | |||
console.error('Dynamic GIF Displayer: No display area found'); | |||
return; | |||
} | |||
displayArea.addEventListener('mouseenter', () => { | |||
clearTimeout(collapseTimer); | |||
}); | |||
displayArea.addEventListener('mouseleave', () => { | |||
scheduleCollapse(container); | |||
}); | |||
} | |||
function attachTooltipListeners(container) { | |||
console.log('Dynamic GIF Displayer: Attaching tooltip listeners...'); | |||
const gifMap = buildGifMap(container); | |||
console.log('Dynamic GIF Displayer: GIF map', gifMap); | |||
const tooltips = document.querySelectorAll('.advanced-tooltip'); | |||
console.log('Dynamic GIF Displayer: Found', tooltips.length, 'tooltips'); | |||
tooltips.forEach((tooltip) => { | |||
const skillTitle = tooltip.querySelector('.skill-title'); | |||
if (!skillTitle) return; | |||
const skillName = skillTitle.textContent.trim(); | |||
const gifUrl = gifMap[skillName]; | |||
console.log('Dynamic GIF Displayer: Processing tooltip:', skillName, 'GIF:', gifUrl); | |||
if (gifUrl) { | |||
// Remove old listeners if they exist | |||
if (tooltip._gifHoverHandler) { | |||
tooltip.removeEventListener('mouseenter', tooltip._gifHoverHandler); | |||
} | |||
if (tooltip._gifLeaveHandler) { | |||
tooltip.removeEventListener('mouseleave', tooltip._gifLeaveHandler); | |||
} | |||
tooltip._gifHoverHandler = () => handleTooltipHover(container, gifUrl, skillName); | |||
tooltip._gifLeaveHandler = () => scheduleCollapse(container); | |||
tooltip.addEventListener('mouseenter', tooltip._gifHoverHandler); | |||
tooltip.addEventListener('mouseleave', tooltip._gifLeaveHandler); | |||
console.log('Dynamic GIF Displayer: Attached listeners to', skillName); | |||
} | |||
}); | |||
} | |||
function buildGifMap(container) { | |||
const gifMap = {}; | |||
const listData = container.getAttribute('data-gif-list'); | |||
if (!listData) { | |||
console.error('Dynamic GIF Displayer: No data-gif-list attribute found'); | |||
return gifMap; | |||
} | |||
console.log('Dynamic GIF Displayer: Raw list data:', listData); | |||
const entries = listData.split(';').filter(e => e.trim()); | |||
console.log('Dynamic GIF Displayer: Found', entries.length, 'entries'); | |||
entries.forEach(entry => { | |||
const parts = entry.split(':'); | |||
if (parts.length >= 2) { | |||
const skillName = parts[0].trim(); | |||
const gifFile = parts.slice(1).join(':').trim(); | |||
if (skillName && gifFile) { | |||
let gifUrl; | |||
if (typeof mw !== 'undefined' && mw.config) { | |||
const scriptPath = mw.config.get('wgScriptPath') || ''; | |||
gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile); | |||
} else { | |||
gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile); | |||
} | |||
gifMap[skillName] = gifUrl; | |||
console.log('Dynamic GIF Displayer: Mapped "' + skillName + '" to', gifUrl); | |||
} | |||
} | |||
}); | |||
return gifMap; | |||
} | |||
function handleTooltipHover(container, gifUrl, skillName) { | function handleTooltipHover(container, gifUrl, skillName) { | ||
console.log('Dynamic GIF Displayer: Hovering', skillName); | console.log('Dynamic GIF Displayer: Hovering', skillName); | ||
| Line 219: | Line 291: | ||
clearTimeout(expandTimer); | clearTimeout(expandTimer); | ||
// Show GIF after delay (allows checking if different from current) | |||
expandTimer = setTimeout(() => { | expandTimer = setTimeout(() => { | ||
showGif(container, gifUrl, skillName); | showGif(container, gifUrl, skillName); | ||
}, CONFIG.expandDelay); | }, CONFIG.expandDelay); | ||
} | } | ||
function showGif(container, gifUrl, skillName) { | function showGif(container, gifUrl, skillName) { | ||
console.log('Dynamic GIF Displayer: Showing GIF for', skillName, gifUrl); | console.log('Dynamic GIF Displayer: Showing GIF for', skillName, gifUrl); | ||
| Line 229: | Line 302: | ||
const displayArea = container.querySelector('.' + CONFIG.displayClass); | const displayArea = container.querySelector('.' + CONFIG.displayClass); | ||
if (!displayArea) return; | if (!displayArea) return; | ||
if (!container.classList.contains('expanded')) { | if (!container.classList.contains('expanded')) { | ||
| Line 237: | Line 308: | ||
} | } | ||
const imgContainer = displayArea.querySelector('.gif-image-container'); | |||
const caption = displayArea.querySelector('.gif-caption'); | |||
// Update caption immediately | |||
if (caption) { | |||
caption.textContent = skillName; | |||
caption.style.display = 'block'; | |||
} | |||
// Only reload if different GIF | |||
if (currentGif === gifUrl) { | |||
console.log('Dynamic GIF Displayer: Already showing', skillName); | |||
return; | |||
} | |||
// Set currentGif IMMEDIATELY to prevent race conditions | |||
currentGif = gifUrl; | |||
if (imgContainer) { | |||
const placeholder = imgContainer.querySelector('.gif-placeholder'); | |||
if (placeholder) { | |||
placeholder.remove(); | |||
} | |||
// Don't set dynamic height - let CSS handle fixed sizing | |||
if (imgContainer) { | const gifFilename = gifUrl.split('/').pop(); | ||
const | const decodedFilename = decodeURIComponent(gifFilename); | ||
if ( | |||
if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') { | |||
imgContainer.innerHTML = ''; | |||
imgContainer.appendChild(safeCreatePlaceholder('This node does not require a GIF due to its simplicity.')); | |||
imgContainer.classList.remove('loading'); | |||
} else { | |||
// Cancel any existing image loads | |||
const existingImg = imgContainer.querySelector('img'); | |||
if (existingImg) { | |||
existingImg.onload = null; | |||
existingImg.onerror = null; | |||
} | } | ||
imgContainer. | imgContainer.classList.add('loading'); | ||
const img = document.createElement('img'); | |||
img.src = gifUrl; | |||
img.alt = skillName; | |||
// CSS handles sizing via .gif-image-container img rules | |||
// Store reference for validation | |||
const targetGif = gifUrl; | |||
img.onload = () => { | |||
// Only update if this is still the current target GIF | |||
if (currentGif === targetGif) { | |||
imgContainer.innerHTML = ''; | imgContainer.innerHTML = ''; | ||
imgContainer.appendChild(img); | imgContainer.appendChild(img); | ||
imgContainer.classList.remove('loading'); | imgContainer.classList.remove('loading'); | ||
console.log('Dynamic GIF Displayer: GIF loaded successfully'); | console.log('Dynamic GIF Displayer: GIF loaded successfully'); | ||
}; | } else { | ||
console.log('Dynamic GIF Displayer: Discarded outdated load for', skillName); | |||
} | |||
}; | |||
img.onerror = () => { | |||
// Only update if this is still the current target GIF | |||
if (currentGif === targetGif) { | |||
imgContainer.innerHTML = ''; | imgContainer.innerHTML = ''; | ||
imgContainer.appendChild(safeCreateError('GIF not found: ' + | imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename)); | ||
imgContainer.classList.remove('loading'); | imgContainer.classList.remove('loading'); | ||
console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl); | console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl); | ||
} | } | ||
} | }; | ||
} | } | ||
} | } | ||
} | } | ||
function scheduleCollapse(container) { | |||
// Keep the most recent GIF displayed instead of clearing it | |||
// GIF will only change when a different skill node is hovered | |||
clearTimeout(collapseTimer); | |||
console.log('Dynamic GIF Displayer: Keeping current GIF displayed'); | |||
} | |||
window.toggleGifDisplay = function(containerId) { | |||
console.log('Dynamic GIF Displayer: Toggle clicked for', containerId); | |||
const container = document.getElementById(containerId); | |||
if (!container) { | |||
console.error('Dynamic GIF Displayer: Container not found', containerId); | |||
return; | |||
} | |||
clearTimeout(collapseTimer); | |||
if (container.style.display === 'none') { | |||
container.style.display = 'block'; | |||
} else { | |||
container.style.display = 'none'; | |||
} | |||
// Only clear the GIF when manually toggling the display off | |||
if (container.style.display === 'none') { | |||
const displayArea = container.querySelector('.' + CONFIG.displayClass); | |||
if (displayArea) { | |||
const imgContainer = displayArea.querySelector('.gif-image-container'); | |||
const caption = displayArea.querySelector('.gif-caption'); | |||
if (imgContainer) { | |||
imgContainer.innerHTML = ''; | |||
imgContainer.appendChild(safeCreatePlaceholder('Hover over a skill node to see its demonstration')); | |||
imgContainer.style.height = ''; | |||
imgContainer.style.minHeight = ''; | |||
} | |||
if (caption) { | |||
caption.textContent = ''; | |||
caption.style.display = 'none'; | |||
} | |||
} | |||
container.classList.remove('expanded'); | |||
currentGif = null; | |||
} | |||
}; | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', init); | |||
} else { | |||
init(); | |||
} | |||
window.addEventListener('load', init); | |||
if (typeof mw !== 'undefined' && mw.hook) { | |||
mw.hook('wikipage.content').add(init); | |||
} | |||
const observer = new MutationObserver(function(mutations) { | |||
let shouldReinit = false; | |||
mutations.forEach(function(mutation) { | |||
if (mutation.addedNodes.length) { | |||
mutation.addedNodes.forEach(function(node) { | |||
if (node.nodeType === 1 && ( | |||
node.classList && node.classList.contains('advanced-tooltip') || | |||
node.querySelector && node.querySelector('.advanced-tooltip') | |||
)) { | |||
shouldReinit = true; | |||
} | |||
}); | |||
} | |||
}); | |||
if (shouldReinit) { | |||
console.log('Dynamic GIF Displayer: Content changed, reinitializing...'); | |||
setTimeout(init, 100); | |||
} | |||
}); | |||
observer.observe(document.body, { | |||
childList: true, | |||
subtree: true | |||
}); | |||
console.log('Dynamic GIF Displayer: Script loaded'); | |||
})(); | |||
/* Make mp-links clickable anywhere in the box */ | |||
(function() { | |||
'use strict'; | |||
function | function initMpLinksClick() { | ||
const mpLinkItems = document.querySelectorAll('.mp-links > ul > li'); | |||
mpLinkItems.forEach(item => { | |||
// Remove any existing click handler | |||
if (item._clickHandlerAttached) return; | |||
item.addEventListener('click', function(e) { | |||
// Find the first anchor tag in this li | |||
const link = this.querySelector('a'); | |||
const | if (link && e.target !== link) { | ||
// Trigger a click on the link | |||
if ( | link.click(); | ||
} | } | ||
}); | |||
item._clickHandlerAttached = true; | |||
}); | |||
} | |||
} | } | ||
// Initialize on page load | |||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
document.addEventListener('DOMContentLoaded', | document.addEventListener('DOMContentLoaded', initMpLinksClick); | ||
} else { | } else { | ||
initMpLinksClick(); | |||
} | } | ||
window.addEventListener('load', | // Reinitialize when content changes | ||
window.addEventListener('load', initMpLinksClick); | |||
if (typeof mw !== 'undefined' && mw.hook) { | if (typeof mw !== 'undefined' && mw.hook) { | ||
mw.hook('wikipage.content').add( | mw.hook('wikipage.content').add(initMpLinksClick); | ||
} | } | ||
// Watch for DOM changes | |||
const observer = new MutationObserver(function(mutations) { | const observer = new MutationObserver(function(mutations) { | ||
let shouldReinit = false; | let shouldReinit = false; | ||
| Line 375: | Line 517: | ||
mutation.addedNodes.forEach(function(node) { | mutation.addedNodes.forEach(function(node) { | ||
if (node.nodeType === 1 && ( | if (node.nodeType === 1 && ( | ||
node.classList && node.classList.contains(' | node.classList && node.classList.contains('mp-links') || | ||
node.querySelector && node.querySelector('. | node.querySelector && node.querySelector('.mp-links') | ||
)) { | )) { | ||
shouldReinit = true; | shouldReinit = true; | ||
| Line 384: | Line 526: | ||
}); | }); | ||
if (shouldReinit) { | if (shouldReinit) { | ||
setTimeout(initMpLinksClick, 100); | |||
setTimeout( | |||
} | } | ||
}); | }); | ||
| Line 393: | Line 534: | ||
subtree: true | subtree: true | ||
}); | }); | ||
})(); | })(); | ||
(function() { | |||
'use strict'; | |||
console.log('Tab GIF Display: Script loading...'); | |||
// Configuration | |||
const CONFIG = { | |||
containerClass: 'tab-gif-container', | |||
displayClass: 'tab-gif-display-area', | |||
tabsClass: 'tab-gif-tabs', | |||
tabClass: 'tab-gif-tab', | |||
activeTabClass: 'tab-gif-active' | |||
}; | |||
let currentContainers = new Map(); | |||
let initAttempts = 0; | |||
const MAX_INIT_ATTEMPTS = 5; | |||
function safeCreateError(message) { | |||
const div = document.createElement('div'); | |||
div.className = 'gif-error'; | |||
div.textContent = message; | |||
} | return div; | ||
}); | } | ||
function safeCreatePlaceholder(message) { | |||
const div = document.createElement('div'); | |||
div.className = 'gif-placeholder'; | |||
div.textContent = message; | |||
return div; | |||
} | |||
function init() { | |||
initAttempts++; | |||
console.log('Tab GIF Display: Initializing... (attempt ' + initAttempts + ')'); | |||
const containers = document.querySelectorAll('.' + CONFIG.containerClass); | |||
console.log('Tab GIF Display: Found', containers.length, 'containers'); | |||
if (containers.length === 0 && initAttempts < MAX_INIT_ATTEMPTS) { | |||
console.log('Tab GIF Display: No containers found yet, will retry...'); | |||
setTimeout(init, 500); | |||
return; | |||
} | |||
let setupCount = 0; | |||
containers.forEach(container => { | |||
const containerId = container.id; | |||
console.log('Tab GIF Display: Checking container', containerId); | |||
if (!currentContainers.has(containerId)) { | |||
console.log('Tab GIF Display: Setting up NEW container', containerId); | |||
const success = setupContainer(container); | |||
if (success) { | |||
currentContainers.set(containerId, container); | |||
setupCount++; | |||
} | |||
} else { | |||
console.log('Tab GIF Display: Container already initialized', containerId); | |||
} | |||
}); | |||
console.log('Tab GIF Display: Successfully set up', setupCount, 'containers'); | |||
} | |||
function setupContainer(container) { | |||
console.log('Tab GIF Display: setupContainer called for', container.id); | |||
const gifMap = buildGifMap(container); | |||
console.log('Tab GIF Display: GIF map built', gifMap); | |||
if (Object.keys(gifMap).length === 0) { | |||
console.error('Tab GIF Display: No GIFs found for container', container.id); | |||
const displayArea = container.querySelector('.' + CONFIG.displayClass); | |||
if (displayArea) { | |||
const imgContainer = displayArea.querySelector('.gif-image-container'); | |||
if (imgContainer) { | |||
imgContainer.innerHTML = ''; | |||
imgContainer.appendChild(safeCreateError('No GIFs configured. Check your list parameter.')); | |||
} | |||
} | |||
return false; | |||
} | |||
createTabs(container, gifMap); | |||
// Display the first GIF by default | |||
const firstTabName = Object.keys(gifMap)[0]; | |||
console.log('Tab GIF Display: Showing first tab:', firstTabName); | |||
showGif(container, gifMap[firstTabName], firstTabName); | |||
return true; | |||
} | |||
function buildGifMap(container) { | |||
const gifMap = {}; | |||
const listData = container.getAttribute('data-gif-list'); | |||
console.log('Tab GIF Display: Building map for container', container.id); | |||
console.log('Tab GIF Display: data-gif-list attribute:', listData); | |||
if (!listData) { | |||
console.error('Tab GIF Display: No data-gif-list attribute found on', container.id); | |||
return gifMap; | |||
} | |||
if (listData.trim() === '') { | |||
console.error('Tab GIF Display: data-gif-list is empty on', container.id); | |||
return gifMap; | |||
} | |||
const entries = listData.split(';').filter(e => e.trim()); | |||
console.log('Tab GIF Display: Found', entries.length, 'entries in list'); | |||
entries.forEach((entry, index) => { | |||
console.log('Tab GIF Display: Processing entry', index, ':', entry); | |||
const parts = entry.split(':'); | |||
if (parts.length < 2) { | |||
console.warn('Tab GIF Display: Invalid entry format (no colon):', entry); | |||
return; | |||
} | |||
const tabName = parts[0].trim(); | |||
const gifFile = parts.slice(1).join(':').trim(); | |||
console.log('Tab GIF Display: Parsed - Tab:', tabName, 'File:', gifFile); | |||
if (!tabName) { | |||
console.warn('Tab GIF Display: Empty tab name in entry:', entry); | |||
return; | |||
} | |||
if (!gifFile) { | |||
console.warn('Tab GIF Display: Empty gif file in entry:', entry); | |||
return; | |||
} | |||
let gifUrl; | |||
if (typeof mw !== 'undefined' && mw.config) { | |||
const scriptPath = mw.config.get('wgScriptPath') || ''; | |||
gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile); | |||
} else { | |||
gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile); | |||
} | |||
gifMap[tabName] = gifUrl; | |||
console.log('Tab GIF Display: Mapped "' + tabName + '" to', gifUrl); | |||
}); | |||
console.log('Tab GIF Display: Final map has', Object.keys(gifMap).length, 'entries'); | |||
return gifMap; | |||
} | |||
function createTabs(container, gifMap) { | |||
const tabsContainer = container.querySelector('.' + CONFIG.tabsClass); | |||
if (!tabsContainer) { | |||
console.error('Tab GIF Display: No tabs container found in', container.id); | |||
return; | |||
} | |||
console.log('Tab GIF Display: Creating tabs in', container.id); | |||
tabsContainer.innerHTML = ''; | |||
let isFirst = true; | |||
let tabCount = 0; | |||
Object.keys(gifMap).forEach(tabName => { | |||
const tab = document.createElement('button'); | |||
tab.className = CONFIG.tabClass; | |||
tab.textContent = tabName; | |||
tab.setAttribute('data-tab-name', tabName); | |||
tab.type = 'button'; | |||
if (isFirst) { | |||
tab.classList.add(CONFIG.activeTabClass); | |||
isFirst = false; | |||
} | |||
tab.addEventListener('click', () => { | |||
console.log('Tab GIF Display: Tab clicked:', tabName); | |||
handleTabClick(container, gifMap, tabName); | |||
}); | |||
tabsContainer.appendChild(tab); | |||
tabCount++; | |||
console.log('Tab GIF Display: Created tab button for', tabName); | |||
}); | |||
console.log('Tab GIF Display: Created', tabCount, 'tabs total'); | |||
} | |||
function handleTabClick(container, gifMap, tabName) { | |||
console.log('Tab GIF Display: Handling tab click for', tabName); | |||
const tabs = container.querySelectorAll('.' + CONFIG.tabClass); | |||
tabs.forEach(tab => { | |||
if (tab.getAttribute('data-tab-name') === tabName) { | |||
tab.classList.add(CONFIG.activeTabClass); | |||
} else { | |||
tab.classList.remove(CONFIG.activeTabClass); | |||
} | |||
}); | |||
const gifUrl = gifMap[tabName]; | |||
showGif(container, gifUrl, tabName); | |||
} | |||
function showGif(container, gifUrl, tabName) { | |||
console.log('Tab GIF Display: Showing GIF for', tabName, '-', gifUrl); | |||
const displayArea = container.querySelector('.' + CONFIG.displayClass); | |||
if (!displayArea) { | |||
console.error('Tab GIF Display: No display area found'); | |||
return; | |||
} | |||
const rawSize = container.getAttribute('data-gif-size') || '400'; | |||
const gifSize = rawSize.replace(/[^0-9]/g, '') || '400'; | |||
const imgContainer = displayArea.querySelector('.gif-image-container'); | |||
const caption = displayArea.querySelector('.gif-caption'); | |||
if (imgContainer) { | |||
const placeholder = imgContainer.querySelector('.gif-placeholder'); | |||
if (placeholder) { | |||
placeholder.remove(); | |||
} | |||
imgContainer.style.height = gifSize + 'px'; | |||
imgContainer.style.minHeight = gifSize + 'px'; | |||
const gifFilename = gifUrl.split('/').pop(); | |||
const decodedFilename = decodeURIComponent(gifFilename); | |||
if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') { | |||
imgContainer.innerHTML = ''; | |||
imgContainer.appendChild(safeCreatePlaceholder('No GIF available for this tab.')); | |||
imgContainer.classList.remove('loading'); | |||
} else { | |||
const oldImg = imgContainer.querySelector('img'); | |||
const img = document.createElement('img'); | |||
img.src = gifUrl; | |||
img.alt = tabName; | |||
img.style.maxHeight = gifSize + 'px'; | |||
img.style.opacity = '0'; | |||
img.style.position = 'absolute'; | |||
img.style.transition = 'opacity 0.2s ease'; | |||
img.onload = () => { | |||
if (oldImg) { | |||
oldImg.style.transition = 'opacity 0.15s ease'; | |||
oldImg.style.opacity = '0'; | |||
setTimeout(() => { | |||
imgContainer.innerHTML = ''; | |||
img.style.position = 'relative'; | |||
img.style.opacity = '1'; | |||
imgContainer.appendChild(img); | |||
imgContainer.classList.remove('loading'); | |||
}, 150); | |||
} else { | |||
imgContainer.innerHTML = ''; | |||
img.style.position = 'relative'; | |||
img.style.opacity = '1'; | |||
imgContainer.appendChild(img); | |||
imgContainer.classList.remove('loading'); | |||
} | |||
console.log('Tab GIF Display: GIF loaded successfully'); | |||
}; | |||
img.onerror = () => { | |||
imgContainer.innerHTML = ''; | |||
imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename)); | |||
imgContainer.classList.remove('loading'); | |||
console.error('Tab GIF Display: Failed to load GIF', gifUrl); | |||
}; | |||
if (!oldImg) { | |||
imgContainer.appendChild(img); | |||
imgContainer.classList.add('loading'); | |||
} | |||
} | |||
} | |||
if (caption) { | |||
caption.textContent = tabName; | |||
caption.style.display = 'block'; | |||
} | |||
} | |||
window.toggleTabGifDisplay = function(containerId) { | |||
console.log('Tab GIF Display: Toggle clicked for', containerId); | |||
const container = document.getElementById(containerId); | |||
if (!container) { | |||
console.error('Tab GIF Display: Container not found', containerId); | |||
return; | |||
} | |||
if (container.style.display === 'none') { | |||
container.style.display = 'block'; | |||
} else { | |||
container.style.display = 'none'; | |||
} | |||
}; | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', init); | |||
} else { | |||
init(); | |||
} | |||
window.addEventListener('load', init); | |||
if (typeof mw !== 'undefined' && mw.hook) { | |||
mw.hook('wikipage.content').add(init); | |||
} | |||
const observer = new MutationObserver(function(mutations) { | |||
let shouldReinit = false; | |||
mutations.forEach(function(mutation) { | |||
if (mutation.addedNodes.length) { | |||
mutation.addedNodes.forEach(function(node) { | |||
if (node.nodeType === 1 && ( | |||
node.classList && node.classList.contains(CONFIG.containerClass) || | |||
node.querySelector && node.querySelector('.' + CONFIG.containerClass) | |||
)) { | |||
shouldReinit = true; | |||
} | |||
}); | |||
} | |||
}); | |||
if (shouldReinit) { | |||
console.log('Tab GIF Display: Content changed, reinitializing...'); | |||
setTimeout(init, 100); | |||
} | |||
}); | |||
observer.observe(document.body, { | |||
childList: true, | |||
subtree: true | |||
}); | |||
console.log('Tab GIF Display: Script loaded and ready'); | |||
})(); | |||
(function () { | |||
const images = [ | |||
'url(/wiki/Special:Redirect/file/Site-background-image.jpg)', | |||
'url(/wiki/Special:Redirect/file/Site-background-image2.jpg)', | |||
'url(/wiki/Special:Redirect/file/Site-background-image3.jpg)', | |||
'url(/wiki/Special:Redirect/file/Site-background-image4.jpg)', | |||
]; | |||
function seededRandom(seed) { | |||
let x = Math.sin(seed * 9999) * 10000; | |||
return x - Math.floor(x); | |||
} | |||
const day = new Date().getDate(); | |||
const pick1 = Math.floor(seededRandom(day) * images.length); | |||
let pick2 = Math.floor(seededRandom(day + 1) * images.length); | |||
if (pick2 === pick1) { | |||
pick2 = (pick1 + 1) % images.length; | |||
} | |||
document.body.style.backgroundImage = images[pick1]; | |||
const footer = document.querySelector('.mw-footer'); | |||
if (footer) { | |||
footer.style.backgroundImage = images[pick2]; | |||
} | |||
})(); | |||
Latest revision as of 11:19, 31 December 2025
/* Any JavaScript here will be loaded for all users on every page load. */
/* DRUID */
$(function () {
$(".druid-main-images-label").off("click");
$(".druid-main-images-label").click(function () {
var $parent = $(this).closest(".druid-container");
$parent.find(".druid-toggleable").removeClass("focused");
var i = $(this).attr("data-druid");
$parent.find(".druid-toggleable[data-druid=" + i + "]").addClass("focused");
});
$(".druid-collapsible").off("click");
$(".druid-collapsible").click(function () {
var kind = $(this).attr("data-druid-section");
$(this).toggleClass("druid-collapsible-collapsed");
$(this)
.closest(".druid-container")
.find("[data-druid-section-row=" + kind + "]")
.toggleClass("druid-collapsed");
});
$(".druid-container").on("click", ".worn-toggle-button", function (e) {
e.preventDefault();
e.stopPropagation();
var $btn = $(this);
var $container = $btn.closest('.has-worn-variant');
var $base = $container.find('.image-base');
var $worn = $container.find('.image-worn');
if ($container.hasClass('is-worn-active')) {
$container.removeClass('is-worn-active');
$worn.hide();
$base.show();
$btn.text('Show Worn');
} else {
$container.addClass('is-worn-active');
$base.hide();
$worn.show();
$btn.text('Show Normal');
}
console.log("Toggle state: " + ($container.hasClass('is-worn-active') ? "Worn" : "Normal"));
});
});
/* End DRUID */
/* Sound */
(function () {
let currentAudio = null;
let currentBtn = null;
$(document).on('click', '.sound-button', function () {
const $btn = $(this);
const soundUrl = $btn.data('sound-file');
if (!soundUrl) return;
if (currentAudio && currentBtn && currentBtn[0] === $btn[0]) {
if (!currentAudio.paused) {
currentAudio.pause();
currentAudio.currentTime = 0;
$btn.removeClass('playing');
return; // Stop here
}
}
if (currentAudio) {
currentAudio.pause();
currentAudio.currentTime = 0;
$('.sound-button').removeClass('playing');
}
const audio = new Audio(soundUrl);
currentAudio = audio;
currentBtn = $btn;
$btn.addClass('playing');
audio.play().catch(function(error) {
console.error("Playback failed:", error);
$btn.removeClass('playing');
});
audio.onended = function() {
$btn.removeClass('playing');
currentAudio = null;
currentBtn = null;
};
});
})();
/* End Sound */
/* [[Template:Spoiler]] */
$(function () {
$('.spoiler-content')
.off('click') // in case this code is loaded twice
.on('click', function(e){
$(this).toggleClass('show');
}).find('a').on('click', function(e){
e.stopPropagation();
});
});
/* End Template:Spoiler */
/* Link to imported modules from Lua code */
$(function() {
var config = mw.config.get([
'wgCanonicalNamespace',
'wgFormattedNamespaces'
]);
if (config.wgCanonicalNamespace !== 'Module') {
return;
}
var localizedNamespace = config.wgFormattedNamespaces[828];
$('.s1, .s2, .s').each(function() {
var $this = $(this);
var html = $this.html();
var quote = html[0];
var isLongStringQuote = quote === '[';
var quoteRE = new RegExp('^\\' + quote + '|\\' + quote + '$', 'g');
if (isLongStringQuote) {
quoteRE = /^\[\[|\]\]$/g;
}
var name = html.replace(quoteRE, '');
var isEnglishPrefix = name.startsWith('Module:');
var isLocalizedPrefix = name.startsWith(localizedNamespace + ':');
var isDevPrefix = name.startsWith('Dev:');
if (isEnglishPrefix || isLocalizedPrefix || isDevPrefix) {
var attrs = {
href: mw.util.getUrl(name)
};
if (isDevPrefix) {
attrs.href = 'https://commons.wiki.gg/wiki/Module:' + mw.util.wikiUrlencode(name.replace('Dev:', ''));
attrs.target = '_blank';
attrs.rel = 'noopener';
}
var link = mw.html.element('a', attrs, name);
var str = quote + link + quote;
if (isLongStringQuote) {
str = '[[' + link + ']]';
}
$this.html(str);
}
});
});
/* dynamic gif displayer ONLY TOUCH IF YOU KNOW WHAT YOU ARE DOING */
(function() {
'use strict';
console.log('Dynamic GIF Displayer: Script loading...');
// Configuration
const CONFIG = {
containerClass: 'dynamic-gif-container',
displayClass: 'gif-display-area',
autoExpand: true,
expandDelay: 200,
collapseDelay: 1000
};
let expandTimer = null;
let collapseTimer = null;
let currentGif = null;
function safeCreateError(message) {
const div = document.createElement('div');
div.className = 'gif-error';
div.textContent = message;
return div;
}
function safeCreatePlaceholder(message) {
const div = document.createElement('div');
div.className = 'gif-placeholder';
div.textContent = message;
return div;
}
function init() {
console.log('Dynamic GIF Displayer: Initializing...');
const containers = document.querySelectorAll('.' + CONFIG.containerClass);
console.log('Dynamic GIF Displayer: Found', containers.length, 'containers');
containers.forEach(container => {
console.log('Dynamic GIF Displayer: Setting up container', container.id);
setupContainer(container);
attachTooltipListeners(container);
});
}
function setupContainer(container) {
const displayArea = container.querySelector('.' + CONFIG.displayClass);
if (!displayArea) {
console.error('Dynamic GIF Displayer: No display area found');
return;
}
displayArea.addEventListener('mouseenter', () => {
clearTimeout(collapseTimer);
});
displayArea.addEventListener('mouseleave', () => {
scheduleCollapse(container);
});
}
function attachTooltipListeners(container) {
console.log('Dynamic GIF Displayer: Attaching tooltip listeners...');
const gifMap = buildGifMap(container);
console.log('Dynamic GIF Displayer: GIF map', gifMap);
const tooltips = document.querySelectorAll('.advanced-tooltip');
console.log('Dynamic GIF Displayer: Found', tooltips.length, 'tooltips');
tooltips.forEach((tooltip) => {
const skillTitle = tooltip.querySelector('.skill-title');
if (!skillTitle) return;
const skillName = skillTitle.textContent.trim();
const gifUrl = gifMap[skillName];
console.log('Dynamic GIF Displayer: Processing tooltip:', skillName, 'GIF:', gifUrl);
if (gifUrl) {
// Remove old listeners if they exist
if (tooltip._gifHoverHandler) {
tooltip.removeEventListener('mouseenter', tooltip._gifHoverHandler);
}
if (tooltip._gifLeaveHandler) {
tooltip.removeEventListener('mouseleave', tooltip._gifLeaveHandler);
}
tooltip._gifHoverHandler = () => handleTooltipHover(container, gifUrl, skillName);
tooltip._gifLeaveHandler = () => scheduleCollapse(container);
tooltip.addEventListener('mouseenter', tooltip._gifHoverHandler);
tooltip.addEventListener('mouseleave', tooltip._gifLeaveHandler);
console.log('Dynamic GIF Displayer: Attached listeners to', skillName);
}
});
}
function buildGifMap(container) {
const gifMap = {};
const listData = container.getAttribute('data-gif-list');
if (!listData) {
console.error('Dynamic GIF Displayer: No data-gif-list attribute found');
return gifMap;
}
console.log('Dynamic GIF Displayer: Raw list data:', listData);
const entries = listData.split(';').filter(e => e.trim());
console.log('Dynamic GIF Displayer: Found', entries.length, 'entries');
entries.forEach(entry => {
const parts = entry.split(':');
if (parts.length >= 2) {
const skillName = parts[0].trim();
const gifFile = parts.slice(1).join(':').trim();
if (skillName && gifFile) {
let gifUrl;
if (typeof mw !== 'undefined' && mw.config) {
const scriptPath = mw.config.get('wgScriptPath') || '';
gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
} else {
gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
}
gifMap[skillName] = gifUrl;
console.log('Dynamic GIF Displayer: Mapped "' + skillName + '" to', gifUrl);
}
}
});
return gifMap;
}
function handleTooltipHover(container, gifUrl, skillName) {
console.log('Dynamic GIF Displayer: Hovering', skillName);
clearTimeout(collapseTimer);
clearTimeout(expandTimer);
// Show GIF after delay (allows checking if different from current)
expandTimer = setTimeout(() => {
showGif(container, gifUrl, skillName);
}, CONFIG.expandDelay);
}
function showGif(container, gifUrl, skillName) {
console.log('Dynamic GIF Displayer: Showing GIF for', skillName, gifUrl);
const displayArea = container.querySelector('.' + CONFIG.displayClass);
if (!displayArea) return;
if (!container.classList.contains('expanded')) {
container.classList.add('expanded');
console.log('Dynamic GIF Displayer: Expanded container');
}
const imgContainer = displayArea.querySelector('.gif-image-container');
const caption = displayArea.querySelector('.gif-caption');
// Update caption immediately
if (caption) {
caption.textContent = skillName;
caption.style.display = 'block';
}
// Only reload if different GIF
if (currentGif === gifUrl) {
console.log('Dynamic GIF Displayer: Already showing', skillName);
return;
}
// Set currentGif IMMEDIATELY to prevent race conditions
currentGif = gifUrl;
if (imgContainer) {
const placeholder = imgContainer.querySelector('.gif-placeholder');
if (placeholder) {
placeholder.remove();
}
// Don't set dynamic height - let CSS handle fixed sizing
const gifFilename = gifUrl.split('/').pop();
const decodedFilename = decodeURIComponent(gifFilename);
if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') {
imgContainer.innerHTML = '';
imgContainer.appendChild(safeCreatePlaceholder('This node does not require a GIF due to its simplicity.'));
imgContainer.classList.remove('loading');
} else {
// Cancel any existing image loads
const existingImg = imgContainer.querySelector('img');
if (existingImg) {
existingImg.onload = null;
existingImg.onerror = null;
}
imgContainer.classList.add('loading');
const img = document.createElement('img');
img.src = gifUrl;
img.alt = skillName;
// CSS handles sizing via .gif-image-container img rules
// Store reference for validation
const targetGif = gifUrl;
img.onload = () => {
// Only update if this is still the current target GIF
if (currentGif === targetGif) {
imgContainer.innerHTML = '';
imgContainer.appendChild(img);
imgContainer.classList.remove('loading');
console.log('Dynamic GIF Displayer: GIF loaded successfully');
} else {
console.log('Dynamic GIF Displayer: Discarded outdated load for', skillName);
}
};
img.onerror = () => {
// Only update if this is still the current target GIF
if (currentGif === targetGif) {
imgContainer.innerHTML = '';
imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
imgContainer.classList.remove('loading');
console.error('Dynamic GIF Displayer: Failed to load GIF', gifUrl);
}
};
}
}
}
function scheduleCollapse(container) {
// Keep the most recent GIF displayed instead of clearing it
// GIF will only change when a different skill node is hovered
clearTimeout(collapseTimer);
console.log('Dynamic GIF Displayer: Keeping current GIF displayed');
}
window.toggleGifDisplay = function(containerId) {
console.log('Dynamic GIF Displayer: Toggle clicked for', containerId);
const container = document.getElementById(containerId);
if (!container) {
console.error('Dynamic GIF Displayer: Container not found', containerId);
return;
}
clearTimeout(collapseTimer);
if (container.style.display === 'none') {
container.style.display = 'block';
} else {
container.style.display = 'none';
}
// Only clear the GIF when manually toggling the display off
if (container.style.display === 'none') {
const displayArea = container.querySelector('.' + CONFIG.displayClass);
if (displayArea) {
const imgContainer = displayArea.querySelector('.gif-image-container');
const caption = displayArea.querySelector('.gif-caption');
if (imgContainer) {
imgContainer.innerHTML = '';
imgContainer.appendChild(safeCreatePlaceholder('Hover over a skill node to see its demonstration'));
imgContainer.style.height = '';
imgContainer.style.minHeight = '';
}
if (caption) {
caption.textContent = '';
caption.style.display = 'none';
}
}
container.classList.remove('expanded');
currentGif = null;
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
window.addEventListener('load', init);
if (typeof mw !== 'undefined' && mw.hook) {
mw.hook('wikipage.content').add(init);
}
const observer = new MutationObserver(function(mutations) {
let shouldReinit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1 && (
node.classList && node.classList.contains('advanced-tooltip') ||
node.querySelector && node.querySelector('.advanced-tooltip')
)) {
shouldReinit = true;
}
});
}
});
if (shouldReinit) {
console.log('Dynamic GIF Displayer: Content changed, reinitializing...');
setTimeout(init, 100);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
console.log('Dynamic GIF Displayer: Script loaded');
})();
/* Make mp-links clickable anywhere in the box */
(function() {
'use strict';
function initMpLinksClick() {
const mpLinkItems = document.querySelectorAll('.mp-links > ul > li');
mpLinkItems.forEach(item => {
// Remove any existing click handler
if (item._clickHandlerAttached) return;
item.addEventListener('click', function(e) {
// Find the first anchor tag in this li
const link = this.querySelector('a');
if (link && e.target !== link) {
// Trigger a click on the link
link.click();
}
});
item._clickHandlerAttached = true;
});
}
// Initialize on page load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initMpLinksClick);
} else {
initMpLinksClick();
}
// Reinitialize when content changes
window.addEventListener('load', initMpLinksClick);
if (typeof mw !== 'undefined' && mw.hook) {
mw.hook('wikipage.content').add(initMpLinksClick);
}
// Watch for DOM changes
const observer = new MutationObserver(function(mutations) {
let shouldReinit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1 && (
node.classList && node.classList.contains('mp-links') ||
node.querySelector && node.querySelector('.mp-links')
)) {
shouldReinit = true;
}
});
}
});
if (shouldReinit) {
setTimeout(initMpLinksClick, 100);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
})();
(function() {
'use strict';
console.log('Tab GIF Display: Script loading...');
// Configuration
const CONFIG = {
containerClass: 'tab-gif-container',
displayClass: 'tab-gif-display-area',
tabsClass: 'tab-gif-tabs',
tabClass: 'tab-gif-tab',
activeTabClass: 'tab-gif-active'
};
let currentContainers = new Map();
let initAttempts = 0;
const MAX_INIT_ATTEMPTS = 5;
function safeCreateError(message) {
const div = document.createElement('div');
div.className = 'gif-error';
div.textContent = message;
return div;
}
function safeCreatePlaceholder(message) {
const div = document.createElement('div');
div.className = 'gif-placeholder';
div.textContent = message;
return div;
}
function init() {
initAttempts++;
console.log('Tab GIF Display: Initializing... (attempt ' + initAttempts + ')');
const containers = document.querySelectorAll('.' + CONFIG.containerClass);
console.log('Tab GIF Display: Found', containers.length, 'containers');
if (containers.length === 0 && initAttempts < MAX_INIT_ATTEMPTS) {
console.log('Tab GIF Display: No containers found yet, will retry...');
setTimeout(init, 500);
return;
}
let setupCount = 0;
containers.forEach(container => {
const containerId = container.id;
console.log('Tab GIF Display: Checking container', containerId);
if (!currentContainers.has(containerId)) {
console.log('Tab GIF Display: Setting up NEW container', containerId);
const success = setupContainer(container);
if (success) {
currentContainers.set(containerId, container);
setupCount++;
}
} else {
console.log('Tab GIF Display: Container already initialized', containerId);
}
});
console.log('Tab GIF Display: Successfully set up', setupCount, 'containers');
}
function setupContainer(container) {
console.log('Tab GIF Display: setupContainer called for', container.id);
const gifMap = buildGifMap(container);
console.log('Tab GIF Display: GIF map built', gifMap);
if (Object.keys(gifMap).length === 0) {
console.error('Tab GIF Display: No GIFs found for container', container.id);
const displayArea = container.querySelector('.' + CONFIG.displayClass);
if (displayArea) {
const imgContainer = displayArea.querySelector('.gif-image-container');
if (imgContainer) {
imgContainer.innerHTML = '';
imgContainer.appendChild(safeCreateError('No GIFs configured. Check your list parameter.'));
}
}
return false;
}
createTabs(container, gifMap);
// Display the first GIF by default
const firstTabName = Object.keys(gifMap)[0];
console.log('Tab GIF Display: Showing first tab:', firstTabName);
showGif(container, gifMap[firstTabName], firstTabName);
return true;
}
function buildGifMap(container) {
const gifMap = {};
const listData = container.getAttribute('data-gif-list');
console.log('Tab GIF Display: Building map for container', container.id);
console.log('Tab GIF Display: data-gif-list attribute:', listData);
if (!listData) {
console.error('Tab GIF Display: No data-gif-list attribute found on', container.id);
return gifMap;
}
if (listData.trim() === '') {
console.error('Tab GIF Display: data-gif-list is empty on', container.id);
return gifMap;
}
const entries = listData.split(';').filter(e => e.trim());
console.log('Tab GIF Display: Found', entries.length, 'entries in list');
entries.forEach((entry, index) => {
console.log('Tab GIF Display: Processing entry', index, ':', entry);
const parts = entry.split(':');
if (parts.length < 2) {
console.warn('Tab GIF Display: Invalid entry format (no colon):', entry);
return;
}
const tabName = parts[0].trim();
const gifFile = parts.slice(1).join(':').trim();
console.log('Tab GIF Display: Parsed - Tab:', tabName, 'File:', gifFile);
if (!tabName) {
console.warn('Tab GIF Display: Empty tab name in entry:', entry);
return;
}
if (!gifFile) {
console.warn('Tab GIF Display: Empty gif file in entry:', entry);
return;
}
let gifUrl;
if (typeof mw !== 'undefined' && mw.config) {
const scriptPath = mw.config.get('wgScriptPath') || '';
gifUrl = scriptPath + '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
} else {
gifUrl = '/index.php?title=Special:Redirect/file/' + encodeURIComponent(gifFile);
}
gifMap[tabName] = gifUrl;
console.log('Tab GIF Display: Mapped "' + tabName + '" to', gifUrl);
});
console.log('Tab GIF Display: Final map has', Object.keys(gifMap).length, 'entries');
return gifMap;
}
function createTabs(container, gifMap) {
const tabsContainer = container.querySelector('.' + CONFIG.tabsClass);
if (!tabsContainer) {
console.error('Tab GIF Display: No tabs container found in', container.id);
return;
}
console.log('Tab GIF Display: Creating tabs in', container.id);
tabsContainer.innerHTML = '';
let isFirst = true;
let tabCount = 0;
Object.keys(gifMap).forEach(tabName => {
const tab = document.createElement('button');
tab.className = CONFIG.tabClass;
tab.textContent = tabName;
tab.setAttribute('data-tab-name', tabName);
tab.type = 'button';
if (isFirst) {
tab.classList.add(CONFIG.activeTabClass);
isFirst = false;
}
tab.addEventListener('click', () => {
console.log('Tab GIF Display: Tab clicked:', tabName);
handleTabClick(container, gifMap, tabName);
});
tabsContainer.appendChild(tab);
tabCount++;
console.log('Tab GIF Display: Created tab button for', tabName);
});
console.log('Tab GIF Display: Created', tabCount, 'tabs total');
}
function handleTabClick(container, gifMap, tabName) {
console.log('Tab GIF Display: Handling tab click for', tabName);
const tabs = container.querySelectorAll('.' + CONFIG.tabClass);
tabs.forEach(tab => {
if (tab.getAttribute('data-tab-name') === tabName) {
tab.classList.add(CONFIG.activeTabClass);
} else {
tab.classList.remove(CONFIG.activeTabClass);
}
});
const gifUrl = gifMap[tabName];
showGif(container, gifUrl, tabName);
}
function showGif(container, gifUrl, tabName) {
console.log('Tab GIF Display: Showing GIF for', tabName, '-', gifUrl);
const displayArea = container.querySelector('.' + CONFIG.displayClass);
if (!displayArea) {
console.error('Tab GIF Display: No display area found');
return;
}
const rawSize = container.getAttribute('data-gif-size') || '400';
const gifSize = rawSize.replace(/[^0-9]/g, '') || '400';
const imgContainer = displayArea.querySelector('.gif-image-container');
const caption = displayArea.querySelector('.gif-caption');
if (imgContainer) {
const placeholder = imgContainer.querySelector('.gif-placeholder');
if (placeholder) {
placeholder.remove();
}
imgContainer.style.height = gifSize + 'px';
imgContainer.style.minHeight = gifSize + 'px';
const gifFilename = gifUrl.split('/').pop();
const decodedFilename = decodeURIComponent(gifFilename);
if (decodedFilename.toLowerCase() === 'blank' || decodedFilename.toLowerCase() === 'blank.gif') {
imgContainer.innerHTML = '';
imgContainer.appendChild(safeCreatePlaceholder('No GIF available for this tab.'));
imgContainer.classList.remove('loading');
} else {
const oldImg = imgContainer.querySelector('img');
const img = document.createElement('img');
img.src = gifUrl;
img.alt = tabName;
img.style.maxHeight = gifSize + 'px';
img.style.opacity = '0';
img.style.position = 'absolute';
img.style.transition = 'opacity 0.2s ease';
img.onload = () => {
if (oldImg) {
oldImg.style.transition = 'opacity 0.15s ease';
oldImg.style.opacity = '0';
setTimeout(() => {
imgContainer.innerHTML = '';
img.style.position = 'relative';
img.style.opacity = '1';
imgContainer.appendChild(img);
imgContainer.classList.remove('loading');
}, 150);
} else {
imgContainer.innerHTML = '';
img.style.position = 'relative';
img.style.opacity = '1';
imgContainer.appendChild(img);
imgContainer.classList.remove('loading');
}
console.log('Tab GIF Display: GIF loaded successfully');
};
img.onerror = () => {
imgContainer.innerHTML = '';
imgContainer.appendChild(safeCreateError('GIF not found: ' + decodedFilename));
imgContainer.classList.remove('loading');
console.error('Tab GIF Display: Failed to load GIF', gifUrl);
};
if (!oldImg) {
imgContainer.appendChild(img);
imgContainer.classList.add('loading');
}
}
}
if (caption) {
caption.textContent = tabName;
caption.style.display = 'block';
}
}
window.toggleTabGifDisplay = function(containerId) {
console.log('Tab GIF Display: Toggle clicked for', containerId);
const container = document.getElementById(containerId);
if (!container) {
console.error('Tab GIF Display: Container not found', containerId);
return;
}
if (container.style.display === 'none') {
container.style.display = 'block';
} else {
container.style.display = 'none';
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
window.addEventListener('load', init);
if (typeof mw !== 'undefined' && mw.hook) {
mw.hook('wikipage.content').add(init);
}
const observer = new MutationObserver(function(mutations) {
let shouldReinit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1 && (
node.classList && node.classList.contains(CONFIG.containerClass) ||
node.querySelector && node.querySelector('.' + CONFIG.containerClass)
)) {
shouldReinit = true;
}
});
}
});
if (shouldReinit) {
console.log('Tab GIF Display: Content changed, reinitializing...');
setTimeout(init, 100);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
console.log('Tab GIF Display: Script loaded and ready');
})();
(function () {
const images = [
'url(/wiki/Special:Redirect/file/Site-background-image.jpg)',
'url(/wiki/Special:Redirect/file/Site-background-image2.jpg)',
'url(/wiki/Special:Redirect/file/Site-background-image3.jpg)',
'url(/wiki/Special:Redirect/file/Site-background-image4.jpg)',
];
function seededRandom(seed) {
let x = Math.sin(seed * 9999) * 10000;
return x - Math.floor(x);
}
const day = new Date().getDate();
const pick1 = Math.floor(seededRandom(day) * images.length);
let pick2 = Math.floor(seededRandom(day + 1) * images.length);
if (pick2 === pick1) {
pick2 = (pick1 + 1) % images.length;
}
document.body.style.backgroundImage = images[pick1];
const footer = document.querySelector('.mw-footer');
if (footer) {
footer.style.backgroundImage = images[pick2];
}
})();