MediaWiki:Gadget-codexTabs.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.
'use strict';
const Vue = require( 'vue' );
const App = require( './codexTabs.vue' );
function makeSafeName( label, index ) {
return String( label || 'tab-' + index )
.toLowerCase()
.replace( /[^a-z0-9_-]+/g, '-' )
.replace( /^-+|-+$/g, '' ) || 'tab-' + index;
}
function getHeadingLabel( heading ) {
const clone = heading.cloneNode( true );
// Remove MediaWiki's [edit] links from the heading label.
Array.prototype.forEach.call(
clone.querySelectorAll( '.mw-editsection, .mw-editsection-like' ),
function ( node ) {
node.remove();
}
);
const headline = clone.querySelector( '.mw-headline' );
return ( headline ? headline.textContent : clone.textContent ).trim();
}
function isTopLevelTabHeading( node ) {
return node &&
node.nodeType === Node.ELEMENT_NODE &&
node.tagName &&
node.tagName.toLowerCase() === 'h2';
}
function collectSectionTabs( wrapper ) {
const headings = Array.prototype.slice.call(
wrapper.querySelectorAll( 'h2' )
).filter( function ( heading ) {
// Ignore the page/table-of-contents heading.
if ( heading.id === 'mw-toc-heading' ) {
return false;
}
if ( heading.closest( '#toc, .toc' ) ) {
return false;
}
// Only use h2 headings that are actually inside this tab wrapper.
return wrapper.contains( heading );
} );
if ( headings.length < 2 ) {
console.warn(
'codexTabs: expected at least 2 h2 headings inside .codex-tabs-sections, found',
headings.length,
headings,
wrapper
);
return [];
}
function getTopLevelNodeInsideWrapper( node ) {
let current = node;
while ( current.parentNode && current.parentNode !== wrapper ) {
current = current.parentNode;
}
return current;
}
function getHeadingLabel( heading ) {
const clone = heading.cloneNode( true );
Array.prototype.forEach.call(
clone.querySelectorAll( '.mw-editsection, .mw-editsection-like' ),
function ( node ) {
node.remove();
}
);
return clone.textContent.trim();
}
return headings.map( function ( heading, index ) {
const label = getHeadingLabel( heading );
const startNode = getTopLevelNodeInsideWrapper( heading );
const nextHeading = headings[ index + 1 ];
const endNode = nextHeading ?
getTopLevelNodeInsideWrapper( nextHeading ) :
null;
const container = document.createElement( 'div' );
let node = startNode.nextSibling;
while ( node && node !== endNode ) {
container.appendChild( node.cloneNode( true ) );
node = node.nextSibling;
}
return {
name: makeSafeName( label, index ) + '-' + index,
label: label,
html: container.innerHTML.trim()
};
} ).filter( function ( tab ) {
return tab.label;
} );
}
function mountOne( wrapper, index ) {
const tabs = collectSectionTabs( wrapper );
if ( tabs.length < 2 ) {
// Helpful debugging message.
// eslint-disable-next-line no-console
console.warn(
'codexTabs: expected at least 2 == headings inside .codex-tabs-sections, found',
tabs.length,
wrapper
);
return;
}
const framed = wrapper.getAttribute( 'data-framed' ) !== 'false';
const activeLabel = wrapper.getAttribute( 'data-active-label' );
let active = tabs[ 0 ].name;
if ( activeLabel ) {
tabs.forEach( function ( tab ) {
if ( tab.label === activeLabel ) {
active = tab.name;
}
} );
}
const mountPoint = document.createElement( 'div' );
mountPoint.className = 'codex-tabs-gadget';
mountPoint.id = 'codex-tabs-gadget-' + index;
wrapper.parentNode.replaceChild( mountPoint, wrapper );
Vue.createMwApp( App, {
tabs: tabs,
framed: framed,
active: active
} ).mount( mountPoint );
}
function initCodexSectionTabs( $content ) {
const root = $content && $content[ 0 ] ? $content[ 0 ] : document;
const wrappers = Array.prototype.slice.call(
root.querySelectorAll( '.codex-tabs-sections' )
);
wrappers.forEach( function ( wrapper, index ) {
if ( wrapper.classList.contains( 'codex-tabs--mounted' ) ) {
return;
}
wrapper.classList.add( 'codex-tabs--mounted' );
mountOne( wrapper, index );
} );
}
mw.hook( 'wikipage.content' ).add( initCodexSectionTabs );
$( function () {
initCodexSectionTabs( $( document.body ) );
} );