MediaWiki:Common.js
Note : après avoir publié vos modifications, il se peut que vous deviez forcer le rechargement complet du cache de votre navigateur pour voir les changements.
- Firefox / Safari : maintenez la touche Maj (Shift) en cliquant sur le bouton Actualiser ou appuyez sur Ctrl + F5 ou Ctrl + R (⌘ + R sur un Mac).
- Google Chrome : appuyez sur Ctrl + Maj + R (⌘ + Shift + R sur un Mac).
- Internet Explorer / Edge : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl + F5.
- Opera : appuyez sur Ctrl + F5.
/* ============================================================
Tooltips pour les exposants <ref> — Ruches wiki v2
============================================================ */
( function () {
'use strict';
function init() {
var refs = document.querySelectorAll( 'sup.reference a' );
if ( !refs.length ) return;
var tip = document.createElement( 'div' );
tip.style.cssText = [
'position:fixed',
'z-index:9999',
'max-width:380px',
'background:#fefefe',
'border:1px solid #a2a9b1',
'border-radius:4px',
'box-shadow:0 2px 8px rgba(0,0,0,.25)',
'padding:10px 14px',
'font-size:.875rem',
'line-height:1.5',
'color:#202122',
'pointer-events:none',
'display:none'
].join( ';' );
document.body.appendChild( tip );
refs.forEach( function ( a ) {
a.addEventListener( 'mouseenter', function ( e ) {
var href = a.getAttribute( 'href' );
if ( !href ) return;
var id = href.replace( /^#/, '' );
var li = document.getElementById( id );
if ( !li ) return;
var clone = li.cloneNode( true );
clone.querySelectorAll( '.mw-cite-backlink' ).forEach( function ( el ) {
el.remove();
} );
tip.textContent = clone.textContent.trim();
tip.style.display = 'block';
move( e );
} );
a.addEventListener( 'mousemove', move );
a.addEventListener( 'mouseleave', function () {
tip.style.display = 'none';
} );
} );
function move( e ) {
var x = e.clientX + 16;
var y = e.clientY + 16;
if ( x + tip.offsetWidth > window.innerWidth - 10 ) {
x = e.clientX - tip.offsetWidth - 16;
}
if ( y + tip.offsetHeight > window.innerHeight - 10 ) {
y = e.clientY - tip.offsetHeight - 16;
}
tip.style.left = x + 'px';
tip.style.top = y + 'px';
}
}
if ( document.readyState === 'loading' ) {
document.addEventListener( 'DOMContentLoaded', init );
} else {
init();
}
}() );
/* === Lien "Coopter une abeille" pour les abeilles et sysops === */
mw.loader.using(['mediawiki.util']).then(function () {
var groups = mw.config.get('wgUserGroups') || [];
var isAbeille = groups.some(function (g) {
return g.indexOf('abeille') === 0 || g === 'sysop';
});
if (isAbeille) {
mw.util.addPortletLink(
'p-tb',
mw.util.getUrl('Special:UserRights'),
'Coopter une abeille',
't-coopter',
'Attribuer des droits à un utilisateur'
);
}
});
/* === Évènements à venir — Agrégation dynamique des ruches === */
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'jquery']).then(function () {
if (mw.config.get('wgPageName') !== 'Accueil') return;
var container = document.getElementById('evenements-ruches');
if (!container) return;
var DEPARTMENTS = {
'01':'Ain','02':'Aisne','03':'Allier','04':'Alpes-de-Haute-Provence',
'05':'Hautes-Alpes','06':'Alpes-Maritimes','07':'Ardèche','08':'Ardennes',
'09':'Ariège','10':'Aube','11':'Aude','12':'Aveyron',
'13':'Bouches-du-Rhône','14':'Calvados','15':'Cantal','16':'Charente',
'17':'Charente-Maritime','18':'Cher','19':'Corrèze',
'2A':'Corse-du-Sud','2B':'Haute-Corse',
'21':"Côte-d'Or",'22':"Côtes-d'Armor",'23':'Creuse','24':'Dordogne',
'25':'Doubs','26':'Drôme','27':'Eure','28':'Eure-et-Loir','29':'Finistère',
'30':'Gard','31':'Haute-Garonne','32':'Gers','33':'Gironde','34':'Hérault',
'35':'Ille-et-Vilaine','36':'Indre','37':'Indre-et-Loire','38':'Isère',
'39':'Jura','40':'Landes','41':'Loir-et-Cher','42':'Loire',
'43':'Haute-Loire','44':'Loire-Atlantique','45':'Loiret','46':'Lot',
'47':'Lot-et-Garonne','48':'Lozère','49':'Maine-et-Loire','50':'Manche',
'51':'Marne','52':'Haute-Marne','53':'Mayenne','54':'Meurthe-et-Moselle',
'55':'Meuse','56':'Morbihan','57':'Moselle','58':'Nièvre','59':'Nord',
'60':'Oise','61':'Orne','62':'Pas-de-Calais','63':'Puy-de-Dôme',
'64':'Pyrénées-Atlantiques','65':'Hautes-Pyrénées','66':'Pyrénées-Orientales',
'67':'Bas-Rhin','68':'Haut-Rhin','69':'Rhône','70':'Haute-Saône',
'71':'Saône-et-Loire','72':'Sarthe','73':'Savoie','74':'Haute-Savoie',
'75':'Paris','76':'Seine-Maritime','77':'Seine-et-Marne','78':'Yvelines',
'79':'Deux-Sèvres','80':'Somme','81':'Tarn','82':'Tarn-et-Garonne',
'83':'Var','84':'Vaucluse','85':'Vendée','86':'Vienne','87':'Haute-Vienne',
'88':'Vosges','89':'Yonne','90':'Territoire de Belfort','91':'Essonne',
'92':'Hauts-de-Seine','93':'Seine-Saint-Denis','94':'Val-de-Marne',
'95':"Val-d'Oise",
'971':'Guadeloupe','972':'Martinique','973':'Guyane',
'974':'La Réunion','976':'Mayotte','FE':'Monde'
};
var REGIONS = [
["Auvergne-Rhône-Alpes", ['01','03','07','15','26','38','42','43','63','69','73','74']],
["Bourgogne-Franche-Comté", ['21','25','39','58','70','71','89','90']],
["Bretagne", ['22','29','35','56']],
["Centre-Val de Loire", ['18','28','36','37','41','45']],
["Corse", ['2A','2B']],
["Grand Est", ['08','10','51','52','54','55','57','67','68','88']],
["Hauts-de-France", ['02','59','60','62','80']],
["Île-de-France", ['75','77','78','91','92','93','94','95']],
["Normandie", ['14','27','50','61','76']],
["Nouvelle-Aquitaine", ['16','17','19','23','24','33','40','47','64','79','86','87']],
["Occitanie", ['09','11','12','30','31','32','34','46','48','65','66','81','82']],
["Pays de la Loire", ['44','49','53','72','85']],
["Provence-Alpes-Côte d'Azur", ['04','05','06','13','83','84']],
["Outre-mer", ['971','972','973','974','976']],
["Français de l'Étranger", ['FE']]
];
/* Convertit du wikitext simplifié en HTML */
function wikitextToHtml(text) {
var html = $('<div>').text(text).html(); // échapper le HTML
// Titres de sous-section (du plus profond au plus superficiel)
html = html.replace(/^======\s*(.+?)\s*======\s*$/gm, '<h6>$1</h6>');
html = html.replace(/^=====\s*(.+?)\s*=====\s*$/gm, '<h5>$1</h5>');
html = html.replace(/^====\s*(.+?)\s*====\s*$/gm, '<h4>$1</h4>');
html = html.replace(/^===\s*(.+?)\s*===\s*$/gm, '<h3>$1</h3>');
// Liens wiki : [[Cible|Texte]] ou [[Cible]]
html = html.replace(/\[\[([^\]|]+)\|([^\]]+)\]\]/g, function(_, target, label) {
return '<a href="' + mw.util.getUrl(target) + '">' + label + '</a>';
});
html = html.replace(/\[\[([^\]]+)\]\]/g, function(_, target) {
return '<a href="' + mw.util.getUrl(target) + '">' + target + '</a>';
});
// Listes à puces
html = html.replace(/^(\* .+)$/gm, '<li>$1</li>').replace(/^(\* )/gm, '');
html = html.replace(/(<li>[\s\S]*?<\/li>)/g, '<ul>$1</ul>');
// Sauts de ligne (hors balises bloc)
html = html.replace(/\n/g, '<br>');
return html;
}
/* Extrait la section Évènements à venir du wikitext */
function extractEvents(wikitext) {
// Stoppe dès qu'une ligne commence par == (titre de section suivant)
var m = wikitext.match(/==\s*Évènements à venir\s*==[^\n]*\n((?:(?!==(?!=))[^\n]*\n?)*)/);
// (?!==(?!=)) : stoppe sur == (niveau 2) mais pas === ou plus profond
if (!m) return '';
var content = m[1].trim();
if (!content) return '';
var placeholders = ['à compléter', '{{à compléter}}', '*', '-', '* à compléter'];
var lines = content.split('\n').filter(function(l) {
var t = l.trim().toLowerCase();
return t && placeholders.indexOf(t) === -1;
});
return lines.length ? lines.join('\n') : '';
}
/* Crée un bloc réductible avec <details>/<summary> (natif, pas de JS) */
function makeCollapsible(title, contentHtml, collapsed) {
var $d = $('<details>').css({
'border': '1px solid #a2a9b1',
'border-radius': '2px',
'margin': '3px 0',
'background': '#fff',
'width': '100%'
});
if (!collapsed) $d.attr('open', '');
$('<summary>').html(title).css({
'padding': '4px 8px',
'cursor': 'pointer',
'background': '#eaecf0',
'user-select': 'none',
'list-style': 'revert'
}).appendTo($d);
$('<div>').html(contentHtml).css('padding', '4px 8px').appendTo($d);
return $d;
}
/* Charge les pages en batch de 50 */
var api = new mw.Api();
var allTitles = ['Ruche 00'];
Object.keys(DEPARTMENTS).forEach(function(code) {
allTitles.push('Ruches/' + DEPARTMENTS[code]);
});
var batches = [];
for (var i = 0; i < allTitles.length; i += 50) {
batches.push(allTitles.slice(i, i + 50));
}
container.innerHTML = '<em>Chargement des évènements...</em>';
Promise.all(batches.map(function(batch) {
return api.get({
action: 'query', titles: batch.join('|'),
prop: 'revisions', rvprop: 'content', rvslots: 'main', format: 'json'
});
})).then(function(results) {
var events = {};
results.forEach(function(result) {
Object.values(result.query.pages).forEach(function(page) {
if (!page.revisions) return;
var wt = (page.revisions[0].slots || {}).main
? page.revisions[0].slots.main['*']
: page.revisions[0]['*'];
var ev = extractEvents(wt || '');
if (ev) events[page.title] = ev;
});
});
var $wrap = $('<div>');
// Ruche 00 en tête
if (events['Ruche 00']) {
$wrap.append(makeCollapsible(
'<strong>Ruche 00 — National</strong>',
wikitextToHtml(events['Ruche 00']), true
));
}
// Régions
REGIONS.forEach(function(reg) {
var regionName = reg[0], codes = reg[1];
var $regionContent = $('<div>');
codes.forEach(function(code) {
var nom = DEPARTMENTS[code];
var title = 'Ruches/' + nom;
if (events[title]) {
var link = '<a href="' + mw.util.getUrl(title) + '">' + nom + ' (' + code + ')</a>';
$regionContent.append(makeCollapsible(link, wikitextToHtml(events[title]), true));
}
});
if ($regionContent.children().length) {
$wrap.append(makeCollapsible(
'<strong>' + regionName + '</strong>',
$regionContent[0].outerHTML, true
));
}
});
container.innerHTML = $wrap.children().length
? ''
: '<em>Aucun évènement à venir pour le moment.</em>';
if ($wrap.children().length) {
$(container).append($wrap.children());
}
}).catch(function() {
container.innerHTML = '<em>Erreur lors du chargement des évènements.</em>';
});
});
/* === Fin Évènements à venir === */
/* === Sections réductibles (h2) === */
mw.hook('wikipage.content').add(function ($content) {
$content.find('h2').each(function () {
var $h2 = $(this);
var $section = $h2.closest('section');
// MW 1.35+ : les sections sont wrappées dans <section>
// Fallback : on parcourt les siblings jusqu'au prochain h2
var $body;
if ($section.length) {
$body = $section.children().not('h2');
} else {
$body = $();
var $cur = $h2.next();
while ($cur.length && !$cur.is('h1, h2')) {
$body = $body.add($cur);
$cur = $cur.next();
}
}
if (!$body.length) return;
var $btn = $('<span>')
.addClass('section-toggle-btn')
.attr('title', 'Replier/déplier cette section')
.css({
cursor : 'pointer',
marginLeft : '6px',
fontSize : '0.75em',
fontWeight : 'normal',
color : '#888',
userSelect : 'none',
verticalAlign: 'middle'
})
.text('[masquer]');
$btn.on('click', function () {
if ($body.is(':visible')) {
$body.hide();
$btn.text('[afficher]');
} else {
$body.show();
$btn.text('[masquer]');
}
});
// Insérer le bouton dans .mw-headline (le span du titre)
var $headline = $h2.find('.mw-headline');
($headline.length ? $headline : $h2).append($btn);
});
});
/* === Fin Sections réductibles === */
/* === Bandeau avertissement pages IA === */
mw.hook('wikipage.content').add(function ($content) {
var cats = mw.config.get('wgCategories') || [];
// Compatibilité : MW peut renvoyer espaces ou underscores dans wgCategories
var cible = 'Généré par IA';
var present = cats.some(function (c) {
return c.replace(/_/g, ' ') === cible;
});
if (!present) return;
var $bandeau = $('<div>').css({
background : '#fff3cd',
border : '1px solid #ffc107',
borderLeft : '4px solid #ffc107',
borderRadius : '4px',
padding : '10px 14px',
margin : '0 0 16px 0',
fontSize : '0.95em',
display : 'flex',
alignItems : 'center',
gap : '10px'
}).html("<span style=\"font-size:1.4em\">⚠️</span><span><strong>Contenu généré par Intelligence Artificielle.</strong> Cette page n'a pas été validée par les membres des Ruches. Méfiez-vous de son contenu.</span>");
$content.prepend($bandeau);
});
/* === Fin Bandeau IA === */
/* === Justification retrait droits abeille === */
if ( mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Userrights' ) {
$( function () {
/* En MW 1.41 (OOUI), les cases ont name="wpGroup-<groupe>"
On détecte celles dont le name contient "abeille". */
function getAbeilleBoxes() {
return $( 'input[type="checkbox"]' ).filter( function () {
return ( $( this ).attr( 'name' ) || '' ).toLowerCase().indexOf( 'abeille' ) !== -1;
} );
}
/* Mémorise l'état initial */
var initialChecked = {};
getAbeilleBoxes().each( function () {
initialChecked[ $( this ).attr( 'name' ) ] = this.checked;
} );
/* Intercepte n'importe quel formulaire de la page */
$( document ).on( 'submit', 'form', function ( e ) {
var retrait = false;
getAbeilleBoxes().each( function () {
var n = $( this ).attr( 'name' );
if ( initialChecked[ n ] === true && !this.checked ) {
retrait = true;
}
} );
if ( !retrait ) { return; }
/* Le champ raison peut être input ou textarea, identifié par name="wpReason" */
var $reason = $( 'input[name="wpReason"], textarea[name="wpReason"]' );
var reason = $reason.val() ? $reason.val().trim() : '';
if ( !reason ) {
e.preventDefault();
e.stopImmediatePropagation();
$( '#justif-error' ).remove();
$( '<div id="justif-error" style="' +
'color:#d33;font-weight:bold;padding:0.5em 0.8em;' +
'background:#fff0f0;border:1px solid #d33;border-radius:4px;margin:0.6em 0;">' +
'⚠ Vous devez indiquer une raison pour retirer les droits d\'abeille.' +
'</div>' ).insertBefore( $reason.closest( '.oo-ui-fieldLayout, tr, p' ).first() );
$reason.trigger( 'focus' );
return false;
}
} );
/* Efface l'erreur dès qu'on tape dans le champ raison */
$( document ).on( 'input', 'input[name="wpReason"], textarea[name="wpReason"]', function () {
$( '#justif-error' ).remove();
} );
} );
}
/* === Fin Justification retrait === */