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.
/* The Story So Far: show chapters as facing pages, with tabs and arrows to turn between spreads. */
mw.hook( 'wikipage.content' ).add( function ( $content ) {
$content.find( '.story-book' ).each( function () {
var book = this;
if ( book.getAttribute( 'data-ready' ) ) {
return;
}
book.setAttribute( 'data-ready', '1' );
var pages = Array.prototype.filter.call( book.children, function ( el ) {
return el.classList.contains( 'story-box' );
} );
if ( !pages.length ) {
return;
}
// Group the chapters into spreads of two facing pages.
var spreads = [];
for ( var i = 0; i < pages.length; i += 2 ) {
var spread = document.createElement( 'div' );
spread.className = 'story-spread';
book.insertBefore( spread, pages[ i ] );
spread.appendChild( pages[ i ] );
if ( pages[ i + 1 ] ) {
spread.appendChild( pages[ i + 1 ] );
}
spreads.push( spread );
}
// Drop stray empty paragraphs the parser left between chapters.
Array.prototype.forEach.call( book.querySelectorAll( ':scope > p' ), function ( p ) {
if ( /^\s*$/.test( p.textContent ) ) {
p.remove();
}
} );
if ( spreads.length < 2 ) {
return;
}
function chapterNumber( page, fallback ) {
var title = page.querySelector( '.story-title' );
var m = title && title.textContent.match( /Chapter\s+([0-9IVXLC]+)/i );
return m ? m[ 1 ] : String( fallback );
}
var labels = spreads.map( function ( spread, n ) {
var first = chapterNumber( spread.children[ 0 ], n * 2 + 1 );
return spread.children[ 1 ] ?
'Chapters ' + first + '–' + chapterNumber( spread.children[ 1 ], n * 2 + 2 ) :
'Chapter ' + first;
} );
var current = 0;
var navs = [];
function makeButton( text, title, onClick ) {
var b = document.createElement( 'button' );
b.type = 'button';
b.textContent = text;
if ( title ) {
b.title = title;
}
b.addEventListener( 'click', onClick );
return b;
}
function makeNav() {
var nav = document.createElement( 'div' );
nav.className = 'story-nav';
var tabs = labels.map( function ( label, n ) {
var tab = makeButton( label, null, function () {
show( n, nav );
} );
tab.setAttribute( 'role', 'tab' );
nav.appendChild( tab );
return tab;
} );
var arrows = document.createElement( 'span' );
arrows.className = 'story-arrows';
var prev = makeButton( '◂ Previous', null, function () {
show( current - 1, nav );
} );
var next = makeButton( 'Next ▸', null, function () {
show( current + 1, nav );
} );
arrows.appendChild( prev );
arrows.appendChild( next );
nav.appendChild( arrows );
navs.push( { tabs: tabs, prev: prev, next: next } );
return nav;
}
function show( n, fromNav ) {
if ( n < 0 || n >= spreads.length ) {
return;
}
current = n;
spreads.forEach( function ( spread, k ) {
spread.hidden = k !== n;
} );
navs.forEach( function ( nav ) {
nav.tabs.forEach( function ( tab, k ) {
tab.setAttribute( 'aria-selected', k === n ? 'true' : 'false' );
} );
nav.prev.disabled = n === 0;
nav.next.disabled = n === spreads.length - 1;
} );
// Turning the page from the bottom bar jumps back up to the top of the book.
if ( fromNav && fromNav === bottomNav && book.getBoundingClientRect().top < 0 ) {
book.scrollIntoView( { behavior: 'smooth' } );
}
}
var topNav = makeNav();
var bottomNav = makeNav();
book.insertBefore( topNav, book.firstChild );
book.appendChild( bottomNav );
show( 0 );
} );
} );