MediaWiki:Gadget-Accessibility.js
Apparence
Note : après avoir enregistré la page, vous devrez forcer le rechargement complet du cache de votre navigateur pour voir les changements.
Mozilla / Firefox / Konqueror / Safari : maintenez la touche Majuscule (Shift) en cliquant sur le bouton Actualiser (Reload) ou pressez Maj-Ctrl-R (Cmd-R sur Apple Mac) ;
Firefox (sur GNU/Linux) / Chrome / Internet Explorer / Opera : maintenez la touche Ctrl en cliquant sur le bouton Actualiser ou pressez Ctrl-F5./**
* Outils de vérification de l'accessibilité des pages.
* Ajoute une palette de liens, classiquement à gauche.
* Auteurs : Lgd, Ltrlg
* {{Projet:JavaScript/Script|Accessibility}}
*/
/* eslint-env browser */
/* jshint esversion: 6 */
/* global $, mw, OO */
( function () {
var
supportedSkins = [ 'vector', 'monobook', 'timeless', 'vector-2022' ],
portletId = 'p-accessibility',
storageId = 'gadget-accessibility',
messages;
// Literal non-breaking space, for situations where HTML entities can't be used
const NBSP = '\xA0';
/* ----- Translations ----- */
messages = {
fr: {
'accessibility-all-disable': 'Tout désactiver',
'accessibility-alt-empty': 'ALT VIDE',
'accessibility-alt-flag': 'L’alternative devrait commencer par « Drapeau… » ou « Pavillon… »',
'accessibility-alt-gallery': 'Indiquez l’alternative de chaque image de la galerie.',
'accessibility-alt-label': 'Alternatives',
'accessibility-alt-link': 'Vérifiez si le lien sur l’image peut être désactivé (LINK vide) (ou si l’image peut être transformée en thumb)',
'accessibility-alt-long': 'Vérifiez si cette alternative de $1 caractères pourrait être plus concise (environ 120' + NBSP + 'caractères au plus) et si l’alternative actuelle pourrait être transférée dans la section description de la page de l’image.',
'accessibility-alt-missing': 'ALT MANQUANT',
'accessibility-alt-title': 'Vérifier la présence des alternatives textuelles',
'accessibility-alt-usemap': 'Indiquez l’alternative de l’image.',
'accessibility-disclaimer': 'Soyez prudent dans vos modifications et n’hésitez pas à <a href="//fr.wikipedia.org/wiki/Discussion_Wikip%C3%A9dia:Atelier_accessibilit%C3%A9">demander conseil</a>.',
'accessibility-good-practice-label': 'Bonnes pratiques',
'accessibility-good-practice-page': 'Wikipédia:Atelier accessibilité/Bonnes pratiques',
'accessibility-good-practice-title': 'Bonnes pratiques d’accessibilité (nouvelle fenêtre)',
'accessibility-headers-dl': 'Possible élément de liste de définition utilisé à la place d’un titre de section',
'accessibility-headers-h1': 'H1',
'accessibility-headers-h2': 'H2',
'accessibility-headers-h3': 'H3',
'accessibility-headers-h4': 'H4',
'accessibility-headers-h5': 'H5',
'accessibility-headers-h6': 'H6',
'accessibility-headers-label': 'Titres',
'accessibility-headers-title': 'Vérifier l’accessibilité des titres de section',
'accessibility-help-label': 'Aide gadget',
'accessibility-help-page': 'Wikipédia:Atelier accessibilité/Aide gadget',
'accessibility-help-title': 'Aide pour l’utilisation du gadget (nouvelle fenêtre)',
'accessibility-lang-dk': 'Code de langue erroné' + NBSP + ': utilisez da (danois)',
'accessibility-lang-empty': 'Code de langue erroné' + NBSP + ': lang ne doit pas être vide',
'accessibility-lang-foreign-latn': 'Vérifiez que ce qui suit est bien une transcription en caractères latins (et non une traduction)' + NBSP + ':',
'accessibility-lang-foreign-missing': 'Code de langue manquant pour une citation en langue étrangère',
'accessibility-lang-gr': 'Code de langue erroné' + NBSP + ': utilisez el (grec moderne) ou grc (grec ancien avant 1453)',
'accessibility-lang-jp': 'Code de langue erroné' + NBSP + ': utilisez ja (japonais)',
'accessibility-lang-label': 'Langues',
'accessibility-lang-lang': 'Lang' + NBSP + ': $1',
'accessibility-lang-lat': 'Code de langue erroné' + NBSP + ': utilisez la (latin)',
'accessibility-lang-lu': 'Code de langue erroné' + NBSP + ': utilisez lb (luxembourgeois)',
'accessibility-lang-mul': 'Code de langue erroné' + NBSP + ': ne pas utiliser le code mul (multiple)',
'accessibility-lang-oci': 'Code de langue erroné' + NBSP + ': utilisez oc (occitan)',
'accessibility-lang-title': 'Vérifier la présence des changements de langue',
'accessibility-lang-und': 'Code de langue erroné' + NBSP + ': ne pas utiliser le code und (undefined)',
'accessibility-lists-dd': 'Définition' + NBSP + ': ',
'accessibility-lists-dl': 'Vérifiez le bon usage et la structure de cette liste de définition.',
'accessibility-lists-dt': 'Terme défini' + NBSP + ': ',
'accessibility-lists-header': 'Possible élément de liste de définition utilisé à la place d’un titre de section',
'accessibility-lists-indent': 'Possible élément de liste de définition utilisé uniquement pour indenter le texte',
'accessibility-lists-label': 'Listes',
'accessibility-lists-orphean': 'Élément de liste potentiellement orphelin' + NBSP + ': vérifier l’absence de sauts de ligne entre les *',
'accessibility-lists-title': 'Vérifier l’accessibilité des listes de définition',
'accessibility-portlet-collapse-title': 'Réduire',
'accessibility-portlet-expand-title': 'Étendre',
'accessibility-portlet-fix-title': 'Fixer le gadget',
'accessibility-portlet-title': 'Accessibilité',
'accessibility-portlet-unfix-title': 'Détacher',
'accessibility-quotes-label': 'Citations',
'accessibility-quotes-ok': 'Citation bien balisée' + NBSP + ': ',
'accessibility-quotes-title': 'Vérifier l’accessibilité des citations',
'accessibility-tables-caption-error': 'Ce tableau ne devrait pas avoir de titre caption (code wiki' + NBSP + ': |+)</caption>',
'accessibility-tables-caption-missing': 'Ce tableau devrait avoir un titre caption (code wiki' + NBSP + ': |+)</caption>',
'accessibility-tables-imbrication': 'Limitez autant que possible l’imbrication de tableaux.',
'accessibility-tables-label': 'Tableaux',
'accessibility-tables-summary': '@summary=$1',
'accessibility-tables-td-headers': 'HEADERS: $1',
'accessibility-tables-td-scope': 'ERREUR TD SCOPE: $1',
'accessibility-tables-th-id': 'ID: $1',
'accessibility-tables-th-missing-scope': 'Vérifiez s’il faut ajouter un SCOPE ou transformer en TD (code wiki' + NBSP + ': |)',
'accessibility-tables-th-scope': 'SCOPE: $1',
'accessibility-tables-th-scope-headers': 'SCOPE: $1 HEADERS: $2',
'accessibility-tables-th-scope-headers-id': 'SCOPE: $1 HEADERS: $2 ID: $3',
'accessibility-tables-th-scope-id': 'SCOPE: $1 ID: $2',
'accessibility-tables-title': 'Vérifier l’accessibilité des tableaux'
}
};
function selectApplicableMessages() {
var
defaultLang = 'fr',
currentLang = mw.config.get( 'wgUserLanguage' );
mw.messages.set( messages[ defaultLang ] );
if ( currentLang !== defaultLang && currentLang in messages ) {
mw.messages.set( messages[ currentLang ] );
}
}
/* ----- Storage ----- */
function loadOptions() {
var res = mw.storage.getObject( storageId );
return $.isPlainObject( res ) ? res : {};
}
function storeOptions( options ) {
mw.storage.setObject( storageId, options );
}
function storeBooleanState( name, val ) {
var options = loadOptions();
options[ name ] = val;
storeOptions( options );
}
function loadBooleanState( name ) {
var options = loadOptions();
return Object.prototype.hasOwnProperty.call( options, name ) ?
options[ name ] :
null;
}
/* ----- Fixed or not ----- */
/** Add a state control to a portlet.
*
* @param {object} description Options bag with the following properties:
* - `$portlet`: jQuery handler for the portlet;
* - `$controls`: jQuery handler for the controls bar;
* - `id`: storage id, partial state class id;
* - `enableIcon': OOUI icon for enabling;
* - `enableTitle`: hover text for enabling;
* - `disableIcon': OOUI icon for disabling;
* - `disableTitle`: hover text for disabling;
*/
function addStateControlToPortlet( description ) {
var
disabledData = {
title: description.enableTitle,
icon: description.enableIcon
},
enabledData = {
title: description.disableTitle,
icon: description.disableIcon
},
enabled = loadBooleanState( description.id ),
currentData = enabled ? enabledData : disabledData,
button = new OO.ui.ButtonWidget( {
framed: false,
icon: currentData.icon,
title: currentData.title
} ),
className = 'acc_' + description.id;
if ( enabled ) {
$( '#' + portletId ).addClass( className );
}
button.on( 'click', function () {
var currentData;
description.$portlet.toggleClass( className );
enabled = !enabled;
currentData = enabled ? enabledData : disabledData;
storeBooleanState( description.id, enabled );
button.setTitle( currentData.title );
button.setIcon( currentData.icon );
} );
description.$controls.append( button.$element );
}
function addControlsToPortlet( $portlet ) {
var $controls = $( '<span>' ).attr( 'id', 'acc_controls' );
addStateControlToPortlet( {
$portlet: $portlet,
$controls: $controls,
id: 'collapsed',
enableTitle: mw.msg( 'accessibility-portlet-collapse-title' ),
enableIcon: 'collapse',
disableTitle: mw.msg( 'accessibility-portlet-expand-title' ),
disableIcon: 'expand'
} );
addStateControlToPortlet( {
$portlet: $portlet,
$controls: $controls,
id: 'fixed',
enableTitle: mw.msg( 'accessibility-portlet-fix-title' ),
enableIcon: 'window',
disableTitle: mw.msg( 'accessibility-portlet-unfix-title' ),
disableIcon: 'close'
} );
if ( ( /vector/ ).test( mw.config.get( 'skin' ) ) === true ) {
$portlet.find( '.vector-menu-heading' ).before( $controls );
} else {
$portlet.find( 'h3' ).before( $controls );
}
}
/* ----- Portlet creation ----- */
function addDisclaimerToPortlet( $portlet ) {
var $disc = $( '<p>' )
.attr( 'id', 'acc_disclaimer' )
.html( mw.msg( 'accessibility-disclaimer' ) );
$portlet.find( 'ul' ).after( $disc );
}
function createPortlet( id, title ) {
var $portlet;
switch ( mw.config.get( 'skin' ) ) {
case 'vector-2022':
case 'vector':
$portlet = $( '<div>' )
.attr( { id: id, 'class': 'vector-menu mw-portlet mw-portlet-accessibility vector-menu-portal portal' } )
.append( $( '<div>' )
.attr( 'class', 'vector-menu-heading' )
.append( $( '<span>' )
.attr( 'class', 'vector-menu-heading-label' )
.text( title )
)
)
.append( $( '<div>' )
.attr( 'class', 'vector-menu-content body' )
.append( $( '<ul>' )
.attr( 'class', 'vector-menu-content-list' )
)
);
$( '#p-navigation' ).after( $portlet );
break;
case 'timeless':
$portlet = $( '<div>' )
.attr( { id: id, 'class': 'mw-portlet', role: 'navigation', 'aria-labelledby': id + '-label' } )
.append( $( '<h3>' )
.attr( { id: id + '-label' } )
.text( title )
)
.append( $( '<div>' )
.attr( 'class', 'mw-portlet-body' )
.append( $( '<ul>' ) )
);
$( '#p-navigation' ).after( $portlet );
break;
case 'monobook':
$portlet = $( '<div>' )
.attr( { id: id, 'class': 'portlet' } )
.append( $( '<h3>' ).text( title ) )
.append( $( '<div>' ).attr( 'class', 'pBody' )
.append( $( '<ul>' ) )
);
$( '#p-search' ).after( $portlet );
break;
default:
throw Error( 'Unsupported skin' );
}
return $portlet;
}
/* ----- CSS manipulation ----- */
/* We want to keep the code in CSS for now, but make strings translatable…
* Let’s build a tiny CSS superset for that…
* We will replace `«msg-name, args…»` (because french quotes are so easy for
* french people) with `"msg with " arg "."`
*/
function replacesMessagesinCss( extendedCss ) {
return extendedCss.replace( /«([^»]+)»/g, function ( _, cont ) {
var
parts = cont.split( /,\s*/ ),
msgName = parts[ 0 ],
msg = mw.msg( msgName ),
// Obviously, we may need escaping at some point…
wrappedMessage = '"' + msg + '"',
finalString = wrappedMessage.replace(
/\$(\d)/g,
function ( _, sIndex ) {
// Exit the string, then add our value.
return '"' + parts[ Number( sIndex ) ] + '"';
}
);
return finalString;
} );
}
/* ----- Individual options ----- */
/** Create a option link in the portlet.
*
* @param {object} description is an option bag with the following properties:
* - [req] `id`: the storage identifier & link identifier;
* - [req] `title`: title for the portlet link;
* - [req] `label`: label for the portlet link;
* - [opt] `activate`: function executed for activating the option;
* - [opt] `deactivate`: function executed for deactivating the option.
* - [opt] `styles`: extended CSS used by this option (see
* `replacesMessagesinCss`).
*
* @return {function} a function to change the state.
* Assuming you ran `var setActive = createOptionLink(…)`, you would:
* - run `setActive(true)` to activate the option;
* - run `setActive(false)` to deactivate the option.
*/
function createOptionLink( description ) {
var
active = loadBooleanState( description.id ),
$portlet = $( mw.util.addPortletLink(
/* portlet */ portletId,
/* href */ '#',
/* text */ description.label,
/* id */ 'acc_' + description.id,
/* tooltip */ description.title,
/* accesskey */ undefined,
/* nextnode */ undefined
) );
if ( description.styles ) {
$( mw.loader.addStyleTag( replacesMessagesinCss( description.styles ) ) )
// People should be able to see were styles come from
.attr( 'id', 'accessibility-option-' + description.id );
}
function getPageContentElement( $content = null ) {
if ( $content ) {
if ( $content.is( '.mw-parser-output' ) ) {
// $content est déjà l'élément .mw-parser-output, [[phab:T349298]]
return $content;
} else {
// ça peut ne pas trouver d'élément, notamment si c'est
// une page de modification n'ayant pas encore de prévisualisation
return $content.find( '.mw-parser-output' );
}
} else {
// ça peut ne pas trouver d'élément, notamment si c'est
// une page de modification n'ayant pas encore de prévisualisation
return $( '#mw-content-text' ).find( '.mw-parser-output' );
}
}
function runCallback( cb, $pageContent ) {
if ( cb ) {
cb( $pageContent );
}
}
function activate( $pageContent ) {
if ( $pageContent.length ) {
$pageContent.addClass( 'acc_' + description.id );
runCallback( description.activate, $pageContent );
}
}
function deactivate( $pageContent ) {
if ( $pageContent.length ) {
$pageContent.removeClass( 'acc_' + description.id );
runCallback( description.deactivate, $pageContent );
}
}
function setActive( newState ) {
if ( newState !== active ) {
active = newState;
storeBooleanState( description.id, active );
var $pageContent = getPageContentElement();
if ( active ) {
$portlet.addClass( 'acc_on' );
activate( $pageContent );
} else {
$portlet.removeClass( 'acc_on' );
deactivate( $pageContent );
}
}
}
mw.hook( 'wikipage.content' ).add( function ( $content ) {
if ( active ) {
var $pageContent = getPageContentElement( $content );
$portlet.addClass( 'acc_on' );
activate( $pageContent );
}
} );
$portlet.click( function ( e ) {
e.preventDefault();
setActive( !active );
} );
return setActive;
}
function createTableOption() {
return createOptionLink( {
id: 'table',
title: mw.msg( 'accessibility-tables-title' ),
label: mw.msg( 'accessibility-tables-label' ),
activate: function ( $pageContent ) {
$pageContent.find( 'table:has(th[scope])' )
.not( ':has(caption)' )
.prepend( $( '<caption>' )
.attr( 'class', 'error acc_caption_error' )
.text( mw.msg( 'accessibility-tables-caption-missing' ) )
);
$pageContent.find( 'table:has(caption)' )
.not( ':has(th[scope])' )
.prepend( $( '<caption>' )
.attr( 'class', 'error acc_caption_error' )
.text( mw.msg( 'accessibility-tables-caption-error' ) )
);
},
deactivate: function ( $pageContent ) {
$pageContent.find( 'caption.acc_caption_error' ).remove();
},
styles: `
.acc_table table {
border: 2px solid #FE7707 !important;
}
.acc_table table caption {
display: table-caption !important;
visibility: visible !important;
position: static !important;
height: auto !important;
width: auto !important;
font-size: 1em !important;
line-height: 1em !important;
text-indent: 0 !important;
background-color: lightgreen !important;
}
.acc_table table caption.error {
background: transparent !important;
font-weight: bold !important;
}
.acc_table table[summary]:before {
background-color:#FFFF99 !important;
border: 1px solid #FFCC66 !important;
color:#000000 !important;
opacity:0.9 !important;
font-size: 10px !important;
line-height: 12px !important;
padding: 2px 5px !important;
content: «accessibility-tables-summary»;
}
.acc_table table th {
background-color: red !important;
}
.acc_table table th:before {
content: «accessibility-tables-th-missing-scope»;
display: block;
color: red !important;
background: #fff !important
}
.acc_table table th[scope]:before,
.acc_table table th[id]:before {
display: none;
}
.acc_table table th[id] {
background-color: lime !important;
}
.acc_table table th[scope] {
background-color: lime !important;
}
.acc_table table th[scope]:after {
content: "SCOPE " attr(scope);
color: darkgreen;
display: block;
}
.acc_table table th[id]:after {
content: «accessibility-tables-th-id, attr(id)»;
display: block;
color: darkgreen;
}
.acc_table table th[scope][id]:after {
content: «accessibility-tables-th-scope-id,
attr(scope),
attr(id)»;
display: block;
color: darkgreen;
}
.acc_table table th[scope][headers]:after {
content: «accessibility-tables-th-scope-headers,
attr(scope),
attr(HEADERS)»;
display: block;
color: darkgreen;
}
.acc_table table th[scope][headers][id]:after {
content: «accessibility-tables-th-scope-headers-id,
attr(scope),
attr(HEADERS),
attr(ID)»;
display: block;
color: darkgreen;
}
.acc_table table td[headers] {
background-color: yellow !important;
}
.acc_table table td[headers]:after {
content: «accessibility-tables-td-headers, attr(HEADERS)»;
display: block;
color: darkgreen;
}
.acc_table table td[scope] {
background-color: red !important;
}
.acc_table table td[scope]:after {
content: «accessibility-tables-td-scope, attr(scope)»;
display: block;
}
.acc_table table table {
border: 5px solid red !important;
}
.acc_table table table:before {
content: «accessibility-tables-imbrication»;
font-weight: bold;
color: red;
background: #fff;
display: block;
padding: 5px;
}
.acc_table table col {
width: auto !important;
}
`
} );
}
function createHeaderOption() {
return createOptionLink( {
id: 'header',
title: mw.msg( 'accessibility-headers-title' ),
label: mw.msg( 'accessibility-headers-label' ),
styles: `
/* Headers*/
.acc_header h1,
.acc_header h2,
.acc_header h3,
.acc_header h4,
.acc_header h5,
.acc_header h6 {
border: 3px solid green !important;
padding: 3px !important;
color: #000 !important;
}
.acc_header h1:before {
content: «accessibility-headers-h1» " ";
}
.acc_header h2 {
margin-left: 15px !important;
}
.acc_header h2:before {
content: «accessibility-headers-h2» " ";
}
.acc_header h3 {
margin-left: 30px !important;
}
.acc_header h3:before {
content: «accessibility-headers-h3» " ";
}
.acc_header h4 {
margin-left: 45px !important;
}
.acc_header h4:before {
content: «accessibility-headers-h4» " ";
}
.acc_header h5 {
margin-left: 60px !important;
}
.acc_header h5:before {
content: «accessibility-headers-h5» " ";
}
.acc_header h6 {
margin-left: 75px !important;
}
.acc_header h6:before {
content: «accessibility-headers-h6» " ";
}
.acc_header dl dt:only-child:after {
content: «accessibility-headers-dl»;
color: red;
margin: 5px;
padding: 0 5px;
border: 1px solid red;
}
`
} );
}
function createListOption() {
return createOptionLink( {
id: 'list',
title: mw.msg( 'accessibility-lists-title' ),
label: mw.msg( 'accessibility-lists-label' ),
styles: `
/* listes */
.acc_list dl,
.acc_list dl dt {
border: 1px solid #FE7707;
}
.acc_list dt:before {
content: «accessibility-lists-dt»;
color: #FE7707;
}
.acc_list dd:before {
content: «accessibility-lists-dd»;
color: #FE7707;
}
.acc_list dl:before {
content: «accessibility-lists-dl»;
color: #FE7707;
font-weight: bold;
display: block;
margin: 5px;
}
.acc_list dl dt:only-child:after {
content: «accessibility-lists-header»;
color: red !important;
margin: 5px;
padding: 0 5px;
border: 1px solid red;
}
.acc_list dl dd:only-child:before {
content: «accessibility-lists-indent»;
color: red !important;
margin: 5px;
padding: 0 5px;
border: 1px solid red;
font-weight: bold;
}
.acc_list ul + ul li:only-child {
border: 1px solid red;
}
.acc_list ul + ul li:only-child:before {
background-color: #fff !important;
border: 1px solid red !important;
color: red !important;
padding: 2px 5px !important;
content: «accessibility-lists-orphean»;
display: block;
}
`
} );
}
function createQuoteOption() {
return createOptionLink( {
id: 'quote',
title: mw.msg( 'accessibility-quotes-title' ),
label: mw.msg( 'accessibility-quotes-label' ),
styles: `
/* Quotes */
.acc_quote .citation,
.acc_quote blockquote {
border: 1px solid green;
background: lightgreen !important;
}
.acc_quote .citation:before,
.acc_quote blockquote:before {
content: «accessibility-quotes-ok»;
color: Darkgreen ! important;
font-weight: bold;
padding: 5px;
}
`
} );
}
function createLangOption() {
return createOptionLink( {
id: 'lang',
title: mw.msg( 'accessibility-lang-title' ),
label: mw.msg( 'accessibility-lang-label' ),
styles: `
.acc_lang *[lang] {
border: 1px solid darkgreen;
}
.acc_lang *[lang]:after {
content: «accessibility-lang-lang, attr(lang)» " ";
border: 1px solid darkgreen;
padding: 2px 5px;
margin: 0 5px;
background: yellow;
}
.acc_lang *[lang]:before {
color: red;
}
.acc_lang *[lang=lat]:before {
content: «accessibility-lang-lat» " ";
}
*[lang=gr]:before,
*[lang=gr-Latn]:before,
*[lang=gr-latn]:before {
content: «accessibility-lang-gr» " ";
}
.acc_lang *[lang=oci]:before {
content: «accessibility-lang-oci» " ";
}
.acc_lang *[lang=lu]:before {
content: «accessibility-lang-lu» " ";
}
.acc_lang *[lang=jp]:before {
content: «accessibility-lang-jp» " ";
}
.acc_lang *[lang=dk]:before {
content: «accessibility-lang-dk» " ";
}
.acc_lang *[lang=mul]:before {
content: «accessibility-lang-mul» " ";
}
.acc_lang *[lang=und]:before {
content: «accessibility-lang-und» " ";
}
.acc_lang *[lang=""]:before {
content: «accessibility-lang-empty» " ";
}
/* Modèle Citation étrangère */
.acc_lang .not_fr_quote {
border: 1px solid darkgreen;
}
.acc_lang .not_fr_quote:before {
color: red;
content: «accessibility-lang-foreign-missing» " ";
}
.acc_lang .not_fr_quote[lang]:not([lang=""]):before {
content: "";
}
.acc_lang .transcription:before {
content: «accessibility-lang-foreign-latn» " ";
}
`
} );
}
/* ----- Alternatives (a bit longer…) ----- */
function missingAltMessage() {
return $( '<span>' )
.attr( 'class', 'error' )
.text( mw.msg( 'accessibility-alt-missing' ) );
}
function $text( text ) {
return $( document.createTextNode( text ) );
}
function joinJqArray( arr ) {
var res, i;
switch ( arr.length ) {
case 0:
return $( [] );
case 1:
return arr[ 0 ];
default:
res = arr[ 0 ];
for ( i = 1; i < arr.length; ++i ) {
res = res
.add( $( '<br>' ) )
.add( arr[ i ] );
}
return res;
}
}
function getMessageForImage( $img, $parent, $grandparent, $great_grandparent ) {
var
alt = $img.attr( 'alt' ) || '',
parts = [];
if ( alt === '' ) {
if ( !$great_grandparent.is( '.bandeau-icone' ) ) {
if ( $parent.is( 'a' ) ) {
parts.push( missingAltMessage() );
} else {
parts.push( $text( mw.msg( 'accessibility-alt-empty' ) ) );
}
}
} else {
parts.push( $text( alt ) );
if ( alt.length > 120 ) {
parts.push( $( '<span>' )
.attr( 'class', 'error' )
.text( mw.msg( 'accessibility-alt-long', alt.length ) )
);
}
}
if ( !$grandparent.is( 'figure[typeof~="mw:File/Thumb"]' ) ) {
if ( /\.(png|svg|jpg|gif)$/i.test( alt ) ) {
return missingAltMessage();
}
if ( $parent.is( '.mw-file-description' ) && !$great_grandparent.is( '.smiley' ) ) {
if ( $great_grandparent.is( '.flagicon' ) ) {
if ( !/^(Drapeau|Pavillon)/i.test( alt ) ) {
parts.push( $( '<span>' )
.attr( 'class', 'error' )
.text( mw.msg( 'accessibility-alt-flag' ) )
);
}
} else {
parts.push( $( '<span>' )
.attr( 'class', 'error' )
.text( mw.msg( 'accessibility-alt-link' ) )
);
}
}
}
return joinJqArray( parts );
}
function processSingleImageForAlternative( $img ) {
var
$parent = $img.parent(),
$grandparent = $parent.parent(),
$great_grandparent = $grandparent.parent(),
$msg, $wrapped;
$msg = getMessageForImage( $img, $parent, $grandparent, $great_grandparent );
if ( $msg.length < 1 ) {
return;
}
$wrapped = $( '<span>' )
.attr( 'class', 'acc_attr_show' )
.append( $msg );
// override du "line-height: 0" hérité de l'élément figure
if ( $grandparent.is( 'figure[typeof~="mw:File/Thumb"]' ) ) {
$wrapped.css( 'line-height', '1.4em' );
}
if ( $parent.is( 'a' ) ) {
$parent.before( $wrapped );
} else {
$img.before( $wrapped );
}
}
function insertBeforeElements( $targets, $element ) {
var i;
try {
$targets.eq( 0 ).before( $element );
for ( i = 1; i < $targets.length; ++i ) {
$targets.eq( i ).before( $element.clone );
}
} catch (e) {
// avoid TypeError: this.map is not a function (seen in VisualEditor)
}
}
function createAltOption() {
return createOptionLink( {
id: 'alt',
title: mw.msg( 'accessibility-alt-title' ),
label: mw.msg( 'accessibility-alt-label' ),
activate: function ( $pageContent ) {
$pageContent.find( 'img' ).each( function () {
processSingleImageForAlternative( $( this ) );
} );
// TODO Are these double spans necessary?
insertBeforeElements(
$pageContent.find( 'ul.gallery' ),
$( '<span>' )
.attr( 'class', 'acc_attr_show' )
.append( $( '<span>' )
.attr( 'class', 'error' )
.text( mw.msg( 'accessibility-alt-gallery' ) )
)
);
insertBeforeElements(
$pageContent.find( 'img[usemap]' ),
$( '<span>' )
.attr( 'class', 'acc_attr_show' )
.append( $( '<span>' )
.attr( 'class', 'error' )
.text( mw.msg( 'accessibility-alt-usemap' ) )
)
);
},
deactivate: function ( $pageContent ) {
$pageContent.find( 'span.acc_attr_show' ).remove();
}
} );
}
/* ----- Global links ----- */
function createGlobalDisabler( optionsActivators ) {
var portlet = mw.util.addPortletLink(
/* portlet */ portletId,
/* href */ '#',
/* text */ mw.msg( 'accessibility-all-disable' ),
/* id */ 'acc_all',
/* tooltip */ undefined,
/* accesskey */ undefined,
/* nextnode */ undefined
);
$( portlet ).click( function ( e ) {
e.preventDefault();
optionsActivators.forEach( function ( setActive ) {
setActive( false );
} );
} );
}
function createNewWindowPortletLink( description ) {
var portlet = mw.util.addPortletLink(
/* portlet */ portletId,
/* href */ mw.util.getUrl( description.page ),
/* text */ description.label,
/* id */ description.id,
/* tooltip */ description.title,
/* accesskey */ undefined,
/* nextnode */ undefined
);
$( portlet ).find( 'a' ).attr( 'target', '_blank' );
}
/* ----- Startup ----- */
function runAccessibilityGadget( $ ) { // eslint-disable-line no-unused-vars
var $portlet, optionsActivators;
if ( !supportedSkins.includes( mw.config.get( 'skin' ) ) ) { return; }
mw.loader.load(
'//fr.wikipedia.org/w/index.php?title=MediaWiki:Gadget-Accessibility.css&action=raw&ctype=text/css',
'text/css'
);
selectApplicableMessages();
$portlet = createPortlet(
portletId,
mw.msg( 'accessibility-portlet-title' )
);
addControlsToPortlet( $portlet );
addDisclaimerToPortlet( $portlet );
optionsActivators = [
createTableOption(),
createHeaderOption(),
createListOption(),
createQuoteOption(),
createLangOption(),
createAltOption()
];
createGlobalDisabler( optionsActivators );
createNewWindowPortletLink( {
id: 'acc_bp',
page: mw.msg( 'accessibility-good-practice-page' ),
title: mw.msg( 'accessibility-good-practice-title' ),
label: mw.msg( 'accessibility-good-practice-label' )
} );
createNewWindowPortletLink( {
id: 'acc_help',
page: mw.msg( 'accessibility-help-page' ),
title: mw.msg( 'accessibility-help-title' ),
label: mw.msg( 'accessibility-help-label' )
} );
}
if ( mw.config.get( 'wgNamespaceNumber' ) >= 0 ) {
mw.loader.using( [ 'mediawiki.storage', 'oojs-ui-core' ], function () {
$( runAccessibilityGadget );
} );
}
} )();