MediaWiki:Gadget-CollapsibleTOC.js
Nota: Después de publicar, quizás necesite actualizar la caché de su navegador para ver los cambios.
- Firefox/Safari: Mantenga presionada la tecla Shift mientras pulsa el botón Actualizar, o presiona Ctrl+F5 o Ctrl+R (⌘+R en Mac)
- Google Chrome: presione Ctrl+Shift+R (⌘+Shift+R en Mac)
- Internet Explorer/Edge: mantenga presionada Ctrl mientras pulsa Actualizar, o presione Ctrl+F5
- Opera: Presiona Ctrl+F5.
/**
* Índice que por defecto solo muestra los títulos de nivel 1 (con el objetivo
* de no congestionar demasiado la pantalla), mientras que los niveles inferiores
* se muestran clicando en el botón [+] (o [-]), de una manera parecida a la que
* lo hacen los navegadores de ficheros clásicos.
*
* Adaptado de [[:fr:MediaWiki:Gadget-SommaireDeveloppable.js]].
*
* Autores del programa original:
* * Eiku 25 abril 2012 para el código base.
* * ArséniureDeGallium para los detalles y los diversos añadidos.
*
* Inspirado por WikiTravel (http://files.wikitravel.org/mw/skins/common/wikibits.js?0.1),
* pero realizado de una manera diferente.
*
* Véase también /resources/src/mediawiki/mediawiki.toc.js de mediawiki/core.
*
* Traducción de [[Usuario:Alakrano]]
* Adaptación para Wikcionario y posterior rediseño: [[Usuario:Peter Bowman]]
*/
// Botones para mostrar/ocultar todas las secciones
var $developAll, $envelopAll;
// Contador de secciones plegadas (excluyendo las que no tienen subsecciones)
var collapsedCount = 0;
// Número total de secciones (excluyendo las que no tienen subsecciones)
var totalCount = 0;
// Contador de secciones de primer nivel plegadas (excluyendo las que no tienen subsecciones)
var collapsedfirstLevelCount = 0;
// Número total de secciones de primer nivel (excluyendo las que no tienen subsecciones)
var totalFirstLevelCount = 0;
// Enumeración que define la acción realizada por el usuario
var OPS = {
INIT: 0,
CLEAR: 1,
FILL: 2,
INCR: 3,
DECR: 4
};
var config = {
labels: {
'collapsibleToc-develop': '►',
'collapsibleToc-envelop': '▼',
'collapsibleToc-empty': '►',
'collapsibleToc-develop-all': '►',
'collapsibleToc-envelop-all': '▼'
},
messages: {
'collapsibleToc-develop': 'Esta sección contiene secciones de nivel inferior, haz clic para verlas',
'collapsibleToc-envelop': 'Haz clic para ocultar las secciones de nivel inferior',
'collapsibleToc-empty': 'Esta sección no contiene secciones de nivel inferior',
'collapsibleToc-develop-all': 'Haz clic para mostrar todas las secciones',
'collapsibleToc-envelop-all': 'Haz clic para ocultar todas las secciones',
'collapsibleToc-develop-all-empty': 'Todas las secciones han sido desplegadas',
'collapsibleToc-envelop-all-empty': 'Todas las secciones visibles han sido plegadas'
}
};
mw.config.set( config.labels );
mw.messages.set( config.messages );
$( function () {
var $toc = $( '#toc' );
// Itera sobre cada elemento <li> de la tabla de contenido
$toc.find( 'li' ).each( doCollapsible );
// Elimina la numeración de los encabezamientos
if ( !mw.user.options.get( 'numberheadings' ) ) {
$toc.find( 'li > a' ).each( function () {
// La estructura HTML es <span>numeración</span> <span>título</span>.
// No basta con un display: none; para el primer <span>, nótese el espacio
// entre medias.
var $this = $( this );
var $tocText = $this.find( '.toctext' ).detach();
$this.empty().append( $tocText );
} );
}
if ( totalCount > 1 ) {
// Añade los botones para mostrar/ocultar todo
addToggleButtons( $toc );
// Pliega por defecto los elementos de nivel 1 a excepción del primero
$toc.find( '.toclevel-1 > [data-collapsible-toc-state]' ).each( function ( i ) {
if ( i !== 0 ) {
$( this ).trigger( 'click' );
}
} );
}
} );
// Callback para cada iteración del bucle .each()
function doCollapsible( i, li ) {
var $li = $( li ), $ul = $li.children( 'ul' ), $button = $( '<span>' ),
isFirstLevel = $li.hasClass( 'toclevel-1' );
// Comprueba si existe una lista de nivel inferior dentro de la actual
if ( $ul.length ) {
$button
.css( 'color', '#0645AD' )
.attr( {
title: mw.msg( 'collapsibleToc-envelop' ),
'data-collapsible-toc-state': 'expanded'
} )
.text( config.labels[ 'collapsibleToc-envelop' ] )
.on( 'click', function () {
if ( $button.attr( 'data-collapsible-toc-state' ) === 'collapsed' ) {
$ul.show();
$button
.attr( {
title: mw.msg( 'collapsibleToc-envelop' ),
'data-collapsible-toc-state': 'expanded'
} )
.text( config.labels[ 'collapsibleToc-envelop' ] );
checkAndUpdateState( OPS.DECR, isFirstLevel );
} else {
$ul.hide();
$button
.attr( {
title: mw.msg( 'collapsibleToc-develop' ),
'data-collapsible-toc-state': 'collapsed'
} )
.text( config.labels[ 'collapsibleToc-develop' ] );
checkAndUpdateState( OPS.INCR, isFirstLevel );
}
} );
// Incrementa el contador de secciones
totalCount++;
if ( isFirstLevel ) {
totalFirstLevelCount++;
}
} else {
// No hay más subniveles
$button
.css( 'color', '#C0C0C0' )
.css( 'cursor', 'default' ) // TODO: trasladar a CSS
.attr( 'title', mw.msg( 'collapsibleToc-empty' ) )
.text( config.labels[ 'collapsibleToc-empty' ] );
}
$button.addClass( 'collapsibleToc-button' );
// Inserta el botón como el primer elemento hijo del <li> actual
$button.prependTo( $li );
}
function addToggleButtons( $toc ) {
var $tocToggle;
// Botón para mostrar todas las secciones
$developAll = $( '<span>' )
.text( config.labels[ 'collapsibleToc-develop-all' ] )
.on( 'click', function () {
if ( $developAll.data( 'collapsibleToc-toggle-active' ) ) {
$toc
.find( '[data-collapsible-toc-state="collapsed"]' )
.trigger( 'click' );
checkAndUpdateState( OPS.CLEAR );
}
} );
// Botón para ocultar todas las secciones
$envelopAll = $( '<span>' )
.text( config.labels[ 'collapsibleToc-envelop-all' ] )
.on( 'click', function () {
if ( $envelopAll.data( 'collapsibleToc-toggle-active' ) ) {
$toc
.find( '[data-collapsible-toc-state="expanded"]' )
.trigger( 'click' );
checkAndUpdateState( OPS.FILL );
}
} );
// Inicializa el estado de los botones
checkAndUpdateState( OPS.INIT );
// "Cajetín" del ToC que contiene ambos botones
$tocToggle = $( '<span>' )
.addClass( 'toctoggle toctoggle-ct' )
.append(
'[',
$developAll,
' ',
$envelopAll,
']'
);
// El ToC está inicialmente oculto, no muestres el cajetín
if ( !$toc.find( 'ul' ).eq( 0 ).is( ':visible' ) ) {
$tocToggle.hide();
}
$tocToggle.appendTo( $toc.find( '.toctitle' ) );
// Configura la animación del cajetín, ejecutándose a la par que el slideUp/Down del ToC
$toc.find( '#togglelink' ).on( 'click', function ( e ) {
e.preventDefault();
$tocToggle.fadeToggle( 'fast' );
} );
}
function checkAndUpdateState( op, isFirstLevel ) {
switch ( op ) {
case OPS.INIT:
updateDevelopAllButton( collapsedCount !== 0 );
updateEnvelopAllButton( collapsedCount !== totalCount );
break;
case OPS.CLEAR:
collapsedCount = 0;
collapsedfirstLevelCount = 0;
updateDevelopAllButton( false );
updateEnvelopAllButton( true );
break;
case OPS.FILL:
collapsedCount = totalCount;
collapsedfirstLevelCount = totalFirstLevelCount;
updateDevelopAllButton( true );
updateEnvelopAllButton( false );
break;
case OPS.INCR:
collapsedCount++;
if ( isFirstLevel ) {
collapsedfirstLevelCount++;
}
if ( collapsedCount === 1 ) {
updateDevelopAllButton( true );
}
if (
collapsedCount === totalCount ||
// Este es un caso especial, pues debe tener en cuenta si la sección plegada es
// de primer nivel. Si esta y sus hermanas se pliegan, ejecuta
// updateEnvelopAllButton( false ) independientemente del estado de las secciones hijas.
isFirstLevel && collapsedfirstLevelCount === totalFirstLevelCount
) {
updateEnvelopAllButton( false );
}
break;
case OPS.DECR:
collapsedCount--;
if ( isFirstLevel ) {
collapsedfirstLevelCount--;
}
if ( collapsedCount === 0 ) {
updateDevelopAllButton( false );
}
if (
collapsedCount === totalCount - 1 ||
// Ver comentario en OPS.INCR
isFirstLevel && collapsedfirstLevelCount === totalFirstLevelCount - 1
) {
updateEnvelopAllButton( true );
}
break;
}
}
function updateDevelopAllButton( isActive ) {
$developAll
// TODO: trasladar a CSS
.css( isActive ? { color: '#0645AD', cursor: 'pointer' } : { color: '#C0C0C0', cursor: 'default' } )
.data( 'collapsibleToc-toggle-active', isActive ? true : false )
.attr( 'title', mw.msg( isActive ? 'collapsibleToc-develop-all' : 'collapsibleToc-develop-all-empty' ) );
}
function updateEnvelopAllButton( isActive ) {
$envelopAll
// TODO: trasladar a CSS
.css( isActive ? { color: '#0645AD', cursor: 'pointer' } : { color: '#C0C0C0', cursor: 'default' } )
.data( 'collapsibleToc-toggle-active', isActive ? true : false )
.attr( 'title', mw.msg( isActive ? 'collapsibleToc-envelop-all' : 'collapsibleToc-envelop-all-empty' ) );
}