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');
}
});
}
// Initialize hamburger menu when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', createHamburgerMenu);
} else {
createHamburgerMenu();
}
})();