MediaWiki:Common.js
Appearance
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.
/* ========================================================================
ERINDAL WIKI - COMMON.JS v5.3
Target: MediaWiki 1.43.x, legacy Vector
The topbar itself is now rendered by MediaWiki:Sidebar. JavaScript only
adds dropdowns, the remembered sidebar toggle, dynamic homepage data and
the progressive deity appearance/symbol switch.
It deliberately does not load fonts or construct the navigation bar.
======================================================================== */
( function () {
'use strict';
var sidebarKey = 'erindal-sidebar';
/* Apply the saved state again as a fallback. LocalSettings.php applies the
same class in the document head, before first paint. */
try {
if ( window.localStorage.getItem( sidebarKey ) === 'collapsed' ) {
document.documentElement.classList.add( 'ew-sidebar-collapsed' );
}
} catch ( error ) {
/* localStorage can be unavailable in strict/private browser modes. */
}
function onReady( callback ) {
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', callback, { once: true } );
} else {
callback();
}
}
function installSidebarToggle() {
if ( !document.getElementById( 'mw-panel' ) || document.getElementById( 'ew-sidebar-toggle' ) ) {
return;
}
var button = document.createElement( 'button' );
button.id = 'ew-sidebar-toggle';
button.type = 'button';
function updateButton() {
var collapsed = document.documentElement.classList.contains( 'ew-sidebar-collapsed' );
button.textContent = collapsed ? '\u203a' : '\u2039';
button.setAttribute( 'aria-expanded', collapsed ? 'false' : 'true' );
button.setAttribute( 'aria-label', collapsed ? 'Open sidebar' : 'Close sidebar' );
button.title = collapsed ? 'Open sidebar' : 'Close sidebar';
}
button.addEventListener( 'click', function () {
var collapsed = document.documentElement.classList.toggle( 'ew-sidebar-collapsed' );
try {
window.localStorage.setItem( sidebarKey, collapsed ? 'collapsed' : 'open' );
} catch ( error ) {
/* The button still works for this page when storage is blocked. */
}
updateButton();
} );
updateButton();
document.body.appendChild( button );
}
function installTopbarDropdowns() {
var topbar = document.getElementById( 'p-erindal-topbar' );
if ( !topbar || topbar.dataset.ewEnhanced === '1' ) {
return;
}
topbar.dataset.ewEnhanced = '1';
var menus = {
'Explore': [
[ 'Main Page', 'Main Page' ],
[ 'Special:RecentChanges', 'Recent Changes' ],
[ 'Special:AllPages', 'All Pages' ],
[ 'Special:Categories', 'Categories' ]
],
'World': [
[ 'Category:Nations and Realms', 'Nations & Realms' ],
[ 'Category:Ancestries and Cultures', 'Ancestries & Cultures' ],
[ 'Category:Locations', 'Locations' ]
],
'People': [
[ 'Category:Characters', 'Characters' ],
[ 'Category:Family Lines', 'Family Lines' ]
],
'Organizations': [
[ 'Category:Organizations', 'All Organizations' ],
[ 'The Conclave', 'The Conclave' ],
[ 'Order of the Holy Flame', 'Order of the Holy Flame' ],
[ 'The Dominion', 'The Dominion' ]
],
'Magic & Lore': [
[ 'Category:Religion and Deities', 'Religion & Deities' ],
[ 'Category:Magic and Cosmology', 'Magic & Cosmology' ]
],
'History': [
[ 'Category:History and Timeline', 'History & Timeline' ],
[ 'Chronology of Erindal', 'Timeline' ],
[ 'Calendar of Erindal', 'Calendar' ]
]
};
Array.prototype.forEach.call( topbar.querySelectorAll( '.mw-list-item' ), function ( item ) {
var link = item.querySelector( ':scope > a' );
if ( !link ) {
return;
}
var label = link.textContent.trim();
var entries = menus[ label ];
if ( !entries ) {
return;
}
item.classList.add( 'ew-has-dropdown' );
var caret = document.createElement( 'button' );
caret.type = 'button';
caret.className = 'ew-topbar-caret';
caret.textContent = '\u2304';
caret.setAttribute( 'aria-label', 'Open ' + label + ' menu' );
caret.setAttribute( 'aria-expanded', 'false' );
var dropdown = document.createElement( 'div' );
dropdown.className = 'ew-topbar-dropdown';
entries.forEach( function ( entry ) {
var menuLink = document.createElement( 'a' );
menuLink.href = mw.util.getUrl( entry[ 0 ] );
menuLink.textContent = entry[ 1 ];
dropdown.appendChild( menuLink );
} );
caret.addEventListener( 'click', function ( event ) {
event.preventDefault();
event.stopPropagation();
var willOpen = !item.classList.contains( 'ew-menu-open' );
Array.prototype.forEach.call( topbar.querySelectorAll( '.ew-menu-open' ), function ( openItem ) {
openItem.classList.remove( 'ew-menu-open' );
var openCaret = openItem.querySelector( '.ew-topbar-caret' );
if ( openCaret ) {
openCaret.setAttribute( 'aria-expanded', 'false' );
}
} );
item.classList.toggle( 'ew-menu-open', willOpen );
caret.setAttribute( 'aria-expanded', willOpen ? 'true' : 'false' );
} );
item.appendChild( caret );
item.appendChild( dropdown );
} );
document.addEventListener( 'click', function () {
Array.prototype.forEach.call( topbar.querySelectorAll( '.ew-menu-open' ), function ( item ) {
item.classList.remove( 'ew-menu-open' );
var caret = item.querySelector( '.ew-topbar-caret' );
if ( caret ) {
caret.setAttribute( 'aria-expanded', 'false' );
}
} );
} );
document.addEventListener( 'keydown', function ( event ) {
if ( event.key === 'Escape' ) {
Array.prototype.forEach.call( topbar.querySelectorAll( '.ew-menu-open' ), function ( item ) {
item.classList.remove( 'ew-menu-open' );
var caret = item.querySelector( '.ew-topbar-caret' );
if ( caret ) {
caret.setAttribute( 'aria-expanded', 'false' );
}
} );
}
} );
}
function makeHomepageCardsClickable() {
var destinations = {
'nations & realms': 'Category:Nations and Realms',
'ancestries & cultures': 'Category:Ancestries and Cultures',
'characters': 'Category:Characters',
'organizations': 'Category:Organizations',
'religion & deities': 'Category:Religion and Deities',
'magic & cosmology': 'Category:Magic and Cosmology',
'locations': 'Category:Locations',
'history & timeline': 'Category:History and Timeline',
'family lines': 'Category:Family Lines'
};
Array.prototype.forEach.call( document.querySelectorAll( '.ew-card' ), function ( card ) {
var link = card.querySelector( 'a[href]' );
var labelElement = card.querySelector( '.ew-card-label' ) || link;
var label = labelElement ? labelElement.textContent.trim().toLowerCase() : '';
var page = card.dataset.ewPage || destinations[ label ] || '';
var destination = link ? link.href : ( page ? mw.util.getUrl( page ) : '' );
if ( !destination || card.dataset.ewClickable === '1' ) {
return;
}
card.dataset.ewClickable = '1';
card.setAttribute( 'tabindex', '0' );
card.setAttribute( 'role', 'link' );
card.setAttribute( 'aria-label', 'Open ' + ( labelElement ? labelElement.textContent.trim() : 'category' ) );
card.addEventListener( 'click', function ( event ) {
if ( !event.target.closest( 'a' ) ) {
window.location.href = destination;
}
} );
card.addEventListener( 'keydown', function ( event ) {
if ( event.key === 'Enter' || event.key === ' ' ) {
event.preventDefault();
window.location.href = destination;
}
} );
} );
}
function installDeityVisualSwitches() {
Array.prototype.forEach.call( document.querySelectorAll( '.deity-visual' ), function ( visual ) {
if ( visual.dataset.ewEnhanced === '1' ) {
return;
}
var appearanceTab = visual.querySelector( '.deity-visual__tab--appearance' );
var symbolTab = visual.querySelector( '.deity-visual__tab--symbol' );
var appearancePanel = visual.querySelector( '.deity-visual__panel--appearance' );
var symbolPanel = visual.querySelector( '.deity-visual__panel--symbol' );
if ( !appearanceTab || !symbolTab || !appearancePanel || !symbolPanel ) {
return;
}
var tabs = [ appearanceTab, symbolTab ];
var panels = [ appearancePanel, symbolPanel ];
function selectView( selectedIndex ) {
tabs.forEach( function ( tab, index ) {
var active = index === selectedIndex;
tab.classList.toggle( 'is-active', active );
tab.setAttribute( 'aria-selected', active ? 'true' : 'false' );
tab.setAttribute( 'tabindex', active ? '0' : '-1' );
panels[ index ].classList.toggle( 'is-active', active );
panels[ index ].hidden = !active;
} );
}
tabs.forEach( function ( tab, index ) {
tab.setAttribute( 'role', 'tab' );
tab.addEventListener( 'click', function () {
selectView( index );
} );
tab.addEventListener( 'keydown', function ( event ) {
if ( event.key === 'Enter' || event.key === ' ' ) {
event.preventDefault();
selectView( index );
} else if ( event.key === 'ArrowLeft' || event.key === 'ArrowRight' ) {
event.preventDefault();
var nextIndex = event.key === 'ArrowRight' ? ( index + 1 ) % tabs.length : ( index + tabs.length - 1 ) % tabs.length;
selectView( nextIndex );
tabs[ nextIndex ].focus();
}
} );
} );
var tabList = visual.querySelector( '.deity-visual__tabs' );
if ( tabList ) {
tabList.setAttribute( 'role', 'tablist' );
tabList.setAttribute( 'aria-label', 'Deity appearance and holy symbol' );
}
visual.dataset.ewEnhanced = '1';
visual.classList.add( 'is-enhanced' );
selectView( 0 );
} );
}
function loadRecentChanges() {
var loading = document.querySelector( '.ew-recent-loading' );
if ( !loading ) {
return;
}
var api = new mw.Api();
api.get( {
action: 'query',
list: 'recentchanges',
rcnamespace: 0,
rcshow: '!bot',
rcprop: 'title|timestamp|user',
rctype: 'edit|new',
rclimit: 5,
formatversion: 2
} ).then( function ( data ) {
var changes = data && data.query && data.query.recentchanges ? data.query.recentchanges : [];
loading.textContent = '';
if ( !changes.length ) {
loading.textContent = 'No recent article changes.';
return;
}
changes.forEach( function ( change ) {
var row = document.createElement( 'div' );
row.className = 'ew-recent-row';
var icon = document.createElement( 'span' );
icon.className = 'ew-recent-icon';
icon.textContent = change.title.slice( 0, 2 ).toUpperCase();
var copy = document.createElement( 'span' );
var link = document.createElement( 'a' );
link.href = mw.util.getUrl( change.title );
link.textContent = change.title;
var detail = document.createElement( 'small' );
var date = new Date( change.timestamp );
detail.textContent = date.toLocaleDateString() + ( change.user ? ' - ' + change.user : '' );
copy.appendChild( link );
copy.appendChild( detail );
row.appendChild( icon );
row.appendChild( copy );
loading.appendChild( row );
} );
} ).catch( function () {
loading.textContent = 'Recent changes could not be loaded.';
} );
}
mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () {
onReady( function () {
installSidebarToggle();
installTopbarDropdowns();
makeHomepageCardsClickable();
installDeityVisualSwitches();
loadRecentChanges();
} );
} );
}() );