MediaWiki:Vector-2022.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.
/* All JavaScript here will be loaded for users of the Vector skin */
/* Second logo in the header, linking to www.linn.co.uk */
mw.loader.using( 'mediawiki.util' ).then( function () {
var start = document.querySelector( '.vector-header-start' );
if ( !start || document.getElementById( 'linn-site-logo' ) ) {
return;
}
var link = document.createElement( 'a' );
link.id = 'linn-site-logo';
link.href = 'https://www.linn.co.uk/';
link.title = 'Linn – www.linn.co.uk';
link.target = '_blank';
link.rel = 'noopener';
var img = document.createElement( 'img' );
img.src = mw.config.get( 'wgScriptPath' ) + '/images/Linn_Logo.png';
img.alt = 'Linn';
link.appendChild( img );
start.appendChild( link );
} );
/* In-page table of contents for Vector 2022 (in addition to the sidebar) */
$( function () {
var content = document.querySelector( '#mw-content-text .mw-parser-output' );
// Pages can opt out of the in-text TOC with <span class="no-inline-toc"></span>
if ( content.querySelector( '.no-inline-toc' ) ) {
return;
}
// MediaWiki leaves this marker where the TOC belongs; none means no TOC
// (e.g. __NOTOC__, or fewer than four headings without __FORCETOC__)
var marker = content.querySelector( 'meta[property="mw:PageProp/toc"]' );
if ( !marker ) {
return;
}
var headings = content.querySelectorAll( 'h2[id], h3[id], h4[id], h5[id], h6[id]' );
if ( !headings.length ) {
return;
}
var levels = Array.prototype.map.call( headings, function ( h ) {
return Number( h.tagName.charAt( 1 ) );
} );
var base = Math.min.apply( null, levels );
var root = document.createElement( 'ul' );
var stack = [ root ];
var nums = [];
Array.prototype.forEach.call( headings, function ( h, i ) {
var depth = levels[ i ] - base;
while ( stack.length - 1 > depth ) {
stack.pop();
}
while ( stack.length - 1 < depth ) {
var parent = stack[ stack.length - 1 ];
var lastLi = parent.lastElementChild || parent.appendChild( document.createElement( 'li' ) );
stack.push( lastLi.appendChild( document.createElement( 'ul' ) ) );
}
nums.length = depth + 1;
nums[ depth ] = ( nums[ depth ] || 0 ) + 1;
var li = document.createElement( 'li' );
li.className = 'toclevel-' + ( depth + 1 );
var a = document.createElement( 'a' );
a.href = '#' + h.id;
var num = document.createElement( 'span' );
num.className = 'tocnumber';
num.textContent = nums.map( function ( n ) { return n || 1; } ).join( '.' );
var text = document.createElement( 'span' );
text.className = 'toctext';
text.textContent = h.textContent.trim();
a.appendChild( num );
a.appendChild( document.createTextNode( ' ' ) );
a.appendChild( text );
li.appendChild( a );
stack[ stack.length - 1 ].appendChild( li );
} );
var toc = document.createElement( 'div' );
toc.id = 'toc';
toc.className = 'toc';
toc.setAttribute( 'role', 'navigation' );
var title = document.createElement( 'div' );
title.className = 'toctitle';
title.textContent = 'Contents';
toc.appendChild( title );
toc.appendChild( root );
marker.parentNode.replaceChild( toc, marker );
} );
/* Quick guide: first-visit overlay, tooltips and "?" button (Vector 2022) */
mw.loader.using( 'mediawiki.cookie' ).then( function () {
var COOKIE = 'linnHelpSeen';
var steps = [
{
sel: [ '#vector-main-menu-dropdown', '#vector-main-menu-pinned-container' ],
title: 'Main menu',
text: 'Site navigation. Choose "move to sidebar" to keep it open on the left, or "hide" to tuck it back into this button.'
},
{
sel: [ '#vector-page-titlebar-toc', '#vector-toc-pinned-container' ],
title: 'Contents',
text: 'Jump to any section of this page. It can sit in the sidebar or be collapsed into this button.'
},
{
sel: [ '#vector-appearance-dropdown', '#vector-appearance-pinned-container' ],
title: 'Appearance',
text: 'Change the text size and page width.'
}
];
// First matching element that is actually visible on screen
function findVisible( selectors ) {
for ( var i = 0; i < selectors.length; i++ ) {
var el = document.querySelector( selectors[ i ] );
if ( el ) {
var r = el.getBoundingClientRect();
if ( r.width && r.height ) {
return el;
}
}
}
return null;
}
// 1. Hover tooltips on the header buttons
steps.forEach( function ( s ) {
var el = document.querySelector( s.sel[ 0 ] );
if ( el ) {
el.title = s.title + ': ' + s.text;
}
} );
function closeTour() {
var old = document.getElementById( 'linn-tour' );
if ( old ) {
old.parentNode.removeChild( old );
}
document.removeEventListener( 'keydown', onKey );
}
function onKey( e ) {
if ( e.key === 'Escape' ) {
finishTour();
}
}
function finishTour() {
mw.cookie.set( COOKIE, '1', { expires: 365 * 24 * 60 * 60 } ); // one year
closeTour();
}
// 2. The overlay
function showTour() {
closeTour();
window.scrollTo( 0, 0 );
var overlay = document.createElement( 'div' );
overlay.id = 'linn-tour';
overlay.addEventListener( 'click', finishTour );
document.body.appendChild( overlay );
var placed = [];
steps.forEach( function ( s,i ) {
var el = findVisible( s.sel );
if ( !el ) {
return;
}
var r = el.getBoundingClientRect();
var ring = document.createElement( 'div' );
ring.className = 'linn-tour-ring';
ring.setAttribute( 'data-step', i + 1 )
ring.style.left = ( r.left - 4 ) + 'px';
ring.style.top = ( r.top - 4 ) + 'px';
ring.style.width = ( r.width + 8 ) + 'px';
ring.style.height = ( r.height + 8 ) + 'px';
overlay.appendChild( ring );
var tip = document.createElement( 'div' );
tip.className = 'linn-tour-tip';
var h = document.createElement( 'strong' );
h.textContent = ( i + 1 ) + '. ' + s.title;
tip.appendChild( h );
tip.appendChild( document.createTextNode( s.text ) );
if ( r.height > 100 ) {
// Tall sidebar panel: put the tip to its right
tip.style.left = ( r.right + 14 ) + 'px';
tip.style.top = ( r.top + 10 ) + 'px';
} else {
// Header button: put the tip below it
tip.style.top = ( r.bottom + 14 ) + 'px';
if ( r.left > window.innerWidth - 300 ) {
tip.style.right = ( window.innerWidth - r.right ) + 'px';
} else {
tip.style.left = r.left + 'px';
}
}
overlay.appendChild( tip );
// Move this tip down until it no longer overlaps an earlier one
var moved = true;
while ( moved ) {
moved = false;
var tr = tip.getBoundingClientRect();
for ( var j = 0; j < placed.length; j++ ) {
var p = placed[ j ];
if ( tr.left < p.right && tr.right > p.left &&
tr.top < p.bottom && tr.bottom > p.top ) {
tip.style.top = ( p.bottom + 10 ) + 'px';
moved = true;
break;
}
}
}
placed.push( tip.getBoundingClientRect() );
} );
var panel = document.createElement( 'div' );
panel.className = 'linn-tour-panel';
var heading = document.createElement( 'strong' );
heading.textContent = 'Welcome to LinnDocs';
var intro = document.createElement( 'p' );
intro.textContent = 'Here’s a quick guide to the buttons at the top of the page. ' +
'You can see this again at any time with the ? button.';
var btn = document.createElement( 'button' );
btn.type = 'button';
btn.textContent = 'Got it';
panel.appendChild( heading );
panel.appendChild( intro );
panel.appendChild( btn );
overlay.appendChild( panel );
document.addEventListener( 'keydown', onKey );
btn.focus();
}
// 3. "?" button in the header to reopen the guide
var end = document.querySelector( '.vector-header-end' );
if ( end && !document.getElementById( 'linn-help-btn' ) ) {
var helpBtn = document.createElement( 'button' );
helpBtn.id = 'linn-help-btn';
helpBtn.type = 'button';
helpBtn.textContent = '?';
helpBtn.title = 'Show the quick guide';
helpBtn.addEventListener( 'click', showTour );
end.insertBefore( helpBtn, end.firstChild );
}
// Show automatically on the first visit (short delay lets Vector finish its layout)
if ( !mw.cookie.get( COOKIE ) ) {
setTimeout( showTour, 800 );
}
} );