MediaWiki:Gadget-switch-tabber.js
外观
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
( function ( window, $, mw ) {
'use strict';
var SWITCH_TABBER_ID = 'SwitchTabber';
var COOKIE_NAME = 'SwitchTab';
var TABBER_HEAD_CLASS = 'tabberex-head oo-ui-widget oo-ui-widget-enabled oo-ui-selectWidget oo-ui-selectWidget-unpressed oo-ui-selectWidget-depressed oo-ui-tabSelectWidget';
var TAB_CLASS = 'oo-ui-widget oo-ui-widget-enabled oo-ui-labelElement oo-ui-optionWidget oo-ui-tabOptionWidget';
var LABEL_CLASS = 'oo-ui-labelElement-label';
var PLATFORM_LABELS = {
mobile: {
hans: '移动版',
hant: '行動版'
},
switch: {
hans: 'Switch',
hant: 'Switch'
}
};
function attrSelector( value ) {
return '[data-tabber-id="' + String( value ).replace( /\\/g, '\\\\' ).replace( /"/g, '\\"' ) + '"]';
}
function isTraditionalVariant() {
var variant = String(
mw.config.get( 'wgUserVariant' ) ||
mw.config.get( 'wgUserLanguage' ) ||
mw.config.get( 'wgContentLanguage' ) ||
''
).toLowerCase();
return /(?:hant|tw|hk|mo)/.test( variant );
}
function getPlatformLabel( key ) {
var labels = PLATFORM_LABELS[ key ];
return isTraditionalVariant() ? labels.hant : labels.hans;
}
function getTabberElements( tabberId, type ) {
return $( '.tabberex-' + type + attrSelector( tabberId ) );
}
function tabberexShowTab( tabberId, index ) {
getTabberElements( tabberId, 'head' ).children( ':nth-child(' + index + ')' )
.addClass( 'oo-ui-optionWidget-selected' )
.css( 'cursor', 'default' );
getTabberElements( tabberId, 'body' ).children( ':nth-child(' + index + ')' ).each( function () {
var displayValue = this.getAttribute( 'data-tab-display' ) || 'block';
this.style.display = displayValue;
} );
}
function tabberexHideTab( tabberId, index ) {
getTabberElements( tabberId, 'head' ).children( ':nth-child(' + index + ')' )
.removeClass( 'oo-ui-optionWidget-selected' )
.css( 'cursor', 'pointer' );
getTabberElements( tabberId, 'body' ).children( ':nth-child(' + index + ')' ).css( 'display', 'none' );
}
function tabberexHideAllTabs( tabberId ) {
getTabberElements( tabberId, 'head' ).children()
.removeClass( 'oo-ui-optionWidget-selected' )
.css( 'cursor', 'pointer' );
getTabberElements( tabberId, 'body' ).children().css( 'display', 'none' );
}
function headingHasContentForTab( heading, index ) {
var clone = heading.cloneNode( true );
Array.prototype.forEach.call(
clone.querySelectorAll( '.tabberex-body' + attrSelector( SWITCH_TABBER_ID ) ),
function ( body ) {
Array.prototype.slice.call( body.children ).forEach( function ( tab, tabIndex ) {
if ( tabIndex !== index - 1 ) {
tab.parentNode.removeChild( tab );
}
} );
}
);
Array.prototype.forEach.call( clone.querySelectorAll( '.mobileonly' ), function ( label ) {
label.parentNode.removeChild( label );
} );
return clone.textContent.trim() !== '' ||
!!clone.querySelector( 'img, svg, math, video, audio, canvas' );
}
function updateSwitchHeadingVisibility( index ) {
Array.prototype.forEach.call(
document.querySelectorAll( 'h1, h2, h3, h4, h5, h6' ),
function ( heading ) {
var wrapper;
if ( !heading.querySelector( '.tabberex-body' + attrSelector( SWITCH_TABBER_ID ) ) ) {
return;
}
wrapper = heading.closest( '.mw-heading' ) || heading;
wrapper.hidden = !headingHasContentForTab( heading, index );
}
);
}
function getSwitchHeadingText( heading, index ) {
var clone = heading.cloneNode( true );
Array.prototype.forEach.call(
clone.querySelectorAll( '.tabberex-body' + attrSelector( SWITCH_TABBER_ID ) ),
function ( body ) {
Array.prototype.slice.call( body.children ).forEach( function ( tab, tabIndex ) {
if ( tabIndex !== index - 1 ) {
body.removeChild( tab );
}
} );
}
);
Array.prototype.forEach.call(
clone.querySelectorAll( '.mobileonly, .mw-editsection' ),
function ( element ) {
element.parentNode.removeChild( element );
}
);
return clone.textContent.replace( /\s+/g, ' ' ).trim();
}
function getSwitchHeadingFromTocLink( link ) {
var fragment = link.hash ? link.hash.slice( 1 ) : '';
var id;
if ( !fragment ) {
return null;
}
try {
id = decodeURIComponent( fragment );
} catch ( e ) {
return null;
}
return document.getElementById( id ) || document.getElementById( id.replace( /_/g, ' ' ) );
}
function updateSwitchTableOfContents( index ) {
Array.prototype.forEach.call(
document.querySelectorAll( '#toc a[href*="#"], .toc a[href*="#"], .vector-toc a[href*="#"], .mw-toc a[href*="#"]' ),
function ( link ) {
var heading = getSwitchHeadingFromTocLink( link );
var wrapper;
var item;
if ( !heading || !heading.querySelector( '.tabberex-body' + attrSelector( SWITCH_TABBER_ID ) ) ) {
return;
}
wrapper = heading.closest( '.mw-heading' ) || heading;
item = link.closest( 'li' );
if ( item ) {
item.hidden = wrapper.hidden;
}
link.textContent = getSwitchHeadingText( heading, index );
}
);
}
function tabberexSwitchTab( tabberId, index ) {
tabberexHideAllTabs( tabberId );
tabberexShowTab( tabberId, index );
if ( tabberId === SWITCH_TABBER_ID ) {
updateSwitchHeadingVisibility( index );
updateSwitchTableOfContents( index );
}
}
function getExHeader( element ) {
var attributes = {};
var names = element.getAttributeNames();
var i;
for ( i = 0; i < names.length; i++ ) {
attributes[ names[ i ] ] = element.getAttribute( names[ i ] );
}
return {
html: element.innerHTML,
attributes: attributes
};
}
function readTabDetails( tabElement, index ) {
var exHeaderElement = tabElement.querySelector( 'p > span.tabberex-tab-header, span.tabberex-tab-header' );
var details = {
header: tabElement.getAttribute( 'data-tab-header' ) || String( index + 1 ),
exHeader: exHeaderElement ? getExHeader( exHeaderElement ) : null,
isDefault: tabElement.classList.contains( 'tabberex-tab-default' ),
location: tabElement.getAttribute( 'data-tab-location' )
};
if ( exHeaderElement ) {
exHeaderElement.parentNode.removeChild( exHeaderElement );
}
return details;
}
function makeTabButton( tabberId, details, index ) {
var tab = document.createElement( 'div' );
var label = document.createElement( 'span' );
var attr;
tab.className = TAB_CLASS;
tab.style.cursor = 'pointer';
label.className = LABEL_CLASS;
if ( details.exHeader ) {
for ( attr in details.exHeader.attributes ) {
label.setAttribute( attr, details.exHeader.attributes[ attr ] );
}
label.innerHTML = details.exHeader.html;
} else {
label.textContent = details.header;
}
tab.appendChild( label );
tab.addEventListener( 'click', function () {
if ( details.location ) {
window.location.href = details.location;
return;
}
tabberexSwitchTab( tabberId, index + 1 );
} );
return tab;
}
function buildTabHead( host, tabberId, tabDetails ) {
var i;
host.innerHTML = '';
host.className = TABBER_HEAD_CLASS;
host.setAttribute( 'data-tabber-id', tabberId );
for ( i = 0; i < tabDetails.length; i++ ) {
host.appendChild( makeTabButton( tabberId, tabDetails[ i ], i ) );
}
}
function processHead( head, defaultTabs, tabberIds, tabberIndex ) {
var tabberId = head.getAttribute( 'data-tabber-id' ) || String( tabberIndex );
var details = [];
var defaultIndex = 0;
if ( head.dataset.tabberexProcessed || !head.querySelector( '.tabberex-tab' ) ) {
return tabberIndex;
}
Array.prototype.forEach.call( head.querySelectorAll( ':scope > .tabberex-tab' ), function ( tab, index ) {
var item = readTabDetails( tab, index );
details.push( item );
if ( item.isDefault ) {
defaultIndex = index;
}
} );
head.dataset.tabberexProcessed = '1';
buildTabHead( head, tabberId, details );
if ( defaultTabs[ tabberId ] === undefined ) {
defaultTabs[ tabberId ] = defaultIndex;
}
tabberIds.push( tabberId );
return tabberIndex + 1;
}
function processStandaloneTabber( tabber, defaultTabs, tabberIds, tabberIndex ) {
var tabberId = tabber.getAttribute( 'data-tabber-id' ) || String( tabberIndex );
var head = document.createElement( 'div' );
var details = [];
var defaultIndex = 0;
if ( tabber.dataset.tabberexProcessed ) {
return tabberIndex;
}
Array.prototype.forEach.call( tabber.querySelectorAll( ':scope > .tabberex-tab' ), function ( tab, index ) {
var item = readTabDetails( tab, index );
details.push( item );
if ( item.isDefault ) {
defaultIndex = index;
}
tab.style.display = 'none';
} );
tabber.dataset.tabberexProcessed = '1';
tabber.setAttribute( 'data-tabber-id', tabberId );
tabber.classList.remove( 'tabberex' );
tabber.classList.add( 'tabberex-body' );
buildTabHead( head, tabberId, details );
tabber.parentNode.insertBefore( head, tabber );
if ( defaultTabs[ tabberId ] === undefined ) {
defaultTabs[ tabberId ] = defaultIndex;
}
tabberIds.push( tabberId );
return tabberIndex + 1;
}
function processTabbers() {
var defaultTabs = {};
var tabberIds = [];
var tabberIndex = 0;
Array.prototype.slice.call( document.querySelectorAll( '.tabberex-head' ) ).reverse().forEach( function ( head ) {
tabberIndex = processHead( head, defaultTabs, tabberIds, tabberIndex );
} );
Array.prototype.slice.call( document.querySelectorAll( '.tabberex-body' ) ).reverse().forEach( function ( body ) {
$( body ).children().css( 'display', 'none' );
} );
Array.prototype.slice.call( document.querySelectorAll( '.tabberex' ) ).reverse().forEach( function ( tabber ) {
tabberIndex = processStandaloneTabber( tabber, defaultTabs, tabberIds, tabberIndex );
} );
tabberIds.forEach( function ( tabberId ) {
tabberexShowTab( tabberId, defaultTabs[ tabberId ] + 1 );
} );
}
function getFileUrl( filename ) {
return mw.util.getUrl( 'Special:FilePath/' + filename );
}
function setIconMask( icon, filename ) {
var url = getFileUrl( filename );
icon.style.webkitMaskImage = 'url("' + url + '")';
icon.style.maskImage = 'url("' + url + '")';
}
function applySwitchTabberIconMasks( $content ) {
var root = $content && $content.length ? $content[ 0 ] : document;
$( root ).find( '.tabberex-head' + attrSelector( SWITCH_TABBER_ID ) + ' .switchtabber-icon--mobile' ).each( function () {
setIconMask( this, 'SwitchTabber_Mobile.png' );
} );
$( root ).find( '.tabberex-head' + attrSelector( SWITCH_TABBER_ID ) + ' .switchtabber-icon--switch' ).each( function () {
setIconMask( this, 'SwitchTabber_Switch.png' );
} );
}
function makeSwitchTab( index, labelText, filename, extraIconClass ) {
var tab = document.createElement( 'div' );
var label = document.createElement( 'span' );
var icon = document.createElement( 'span' );
tab.className = TAB_CLASS;
tab.style.cursor = 'pointer';
label.className = LABEL_CLASS;
icon.className = 'switchtabber-icon' + ( extraIconClass ? ' ' + extraIconClass : '' );
icon.setAttribute( 'aria-hidden', 'true' );
setIconMask( icon, filename );
label.appendChild( icon );
label.appendChild( document.createTextNode( labelText ) );
tab.appendChild( label );
tab.addEventListener( 'click', function () {
switchtabberSwitchTab( index );
} );
return tab;
}
function createSwitchTabber( modifierClass ) {
var switchTabber = document.createElement( 'div' );
switchTabber.className = TABBER_HEAD_CLASS + ' switchtabber-head' + ( modifierClass ? ' ' + modifierClass : '' );
switchTabber.setAttribute( 'data-tabber-id', SWITCH_TABBER_ID );
switchTabber.dataset.switchtabberGenerated = '1';
switchTabber.appendChild( makeSwitchTab( 1, getPlatformLabel( 'mobile' ), 'SwitchTabber_Mobile.png', 'switchtabber-icon--mobile' ) );
switchTabber.appendChild( makeSwitchTab( 2, getPlatformLabel( 'switch' ), 'SwitchTabber_Switch.png', 'switchtabber-icon--switch' ) );
return switchTabber;
}
function getSelectedSwitchTab() {
var match = document.cookie.match( /(?:^|;\s*)SwitchTab=(Mobile|Switch)(?:;|$)/ );
return match && match[ 1 ] === 'Switch' ? 2 : 1;
}
function setSelectedSwitchTab( index ) {
document.cookie = COOKIE_NAME + '=' + ( index === 1 ? 'Mobile' : 'Switch' ) + '; path=/; SameSite=Lax';
}
function switchtabberSwitchTab( index ) {
tabberexSwitchTab( SWITCH_TABBER_ID, index );
setSelectedSwitchTab( index );
}
function hasSwitchTabberContent() {
return !!document.querySelector(
'.tabberex-body' + attrSelector( SWITCH_TABBER_ID ) + ', .tabberex' + attrSelector( SWITCH_TABBER_ID ) + ', .tabberex-head' + attrSelector( SWITCH_TABBER_ID ) + ':not([data-switchtabber-generated])'
);
}
function addLocalPageHeaderTabber() {
var heading = document.getElementById( 'firstHeading' ) || document.querySelector( '.mw-first-heading' );
var wrapper;
if ( !heading || document.querySelector( '.switchtabber-local-heading > .switchtabber-head' ) ) {
return;
}
wrapper = document.createElement( 'div' );
wrapper.className = 'switchtabber-local-heading';
heading.parentNode.insertBefore( wrapper, heading );
wrapper.appendChild( heading );
wrapper.appendChild( createSwitchTabber( 'switchtabber-head--page' ) );
}
function addFandomPageHeaderTabber() {
if ( document.querySelector( '.page-header__title-wrapper > .switchtabber-head' ) ) {
return;
}
if ( $( '.page-header__title-wrapper' ).length ) {
$( '.page-header__title-wrapper' )
.css( 'display', 'flex' )
.css( 'align-content', 'center' )
.css( 'justify-content', 'space-between' )
.append( createSwitchTabber( 'switchtabber-head--page' ) );
} else {
addLocalPageHeaderTabber();
}
}
function addFandomStickyHeaderTabber() {
if ( !$( '.fandom-sticky-header > :first-child' ).length || document.querySelector( '.fandom-sticky-header .switchtabber-head' ) ) {
return;
}
$( createSwitchTabber( 'switchtabber-head--sticky' ) ).insertAfter( '.fandom-sticky-header > :first-child' );
}
function addEditorPreviewTabber() {
if ( !$( '.oo-ui-processDialog-actions-safe' ).length || document.querySelector( '.oo-ui-processDialog-actions-safe .switchtabber-head' ) ) {
return;
}
$( '.oo-ui-processDialog-actions-safe' )
.css( 'flex-direction', 'row-reverse' )
.append( createSwitchTabber( 'switchtabber-head--editor' ) );
}
function addSwitchTabber() {
var initialTab;
if ( !hasSwitchTabberContent() ) {
return;
}
addFandomPageHeaderTabber();
addFandomStickyHeaderTabber();
addEditorPreviewTabber();
applySwitchTabberIconMasks( $( document ) );
initialTab = getSelectedSwitchTab();
tabberexSwitchTab( SWITCH_TABBER_ID, initialTab );
}
window.tabberex_showTab = tabberexShowTab;
window.tabberex_hideTab = tabberexHideTab;
window.tabberex_hideAllTabs = tabberexHideAllTabs;
window.tabberex_switchTab = tabberexSwitchTab;
window.switchtabber_switchTab = switchtabberSwitchTab;
mw.hook( 'wikipage.content' ).add( processTabbers );
mw.hook( 'wikipage.content' ).add( addSwitchTabber );
}( this, jQuery, mediaWiki ) );