« MediaWiki:Gadget-explorer.js » : différence entre les versions
Page de l’interface de MediaWiki
Autres actions
mAucun résumé des modifications |
mAucun résumé des modifications |
||
| Ligne 208 : | Ligne 208 : | ||
attachDismissHandlers( trigger, root ); | attachDismissHandlers( trigger, root ); | ||
attachRubricHandler( root ); | attachRubricHandler( root ); | ||
$(document.body).addClass('explorer-vailable'); | |||
} | } | ||
Version du 6 juin 2026 à 14:01
/**
* MediaWiki:Gadget-explorer.js
* Explorateur de wiki — étape 5 : chargement à la demande des chunks.
*
* Au clic sur une rubrique, charge le sous-template correspondant et
* l'injecte dans le pane. Déclenche mw.hook('wikipage.content') pour
* que CategoryTree initialise ses arbres dans le contenu fraîchement
* injecté. Comportement accordéon : une seule rubrique active à la fois.
*/
( function () {
'use strict';
// --- Configuration ------------------------------------------------------
var HEADER_SLOT_SELECTOR = '.citizen-header__start';
var TRIGGER_CLASS = 'explorer-trigger';
var ROOT_ID = 'explorer-root';
var SHELL_PAGE = 'Modèle:Explorer';
// --- Internal state -----------------------------------------------------
var shellLoaded = false;
var shellLoading = false;
// --- DOM construction ---------------------------------------------------
function createTrigger() {
var btn = document.createElement( 'button' );
btn.type = 'button';
btn.className = TRIGGER_CLASS;
btn.setAttribute( 'aria-expanded', 'false' );
btn.setAttribute( 'aria-controls', ROOT_ID );
btn.setAttribute( 'aria-label', 'Ouvrir l\'explorateur' );
btn.innerHTML = '<img src="/images/e/ef/Navigator-tree.png">';
return btn;
}
function createRoot() {
var root = document.createElement( 'div' );
root.id = ROOT_ID;
root.className = 'explorer-root';
root.dataset.state = 'closed';
return root;
}
// --- Shell loading ------------------------------------------------------
function loadShell( root ) {
if ( shellLoaded || shellLoading ) {
return;
}
shellLoading = true;
root.dataset.loading = 'true';
var api = new mw.Api();
api.get( {
action: 'parse',
page: SHELL_PAGE,
prop: 'text',
disablelimitreport: 1,
disabletoc: 1,
formatversion: 2
} ).then( function ( data ) {
root.innerHTML = data.parse.text;
shellLoaded = true;
} ).catch( function ( code, info ) {
mw.log.error( '[explorer] failed to load shell', code, info );
root.innerHTML = '<div class="explorer-error">Impossible de charger l\'explorateur.</div>';
} ).always( function () {
shellLoading = false;
delete root.dataset.loading;
} );
}
// --- Chunk loading ------------------------------------------------------
function loadChunk( menuItem ) {
var chunkId = menuItem.dataset.chunk;
var content = menuItem.querySelector( '.explorer-chunk-content' );
if ( !content ) {
return;
}
var state = content.dataset.state;
if ( state === 'loaded' || state === 'loading' ) {
return;
}
content.dataset.state = 'loading';
var pageTitle = SHELL_PAGE + '/' + chunkId;
var api = new mw.Api();
// 1. Purger pour invalider le parser cache.
api.postWithToken( 'csrf', {
action: 'purge',
titles: pageTitle,
formatversion: 2
} ).then( function () {
// 2. Parser frais.
return api.get( {
action: 'parse',
page: pageTitle,
prop: 'text',
disablelimitreport: 1,
disabletoc: 1,
formatversion: 2
} );
} ).then( function ( data ) {
content.innerHTML = data.parse.text;
content.dataset.state = 'loaded';
mw.hook( 'wikipage.content' ).fire( $( content ) );
} ).catch( function ( code, info ) {
mw.log.error( '[explorer] failed to load chunk', chunkId, code, info );
content.dataset.state = 'error';
content.innerHTML = '<div class="explorer-error">Contenu indisponible.</div>';
} );
}
// --- Rubric activation (accordion) -------------------------------------
function activateRubric( menuItem, root ) {
// Désactiver les autres rubriques.
var others = root.querySelectorAll( '.explorer-menu-item[data-active]' );
Array.prototype.forEach.call( others, function ( el ) {
if ( el !== menuItem ) {
el.removeAttribute( 'data-active' );
}
} );
// Toggle sur la rubrique cliquée.
if ( menuItem.hasAttribute( 'data-active' ) ) {
menuItem.removeAttribute( 'data-active' );
} else {
menuItem.setAttribute( 'data-active', '' );
loadChunk( menuItem );
}
}
// --- Event handlers -----------------------------------------------------
function attachRubricHandler( root ) {
root.addEventListener( 'click', function ( e ) {
var label = e.target.closest( '.explorer-menu-label' );
if ( !label ) {
return;
}
var menuItem = label.closest( '.explorer-menu-item[data-chunk]' );
if ( !menuItem ) {
return;
}
activateRubric( menuItem, root );
} );
}
function attachDismissHandlers( trigger, root ) {
root.addEventListener( 'click', function ( e ) {
if ( !e.target.closest( '.explorer-menu' ) ) {
close( trigger, root );
}
} );
document.addEventListener( 'keydown', function ( e ) {
if ( e.key === 'Escape' && root.dataset.state === 'open' ) {
close( trigger, root );
}
} );
}
// --- State management ---------------------------------------------------
function open( trigger, root ) {
root.dataset.state = 'open';
trigger.setAttribute( 'aria-expanded', 'true' );
loadShell( root );
}
function close( trigger, root ) {
root.dataset.state = 'closed';
trigger.setAttribute( 'aria-expanded', 'false' );
}
function toggle( trigger, root ) {
if ( root.dataset.state === 'open' ) {
close( trigger, root );
} else {
open( trigger, root );
}
}
// --- Bootstrap ----------------------------------------------------------
function init() {
var slot = document.querySelector( HEADER_SLOT_SELECTOR );
if ( !slot ) {
mw.log.warn( '[explorer] header slot not found: ' + HEADER_SLOT_SELECTOR );
return;
}
var trigger = createTrigger();
var root = createRoot();
slot.appendChild( trigger );
document.body.appendChild( root );
trigger.addEventListener( 'click', function () {
toggle( trigger, root );
} );
attachDismissHandlers( trigger, root );
attachRubricHandler( root );
$(document.body).addClass('explorer-vailable');
}
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', init );
} else {
init();
}
}() );