MediaWiki:Common.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// MainPage2 interactions. Designed for MediaWiki site usage.
(function(){
'use strict';
// Simple tab controller (inspired by GuardianGuide.html)
window.showTab = function(tabId, el){
var contents = document.querySelectorAll('.tab-content');
for (var i=0;i<contents.length;i++){ contents[i].classList.remove('active'); }
var tabs = document.querySelectorAll('.nav-tab');
for (var j=0;j<tabs.length;j++){ tabs[j].classList.remove('active'); }
var target = document.getElementById(tabId);
if (target) target.classList.add('active');
if (el) el.classList.add('active');
};
// MediaWiki-safe tab click handler using event delegation
function onTabClick(e) {
var tab = e.target.closest('.nav-tab[data-tab]');
if (!tab) return;
e.preventDefault();
var targetId = tab.getAttribute('data-tab');
if (targetId) {
showTab(targetId, tab);
}
}
document.addEventListener('click', onTabClick, false);
// Handle special navigation clicks for tabs (like in Hologramsie)
function onSpecialTabNav(e) {
var trigger = e.target.closest('[data-tab-trigger]');
if (!trigger) return;
e.preventDefault();
var tabData = trigger.getAttribute('data-tab-trigger');
var buttonId = trigger.getAttribute('data-tab-button');
// Support anchors via href="#section" when data-scroll-to is not provided
var href = trigger.getAttribute('href');
var scrollToAttr = trigger.getAttribute('data-scroll-to');
var scrollTargetSelector = null;
if (scrollToAttr && scrollToAttr.trim()) {
scrollTargetSelector = '#' + scrollToAttr.replace(/^#/, '');
} else if (href && href.charAt(0) === '#') {
scrollTargetSelector = href; // already a selector
}
if (tabData && buttonId) {
var targetButton = document.getElementById(buttonId);
if (targetButton) {
showTab(tabData, targetButton);
// Scroll to anchor if specified (from data-scroll-to or href)
if (scrollTargetSelector) {
var element = document.querySelector(scrollTargetSelector);
if (element) {
setTimeout(function() {
element.scrollIntoView({behavior: 'smooth'});
}, 100);
}
}
}
}
}
document.addEventListener('click', onSpecialTabNav, false);
// Back to Top smooth scroll
function onBackToTop(e){
var trigger = e.target.closest('.back-to-top');
if (!trigger) return;
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
}
document.addEventListener('click', onBackToTop, false);
// Card click-throughs using data-link to wiki pages or in-page anchors
function onCardClick(e){
var card = e.target.closest('.card[data-link], .destaque-card[data-link]');
if (!card) return;
var link = card.getAttribute('data-link');
if (!link) return;
// In-page anchor navigation: data-link="#section-id"
if (link.charAt(0) === '#') {
e.preventDefault();
var anchorEl = document.querySelector(link);
if (anchorEl) {
anchorEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
return;
}
// Absolute external URL
if (/^https?:\/\//i.test(link)) {
window.location.href = link;
return;
}
// Navigate to the MediaWiki page (prefer mw.util if available)
var targetUrl = (window.mw && mw.util && typeof mw.util.getUrl === 'function')
? mw.util.getUrl(link)
: ('index.php?title=' + encodeURIComponent(link));
window.location.href = targetUrl;
}
document.addEventListener('click', onCardClick, false);
// Helper: determine if a node is inside an excluded container
function isInExcludedContext(node) {
if (!node || !node.parentElement) return false;
var p = node.parentElement.closest('script, style, textarea, input, select, option, .ve-ui-surface, .mw-editform, .CodeMirror, .cm-editor, .ace_editor');
return !!p;
}
// Safely process MediaWiki-like internal link markup [[Title]] -> <a href="...">Title</a>
function processWikiLinks() {
// Skip if the page was already parsed by MediaWiki (no raw brackets found)
if (document.body && document.body.textContent.indexOf('[[') === -1) return;
var walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
var textNodes = [];
var node;
while ((node = walker.nextNode())) {
var value = node.nodeValue;
if (!value) continue;
if (value.indexOf('[[') === -1) continue;
if (isInExcludedContext(node)) continue;
textNodes.push(node);
}
var linkRe = /\[\[([^\|\]]+)(?:\|([^\]]+))?\]\]/g;
textNodes.forEach(function(textNode) {
var src = textNode.nodeValue;
if (!linkRe.test(src)) return; // quick check
linkRe.lastIndex = 0; // reset stateful regex
var parent = textNode.parentNode;
var frag = document.createDocumentFragment();
var last = 0; var m;
while ((m = linkRe.exec(src)) !== null) {
// text before match
if (m.index > last) frag.appendChild(document.createTextNode(src.slice(last, m.index)));
var rawTitle = (m[1] || '').trim();
var display = (m[2] != null ? m[2] : rawTitle).trim();
// Skip File: or Image: links - keep original text intact
if (/^(File:|Image:)/i.test(rawTitle)) {
frag.appendChild(document.createTextNode(m[0]));
} else {
// Remove leading colon if present
if (rawTitle.charAt(0) === ':') rawTitle = rawTitle.slice(1);
var href = (window.mw && mw.util && typeof mw.util.getUrl === 'function')
? mw.util.getUrl(rawTitle)
: ('index.php?title=' + encodeURIComponent(rawTitle));
var a = document.createElement('a');
a.className = 'mw-link-internal';
a.setAttribute('href', href);
a.appendChild(document.createTextNode(display)); // avoid HTML injection
frag.appendChild(a);
}
last = linkRe.lastIndex;
}
// trailing text
if (last < src.length) frag.appendChild(document.createTextNode(src.slice(last)));
// Replace the original text node safely
parent.insertBefore(frag, textNode);
parent.removeChild(textNode);
});
}
// Process wiki links when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', processWikiLinks);
} else {
processWikiLinks();
}
// Mobile hamburger menu for sidebar
function createHamburgerMenu() {
// Only create if we're in MediaWiki Vector Legacy and on mobile
if (!document.body.classList.contains('skin-vector-legacy')) return;
var panel = document.getElementById('mw-panel');
if (!panel) return;
// Create hamburger button
var hamburger = document.createElement('button');
hamburger.className = 'mobile-hamburger-menu';
hamburger.setAttribute('aria-label', 'Toggle navigation menu');
hamburger.innerHTML = '<span></span><span></span><span></span>';
// Insert hamburger button at the top of the page
var content = document.getElementById('content') || document.querySelector('.mw-body');
if (content) {
content.parentNode.insertBefore(hamburger, content);
}
// Toggle sidebar function
function toggleSidebar() {
panel.classList.toggle('mobile-open');
hamburger.classList.toggle('active');
document.body.classList.toggle('sidebar-open');
}
// Click handler for hamburger
hamburger.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
toggleSidebar();
});
// Close sidebar when clicking outside
document.addEventListener('click', function(e) {
if (panel.classList.contains('mobile-open') &&
!panel.contains(e.target) &&
!hamburger.contains(e.target)) {
panel.classList.remove('mobile-open');
hamburger.classList.remove('active');
document.body.classList.remove('sidebar-open');
}
});
// Close sidebar on escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && panel.classList.contains('mobile-open')) {
panel.classList.remove('mobile-open');
hamburger.classList.remove('active');
document.body.classList.remove('sidebar-open');
}
});
}
// Collapsible sections functionality
function initCollapsibleSections() {
// Handle .collapsible-header elements (new style)
var collapsibleHeaders = document.querySelectorAll('.collapsible-header');
collapsibleHeaders.forEach(function(header) {
header.addEventListener('click', function(e) {
e.preventDefault();
var section = header.parentElement;
var content = section.querySelector('.collapsible-content');
if (content) {
var isExpanded = section.classList.contains('expanded');
if (isExpanded) {
section.classList.remove('expanded');
content.style.maxHeight = '0';
} else {
section.classList.add('expanded');
content.style.maxHeight = content.scrollHeight + 'px';
}
}
});
});
// Handle .collapsible elements (original GuardianGuide style)
var collapsibleButtons = document.querySelectorAll('.collapsible');
collapsibleButtons.forEach(function(button) {
button.addEventListener('click', function(e) {
e.preventDefault();
var content = button.nextElementSibling;
if (content && content.classList.contains('collapsible-content')) {
var isActive = button.classList.contains('active');
if (isActive) {
button.classList.remove('active');
content.style.maxHeight = '0';
} else {
button.classList.add('active');
content.style.maxHeight = content.scrollHeight + 'px';
}
}
});
});
}
// Guardian Type Decision Helper functionality
function initGuardianDecisionHelper() {
// Initialize accordion functionality for guardian types
function initAccordion() {
const accordions = document.getElementsByClassName("guardian-accordion");
for (let i = 0; i < accordions.length; i++) {
accordions[i].addEventListener("click", function() {
this.classList.toggle("active");
const panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
}
// Function to show guardian by difficulty level
function showGuardianByDifficulty(level) {
const accordions = document.getElementsByClassName("guardian-accordion");
let targetAccordion;
// Reset all accordions
for (let i = 0; i < accordions.length; i++) {
accordions[i].classList.remove("active");
accordions[i].nextElementSibling.style.maxHeight = null;
}
// Select the appropriate accordion based on difficulty level
switch(level) {
case "1":
targetAccordion = document.querySelector(".regular-accordion");
break;
case "2":
targetAccordion = document.querySelector(".mighty-accordion");
break;
case "3":
targetAccordion = document.querySelector(".legendary-accordion");
break;
case "4":
targetAccordion = document.querySelector(".superior-accordion");
break;
case "5":
targetAccordion = document.querySelector(".accomplished-accordion");
break;
}
// Activate the target accordion
if (targetAccordion) {
targetAccordion.classList.add("active");
const panel = targetAccordion.nextElementSibling;
panel.style.maxHeight = panel.scrollHeight + "px";
// Scroll to the accordion
setTimeout(function() {
targetAccordion.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 300);
}
}
// Initialize the decision helper
function initDecisionHelper() {
// Find all option elements
const options = document.querySelectorAll('.decision-options .option');
// Add click event listeners to each option
options.forEach(option => {
option.addEventListener('click', function() {
// Remove selected class from siblings
const siblings = this.parentElement.querySelectorAll('.option');
siblings.forEach(sib => sib.classList.remove('selected'));
// Add selected class to clicked option
this.classList.add('selected');
// Update recommendation
updateRecommendation();
});
});
}
// Function to update recommendation based on selections
function updateRecommendation() {
const selectedOptions = document.querySelectorAll('.decision-options .option.selected');
// If no options selected, show default message
if (selectedOptions.length === 0) {
document.getElementById('recommendedType').textContent = 'Select options above';
return;
}
// Count votes for each guardian type
const votes = {
'regular': 0,
'mighty': 0,
'legendary': 0,
'superior': 0,
'accomplished': 0
};
// Tally votes from selected options
selectedOptions.forEach(option => {
const types = option.getAttribute('data-type').split(',');
types.forEach(type => {
votes[type]++;
});
});
// Find the type with the most votes
let maxVotes = 0;
let recommendedType = '';
let tiedTypes = [];
for (const type in votes) {
if (votes[type] > maxVotes) {
maxVotes = votes[type];
recommendedType = type;
tiedTypes = [type];
} else if (votes[type] === maxVotes && maxVotes > 0) {
tiedTypes.push(type);
}
}
// Handle ties by recommending the more challenging type
if (tiedTypes.length > 1) {
const difficultyOrder = ['regular', 'mighty', 'legendary', 'superior', 'accomplished'];
let highestDifficultyIndex = -1;
tiedTypes.forEach(type => {
const typeIndex = difficultyOrder.indexOf(type);
if (typeIndex > highestDifficultyIndex) {
highestDifficultyIndex = typeIndex;
recommendedType = type;
}
});
}
// Format the recommendation with additional context
let formattedType = recommendedType.charAt(0).toUpperCase() + recommendedType.slice(1) + ' Guardian';
let recommendationText = formattedType;
// Add context based on the recommended type
switch(recommendedType) {
case 'regular':
recommendationText += ' - Perfect for beginners with limited time';
break;
case 'mighty':
recommendationText += ' - Good balance of effort and power';
break;
case 'legendary':
recommendationText += ' - Excellent for endgame content';
break;
case 'superior':
recommendationText += ' - For dedicated players seeking optimization';
break;
case 'accomplished':
recommendationText += ' - For true perfectionists';
break;
}
// Update the recommendation text
document.getElementById('recommendedType').textContent = recommendationText;
// Highlight the recommended guardian type in the accordion
highlightRecommendedType(recommendedType);
}
// Function to highlight the recommended guardian type in the accordion
function highlightRecommendedType(type) {
// Remove highlight from all accordions
const accordions = document.querySelectorAll('.guardian-accordion');
accordions.forEach(accordion => {
accordion.classList.remove('recommended');
});
// Add highlight to the recommended type
const recommendedAccordion = document.querySelector('.' + type + '-accordion');
if (recommendedAccordion) {
recommendedAccordion.classList.add('recommended');
// Scroll to the recommended type if not visible
const isVisible = isElementInViewport(recommendedAccordion);
if (!isVisible) {
recommendedAccordion.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
}
// Helper function to check if an element is in the viewport
function isElementInViewport(el) {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// Initialize components if they exist on the page
if (document.querySelector('.guardian-accordion') || document.querySelector('.decision-options')) {
initAccordion();
initDecisionHelper();
// Make functions available globally
window.updateGuardianRecommendation = updateRecommendation;
window.showGuardianByDifficulty = showGuardianByDifficulty;
}
}
// Initialize collapsible sections when DOM is ready
function initializeAll() {
createHamburgerMenu();
initCollapsibleSections();
initGuardianDecisionHelper();
}
// Initialize all functionality when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeAll);
} else {
initializeAll();
}
})();