Aller au contenu

Utilisateur:ContributorQ/scripts/TemplatesToolbar.js

Une page de Wikipédia, l'encyclopédie libre.
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.
// TemplatesToolbar.js
// A wikiEditor toolbar's add-on: a "Utilisateur:UserName/common.js" add-on which add two dropdownlist
// and some buttons in the toolbar, allowing quick template insertion and selection's wikification.
// Creation: 27/04/2017. (Version 6.0, 15/04/2022).
// Distributed under the terms of the GNU GPLv2 license: http://www.gnu.org/licenses/gpl-2.0.html
// Copyleft (c) ContributorQ (2017-2027).
// This experimental code applies only in the articles' namespace, under edit mode.

//<nowiki>

if ( $.inArray( mediaWiki.config.get( 'wgAction' ), [ 'edit', 'submit' ] ) !== -1 ) {
    ( function ( mw, $ ) {
        // Init the library object "mw.libs" providing routines for any script.
        // See: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.
        mw.libs = Object.assign( mw.libs, { 
            // The months of the year in french.
            frMonths: [ 'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre' ],
            // Digits in french.
            frDigits: [ 'un', 'deux', 'trois', 'quatre', 'cinq', 'six', 'sept', 'huit', 'neuf', 'dix', 'onze', 'douze', 'treize', 'quatorze', 'quinze', 'seize' ],
            // Separator for templates' parameters.
            tmplSeparator: '|',
            // Assemble a wiki template.
            fillTemplate: function ( templateName, paramValues ) {
                var template = '';

                if ( templateName ) {
                    template = '{{' + templateName;

                    if ( paramValues && paramValues.length > 0 ) {
                        template += this.tmplSeparator + paramValues.join( this.tmplSeparator );
                    }

                    return template + '}}';
                }

                return template;
            },
            // Get the current date in french (eg: "24 mai 2017").
            getCurrentDate: function () {
                var d = new Date();

                return mw.format( '$1 $2 $3', d.getDate(), 
                    this.frMonths[ d.getMonth() ].toLowerCase(),    // The getMonth() method returns the month (from 0 to 11) for the specified date, according to local time.
                    d.getFullYear() );
            }
        } );

        ////////////////////////////////// Main code ///////////////////////////////////////
        var customizeToolbar = function () {
            var $wikiEditor = $( '#wpTextbox1' ), //Get the editor element.
            basicToolbarButton = {
                type: 'button',
                icon: 'none'
            },
            toolbarButtonArray = {},
            toolbarButtonGroups = {},
            toolbarButtonIds = [],
            groupsCount = 1,
            toolbarButtonsCount = 0,
            divGroup,
            buttonsPerGroup = 13,    // Number of options on each row of the toolbar.
            /* Template descriptor's parameters. ********************
            * linkTitle: link's title in the templates toolbar.
            * name: exact name of the template.
            * suffix: a suffix specific to the template (e.g.: "e" in the "s" template).
            * paramsCount: the number of parameters of the template (-1 for any, 1 for only one, 0 when the "addTmpl" function is defined).
            * cleanSelection : an optional function to format the selection before applying the template.
            * addTmpl : an optional function to apply a specific template.
            * addTmplDeferred : an option to defer the call to the optional "addTmpl" finction if defined.
            * global: a function is globally applied to the whole text.
            * globalAction: a routine  applied to a selected texte or globally to the whole text.
            *********************************************************/
            tmplDescriptors = [
            { linkTitle: '- [[ ]]', name: '', tooltip: "Déwikification d'un lien interne.", suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return removeInternalLink( txt, false ); }, asButton: false, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ {{ }}', name: '', tooltip: 'Surround by "{{" and "}}".', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addWikiTemplateTags( txt ); }, asButton: false, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ {{ | }}', name: '', tooltip: 'Surround by "{{" and "}}" and replace the middle blank or "&nbsp;" by a "|".', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addWikiTemplateTagsAndMiddlePipe( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '- "|"', name: '', tooltip: 'Replace "|" in "date" and "unité" templates (ex.: "{{date|24|juin|2001}}" becomes "{{date|24 juin 2001}}").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return removePipes( selection ); } },
            { linkTitle: '+ {{s-|}}', name: '', tooltip: 'Format century (ex.: "XIe siècle" becomes "{{s-|XI}}" or "XIe et XIIe siècles" becomes "{{s2-|XI|XII}}").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addCenturyTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ {{p.}}', name: '', tooltip: 'Replace a "p.(/page) nnn" expression by "{{p.|nnn}} (note: "p", "pp" or "pp." are converted to "p.").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addPageTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Dt', name: 'date', tooltip: 'Add a {{date|}} template globally or on a selected text (ex.: "1er mai [[1981]]" becomes "{{date|1 mai [[1981}}")".', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return addDateTemplate( selection, this.name ); } },
            { linkTitle: 'Unité', name: 'unité', tooltip: 'Add a {{unité|}} template.', suffix: '', paramsCount: 1, cleanSelection:  function ( txt ) { return cleanBeforeAddingUnitTemplate( txt ); }, addTmpl: null, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Dunité', name: 'dunité', tooltip: 'Add a {{dunité|}} template.', suffix: '', paramsCount: 1, cleanSelection:  null, addTmpl: function ( txt ) { return addDunitTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'FrmNum', name: '', tooltip: 'Add "formatnum:" template.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return formatNum( txt ); }, asButton: false, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ Nobr', name: '', tooltip: 'Add "{{nobr|...}}" globally or on a selected text.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return addNoBrTemplate( selection ); } },
            { linkTitle: 'Lg (en)', name: 'langue|en', tooltip: 'Add a {{langue|en|}} template.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: false, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Lg (ja)', name: 'langue|ja', tooltip: 'Add a {{langue|ja|}} template.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: false, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Hre', name: 'heure', tooltip: 'Add a {{heure|}} template.', suffix: '', paramsCount: -1, cleanSelection: function ( txt ) { return removeHourSymbol( txt ); }, addTmpl: null, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Hre (durée)', name: 'heure', tooltip: 'Add a {{heure|}} template.', suffix: 'durée=oui', paramsCount: -1, cleanSelection: function ( txt ) { return removeHourSymbol( txt ); }, addTmpl: null, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '« »', name: '', tooltip: 'Add « ... » or replace "..." by « ... ».', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addFrenchQuotes( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '"«...»"', name: '', tooltip: "Change ''«...»'' into « ''...'' ».", suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return arrangeQuotes( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ {{incise|', name: '', tooltip: 'Encapsulate a selected texte in the "{{incise|...}}" template.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addInciseTemplate( txt ); }, asButton: false, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'frmNum ► unité', name: '', tooltip: 'Replace "{{formatnum:N}} ABC" expression by {{unité|N ABC}}.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return replaceFormatnumByUnit( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '{{...}} ► {{...|}}', name: '', tooltip: 'Replace "{{...}} ABC" expression by "{{...|ABC}}" (ex.: "{{10e}} tour" becomes "{{10e|tour}}" ; "{{p.}}20" becomes "{{p.|20}}").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return compactTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'A ► a', name: '', tooltip: 'Toggle a selected text to lower case.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return toggleTextToLowerCase( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Trim', name: '', tooltip: 'Remove useless spaces around | and =, and before }}.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return removeUselessSpaces( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ <ref>', name: '', tooltip: 'Add "<ref>...</ref>" tags.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addReferenceTags( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'To ref.', name: '', tooltip: 'Frame an external link with <ref>...</ref> tags.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return convertToReference( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'To Off.', name: '', tooltip: 'Convert a formal reference of an official website to a templated one.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return convertToOfficelTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Art. conn. ►', name: '', tooltip: 'Extract a list of related articles from a string contening some internal links.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return makeRelatedArticlesList( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'List ►', name: '', tooltip: 'Format a wiki list according to french typographical conventions.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return formatList( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Gallery ►', name: '', tooltip: 'Format a wiki gallery...', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return formatGallery( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Frm [[:lg:', name: '', tooltip: 'Format an interlanguage link. E.g.: "[[:en:Abc" to "{{lien|langue=en|trad=Abc...".', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return formatInterlangLink( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'File ►', name: '', tooltip: 'Globally clean [[File:]] wiki code.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return normalizeFilesTmpl( selection ); } },
            { linkTitle: '+ "." to <ref>', name: '', tooltip: 'Insert a final "." to a reference when missing.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return addFinalPeriodToReference( selection ); } },
            { linkTitle: 'Titles ►', name: '', tooltip: 'Add spaces globally in the titles when needed (ex.: "==ABC==" becomes "== ABC ==").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return formatSectionTitle( selection ); } },
            { linkTitle: '- bold', name: '', tooltip: 'Remove the wiki bold tags.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return removeBoldTags( selection ); } },
            { linkTitle: '- italic', name: '', tooltip: 'Remove the italic tags.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return removeItalicTags( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Century ►', name: '', tooltip: 'Globally clean century statements.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return updateCenturyTmpl( selection ); } },
            { linkTitle: '{{av JC}}', name: '', tooltip: 'Format globally any "av. J.-C." expression.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: false, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return formatAvJC( selection ); } },
            { linkTitle: 'Clean réf.', name: '', tooltip: 'Include extra templates in main template (ex.: "{{en}} {{Lien web|" becomes "{{Lien web|langue=en|").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return normalizeReference( selection ); } },
            { linkTitle: 'Remove tmpl', name: '', tooltip: 'Remove a template (ex.: "{{unité|14|heures}}" becomes "14 heures").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return clearTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: '+ Chem. tmpl', name: '', tooltip: 'Add a "formule chimique" template (ex. : "C<sub>7</sub>H<sub>5</sub>O" devient "{{formule chimique|C|7|H|5|O}}").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return addFormuleChimiqueTemplate( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'nb ► nobr', name: '', tooltip: 'Replace "unité" by nobr "unité" templates (ex.: "{{unité|3|fois}}" becomes "{{nobr|3 foiss}}").', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: function ( txt ) { return substituteNobrForNb( txt ); }, asButton: true, addTmplDeferred: false, global: false, globalAction: null },
            { linkTitle: 'Spec.', name: '', tooltip: 'Special function executing code temporarily useful. Current action: arrange artilce about swedish localities.', suffix: '', paramsCount: 1, cleanSelection: null, addTmpl: null, asButton: true, addTmplDeferred: false, global: true, globalAction: function ( selection ) { return specialFunction( selection ); } }
            ],
            // Set whether an action is globally applied.
            globalAction = false,
            // A function to translate in french some "file" template parameters' names.
            normalizeFilesTmpl = function ( editorText ) {
                var startFileTmplIdx = 0, startLinkTagIdx = 0, endLinkTagIdx = 0, backStartLinkTagIdx = 0, oldTemplate, newTemplate;

                editorText = editorText.replace( /\[\[(file|image|fichier):/ig, '[[Fichier:' ).replace( /\s{1,3}\]\]/ig, ']]' ).replace( /\s?\|\s/g, '|' ) ;

                // Add a "." just before the template's end tag.
                while ( startLinkTagIdx >= 0 ) {
                    startLinkTagIdx = editorText.indexOf( '[[Fichier:', endLinkTagIdx );
                    startFileTmplIdx = startLinkTagIdx;

                    if ( startLinkTagIdx >= 0 ) {
                        do {
                            endLinkTagIdx = editorText.indexOf( ']]', startLinkTagIdx + 2 );

                            if ( endLinkTagIdx >= 0 ) {
                                backStartLinkTagIdx = editorText.lastIndexOf( '[[', endLinkTagIdx );

                                if ( backStartLinkTagIdx > startLinkTagIdx ) {
                                    // An internal link found.
                                    startLinkTagIdx = endLinkTagIdx;
                                }
                                else {
                                    // Clean the current template.
                                    oldTemplate = editorText.slice( startFileTmplIdx, endLinkTagIdx + 2 );
                                    newTemplate = cleanFileTemplate( oldTemplate );
                                    editorText = editorText.replace( oldTemplate, newTemplate );
                                }
                            }
                        } while ( startLinkTagIdx == endLinkTagIdx );

                        endLinkTagIdx = endLinkTagIdx - ( oldTemplate.length - newTemplate.length );
                    }
                }

                return editorText;
            },
            // Insert a string into another string.
            insert = function ( originalString, stringToInsert, position ) {
                // Add a "." just before the template's end tag.
                if ( originalString && stringToInsert ) {
                    if ( position > 0 && position <= originalString.length - 1 ) {
                        return originalString.slice( 0, position )
                                + stringToInsert
                                + originalString.slice( position );    
                    }
                }

                return originalString;
            },
            // Clean a file template.
            cleanFileTemplate = function ( template ) {
                var tmplEndToken;

                if ( template && template.startsWith( '[[Fichier:' ) && template.endsWith( ']]' ) ) {
                    // Translate parameters and remove useless ones.
                    template = template.replace( '|left', '|gauche' )
                                        .replace( /\|thumb(nail)?/i, '|vignette' )
                                        .replace( '|upright', '|redresse' )
                                        .replace( /\|(droite|right)/i, '' );
                                           
                    // Remove useless image's width.
                    template = template.replace( /\|(\d{3})px/i, function ( match, capture1 ) {
                        var imgWidth = Number( capture1 );
                    
                        if ( imgWidth >= 120 && imgWidth <= 240 ) {
                            return '';
                        }

                        return match;
                    } );

                    // Get the last parameter's value.
                    tmplEndToken = template.slice( template.lastIndexOf( '|' ), template.length - 2 );

                    if ( tmplEndToken.charAt( tmplEndToken.length - 1 ) != '.' ) {
                        // Add a "." just before the template's end tag.
                        if ( tmplEndToken.search( /^\|(vignette|gauche|redresse|link=|\d{1,3}p?x)/m ) == -1 ) {
                            template = insert( template, '.', template.length - 2 );
                        }
                    }
                }

                return template;
            },
            // Adjust the "century" template.
            updateCenturyTmpl = function ( editorText ) {
                return editorText.replace( /(\{\{s(\smini)?-?\|[IVX]{1,5})\|e\}\}/ig, '$1}}' )
                                 .replace( /\{\{([IVX]{2,5})e siècle\}\}/ig, '{{s|$1}}' )
                                 .replace( /(\{\{s2-?\|[IVX]{1,5})\|e\|([IVX]{1,5})\|e\}\}/ig, '$1|$2}}' );
            },
            // Insert a "." before "</ref>" when needed.
            addFinalPeriodToReference = function ( editorText ) {
                return editorText.replace( /<ref>\s{1,4}/g, '<ref>' )
                                 .replace( /([^\.])\s{0,4}<\/ref>/g, '$1.</ref>' )
                                 .replace( /\.\s?\.<\/ref>/g, '.</ref>' )
                                 .replace( /\.(\}\}|\]).<\/ref>/g, '$1.</ref>' );
            },
            // Replace "unité" or "nombre" name's template by "nobr" (ex.: "{{unité|24|jours}}" becomes "{{nobr|24 jours}}".
            substituteNobrForNb = function ( txt ) {
                if ( txt.search( /\{\{(unité|nombre|nb)\s{0,4}\|/i ) == -1 ) {  // Template's name checking.
                    return txt;
                }

                var tmplTxt = clearTemplate( txt ),  // Ex.: "{{nb|2|ans}}" becomes "2 ans".
                spaceIndex = tmplTxt.indexOf( ' ' );

                if ( spaceIndex > 0 ) {
                    let valuePart = tmplTxt.slice( 0, spaceIndex );

                    if ( valuePart.search( /[\.,]/ ) == -1 ) {
                        if ( spaceIndex > 3 ) {  // More than three digits number.
                            return txt;
                        }
                    }
                    else {
                        let valueParts = valuePart.split( /[\.,]/ );

                        if ( valueParts.length > 2 ) {
                            return txt;  // Abnormal case.
                        }
                        else {
                            if ( valueParts[0].length > 3 || valueParts[1].length > 3 ) {
                                return txt;  // More than three digits number.
                            }

                            txt = txt.replace( /[\.]/, ',' );
                        }
                    }
                }

                if ( spaceIndex <= 2 ) {  // One or two digits number.
                    value = Number( tmplTxt[0] + tmplTxt[1].trim() );

                    if ( value <= 16 ) {
                        return mw.libs.frDigits[ value - 1 ] + tmplTxt.slice( spaceIndex );
                    }
                }

                return txt.replace( /\{\{(unité|nombre|nb)\s{0,4}\|/i, '{{nobr@' )
                                 .replace( /\|/ig, ' ' )
                                 .replace( /nobr@/, 'nobr|' );
            },
            // Include extra templates in main template (ex.: "{{en}} {{Lien web|" becomes "{{Lien web|langue=en|").
            normalizeReference = function ( editorText ) {
                var pattern = /(\{\{pdf\}\})?\s{0,4}((\{\{[a-z]{2}\}\}\s{0,4}){1,5})\{\{(lien\s?web|ouvrage|article|lien brisé|chapitre)\s{0,4}\|/ig;

                return editorText.replace( pattern, function ( match, capture1, capture2, capture3, capture4 ) {
                    var result;

                    capture2 = capture2.trim();
                    capture2 = capture2.slice( 2, capture2.length - 2 ).replace( /\}\}\s{0,4}\{\{/g, '+' );
                    result = '{{' + capture4 + '|langue=' + capture2 + '|';

                    if ( capture1 && capture1.search( /pdf/i ) >= 0 ) {
                        result += 'format=pdf|';
                    }

                    return result;
                } )
                .replace( /((\{\{[a-z]{2}\}\}\s{0,4}){0,5})(\{\{pdf\}\})\s{0,4}\{\{(lien web|ouvrage|article|chapitre)\s{0,4}\|/ig, function ( match, capture1, capture2, capture3, capture4 ) {
                    var result;

                    if ( capture1 ) {
                        capture1 = capture1.trim();
                        capture1 = capture1.slice( 2, capture1.length - 2 ).replace( /\}\}\s{0,4}\{\{/g, '+' );
                        result = '{{' + capture4 + '|langue=' + capture1 + '|format=pdf|';
                    }
                    else {
                       result = '{{' + capture4 + '|format=pdf|';
                    }

                    return result;
                } )
                .replace( /\|\s{0,4}(date|consulté le)\s{0,4}=\s{0,4}((1\{\{)|(\{\{1))er\}\}/ig, '|$1=1' )
                .replace( /\|\s{0,4}(date|consulté le)\s{0,4}=\s{0,4}(\{\{date[^\}]+\}\})/ig, function ( match, capture1, capture2 ) {
                    return '|' + capture1 + '=' + clearTemplate( capture2 ).replace( '1er ', '1 ' );
                } )
                .replace( /(\|langue=[a-z]{2})\|\s{0,4}langue\s{0,4}=\s{0,4}[a-z]{2}/gi, '$1' ); // Remove duplicates.
            },
            // Special function executing code temporarily useful.
            // Current action: arrange artilce about swedish localities.
            specialFunction = function ( editorText ) {
                return editorText.replace( 'per km<sup>2</sup>', 'per {{unité|km2}}' )
                                .replace( '|taille=30', '' )
                                .replace( /unité\|(\d{2,3})\|/i, 'nobr|$1 ' );
            },
            // Replace "|" in "date" and "unité" templates (ex.: "{{date|24|juin|2001}}" becomes "{{date|24 juin 2001}}".
            removePipes = function ( editorText ) {
                // Replace the "nombre" template, a redirect to the "unité" template.
                if ( editorText.search( /\{\{(nombre|nb)/gi ) >= 0 ) {
                    editorText = editorText.replace( /\{\{(nombre|nb)/gi, '{{unité' );
                }

                // Replace "1er" by "1" in each "date" template.
                if ( editorText.search( /\{\{\s?date-?\|\s?((\{\{1er\}\})|(1\{\{er\}\})|(1er))/gi ) >= 0 ) {
                    editorText = editorText.replace( /\{\{\s?(date-?)\|\s?((\{\{1er\}\})|(1\{\{er\}\})|(1er))/gi, '{{$1|1' );
                }

                // In case of a selected text.
                if ( globalAction === false && !editorText.endsWith( '}}}}' ) ) {
                    // Add a "," so that the pattern will be below recognised.
                    editorText += ',';
                }

                // Replace "|" in "unité" or "date" templates.
                return editorText.replace( /(\{\{\s?(unité|date-?)\|)([^\}]+\}\})([\s\.,\/\)<]|\}\})/ig,
                    function ( match, capture1, capture2, capture3, capture4 ) {
                        var matchIsDate = false, result;

                        // No "|" to remove.
                        if ( capture3.indexOf( '|' ) < 0 ) {
                            // In case of a selected text.
                            if ( globalAction === false && capture4 == ',' ) {
                                // Discard the added ",".
                                match = match.slice( 0, match.length - 1 );
                            }

                            return match;
                        }

                        if (capture2.toLowerCase().startsWith( 'date' ) ) {
                            matchIsDate = true;

                            if ( capture3.indexOf( '=' ) >= 0 ) {
                                // At least one extra option is defined.
                                return match;
                            }
                            else if ( capture3.indexOf( 'en ' ) >= 0
                                     || capture3.indexOf( 'dans ' ) >= 0
                                     || capture3.indexOf( 'au ' ) >= 0 ) {
                                // A qualification is defined.
                                return match;
                            }
                        }
                        else {
                            if ( capture3.indexOf( '°C' ) >= 0 ) {
                                // A temperature.
                                return match;
                            }

                            // Swith to french number notation for the "unité" template and ajust exponent if any.
                            capture3 = capture3.trim().replace( /(,(\d{3}))?,(\d{3})\.(\d{1,6})\s?\|/, '$2$3,$4|' )
                                                      .replace( /(\d{1,9})\.(\d{1,6})\s?\|/, '$1,$2|' )
                                                      .replace( /\{\{(-?[1-3])\}\}/, ' $1' );
                        }

                        // Spare "|" in an internal link.
                        if ( capture3.search( /\|[^\[\]\|]+\]\]/i ) >= 0 ) {
                            capture3 = capture3.replace( /\|([^\[\]\|]+\]\])/i, '@Z@$1' );
                        }

                        // In case of a selected text.
                        if ( globalAction === false && capture4 == ',' ) {
                            // Discard the added ",".
                            result = capture1 + capture3.replace( /\s?\|\s?/g, ' ' ).trim().replace( /@Z@/, '|' );

                            if ( matchIsDate ) {
                                return result;
                            }

                            return result.replace( /\s(\d\}\})/, '$1' );
                        }

                        result = capture1 + capture3.replace( /\s?\|\s?/g, ' ' ).trim().replace( /@Z@/, '|' ) + capture4;

                        if ( matchIsDate ) {
                            return result;
                        }

                        return result.replace( /\s(\d\}\})/, '$1' );
                } );
            },
            // Add "{{nobr|...}}" globally or on a selected text.
            addNoBrTemplate = function ( editorText ) {
                // Control the main captures of the replace calls below.
                var ctrlCapture = function ( capture ) {
                    var captureToken,
                    discardedWords = [ 'à', 'et', 'ou', 'en', 'au', 'du', 'de', "d'", 'pour', 'dans' ];

                    // Avoid to format dates and strings like "XX et/à/ou/de".
                    if ( capture ) {
                        captureToken = capture.split( ' ' )[1];

                        if ( captureToken && ( mw.libs.frMonths.indexOf( captureToken.toLowerCase() ) === -1
                                            && discardedWords.indexOf( captureToken ) === -1 ) ) {
                            return true;
                        }
                        
                        return false;
                    }
                },
                getNewText = function ( match, capture ) {
                    if ( ctrlCapture( capture ) ) {
                        return match.replace( capture, '{{nobr|' + capture + '}}' );
                    }

                    return match;
                };

                // In case of a selected text.
                if ( globalAction === false ) {
                    return '{{nobr|' + editorText + '}}';
                }

                // In case of the whole text.
                // Ex.: replace " 0 à 24 ans " by " 0 à {{nobr|24 ans}} ".
                editorText = editorText.replace( /([>\*,:;a-zàé]\s|['\(])(?:\d{1,3}|\d{1,3},\d{1,3})\s(à|et|ou|en|au|du|de)\s((?:\d{1,3}|\d{1,3},\d{1,3})\s[éèêïîû/a-z]+)([\s\.,<'\)])/ig,
                    function ( match, capture1, capture2, capture3, capture4 ) {
                    return getNewText( match, capture3 );
                } );

                // Ex.: replace " 24 km " by " {{nobr|24 km}} ".
                editorText = editorText.replace( /([>\*,:;a-zàé]\s|['\(]|\d{1,3}-)((?:\d{1,3}|\d{1,3},\d{1,3})\s[éèêïîû/a-z]+)([\s\.,<'\)])/ig,
                    function ( match, capture1, capture2, capture3 ) {
                        return getNewText( match, capture2 );
                } );

                return editorText;
            },
            // Remove "'''" tags.
            removeBoldTags = function ( editorText ) {
                return editorText.replace( /([^'])'''([a-z\d])'''([^'])/ig, '$1$2$3' )
                                 .replace( /([^'])'''([^'])/ig, '$1$2' )
                                 .replace( /([ld])''''([^'])/ig, "$1'$2" )
                                 .replace( /'''''([^'])/ig, "''$1" );
            },
            // Remove "''" tags.
            removeItalicTags = function ( txt ) {
                if ( txt && txt.startsWith( "''" ) && txt.endsWith( "''" ) ) {
                    txt = txt.slice( 2, txt.length - 2 );
                }

                return txt;
            },
            // Remove template's markers (ex.: "{{unité|14|heures}}" becomes "14 heures").
            clearTemplate = function ( txt ) {
                if ( txt && txt.startsWith( '{{' ) && txt.endsWith( '}}' ) ) {
                    if ( txt.indexOf( '|' ) >= 0 ) {
                        if ( txt.startsWith( '{{lang' ) ) {
                            txt = txt.replace( /\|/, '' );
                        }

                        return txt.slice( txt.indexOf( '|' ) + 1, txt.length - 2 ).replace( /\|/g, ' ' );
                    }

                    return txt.slice( 2, txt.length - 2 );
                }

                return txt;
            },
            // Add globally spaces in sections's titles when needed (ex.: "==ABC==" becomes "== ABC ==").
            formatSectionTitle = function ( editorText ) {
                return editorText.replace( /^(={2,5})([^\s=])/igm, '$1 $2' )
                                 .replace( /([^\s=])(={2,5})$/igm, '$1 $2' );
            },
            // Format globally any "av. J.-C." expression.
            formatAvJC = function ( editorText ) {
                return editorText.replace( /av\.?\s?J\.?-?C\.?/ig, '{{av JC}}' );
            },
            // Selection cleaner function for the "heure" template.
            removeHourSymbol = function ( txt ) {
                if ( txt ) {
                    return txt.replace( /\s?h(eure)?s?(\set)?\s?/i, ' ' )
                                .replace( /\s?min(ute)?s?(\set)?\s?/i, ' ' );
                }
                
                return txt;
            },
            // Selection cleaner function for the "unité" template.
            cleanBeforeAddingUnitTemplate = function ( txt ) {
                if ( txt ) {
                    // Remove the HTML space.
                    txt = removeSpaceInNumber( txt );

                    if ( txt.indexOf( 'm²' ) > 0 ) {
                        txt = txt.replace( 'm²', 'm2' );
                    }
                    else if ( txt.indexOf( '<sup>2</sup>' ) > 0 ) {
                        txt = txt.replace( '<sup>2</sup>', '2' );
                    }
                }
                
                return txt;
            },
            // Remove useless spaces in a string containing some numbers.
            removeSpaceInNumber = function ( txt ) {
                if ( txt ) {
                    // Remove the HTML space.
                    txt = txt.replace( '&nbsp;', ' ' );

                    if ( txt.search( /\d\s\d/gi ) >= 0 ) {
                        return txt.replace( /(\d)\s(\d)/gi, '$1$2' );
                    }
                }
                
                return txt;
            },
            // Convert an external link to a wiki reference.
            convertToReference = function ( txt ) {
                var protocolIdx, titleIdx, title;

                // External link example: [http://www.site.fr/mapage.html ma page].
                if ( txt && txt.startsWith( '[' ) && txt.endsWith( ']' ) ) {
                    protocolIdx = txt.indexOf( '://' );

                    if ( protocolIdx > 0 ) {
                        // Get the link's title.
                        titleIdx = txt.indexOf( ' ', protocolIdx );

                        if ( titleIdx > 0 ) {
                            title = txt.substring( titleIdx + 1, txt.length - 1 ).trim();

                            if ( title ) {
                                return title + '<ref>' + txt + '.</ref>';
                            }
                        }
                    }
                }
                
                return txt;
            },
            // Convert a formal reference of an official website to a templated one.
            convertToOfficelTemplate = function ( txt ) {
                var protocolIdx, titleIdx, url, urlIdx, languageCode = '', languageCodeIdx;

                // Example: * {{ja}} [http://www.town.koya.wakayama.jp/ Site officiel de Kōya].
                if ( txt && txt.startsWith( '*' ) && txt.endsWith( ']' ) ) {
                    protocolIdx = txt.indexOf( '://' );

                    if ( protocolIdx > 0 ) {
                        // Get the url.
                        titleIdx = txt.indexOf( ' ', protocolIdx );
                        urlIdx = txt.lastIndexOf( 'h', protocolIdx );

                        if ( titleIdx > 0 && urlIdx > 0 ) {
                            url = txt.substring( urlIdx, titleIdx );

                            if ( url ) {
                                languageCodeIdx = txt.search( /\{\{[a-z]{2}\}\}/ );

                                if ( languageCodeIdx > 0 ) {
                                    languageCode = txt.slice( languageCodeIdx + 2, languageCodeIdx + 4 );
                                }

                                return '* {{Site officiel|langue=' + languageCode + '|url=' + url + '}}';
                            }
                        }
                    }
                }
                
                return txt;
            },
            // Internal link dewikification.
            removeInternalLink = function ( txt, pageName ) {
                if ( txt && txt.startsWith( '[[' ) && txt.endsWith( ']]' ) ) {
                    txt = txt.slice( 2, txt.length - 2 );

                    if ( txt.indexOf( '|' ) > 0 ) {
                        if ( pageName ) {
                            return txt.slice( 0, txt.indexOf( '|' ) );
                        }

                        return txt.slice( txt.indexOf( '|' ) + 1 );
                    }
                }
                
                return txt;
            },
            // Remove useless spaces in a selected text (typically: " | ", " }}" and " = ".
            removeUselessSpaces = function ( txt ) {
                if ( txt ) {
                    if ( /(\s{1,8}\|\s{1,8})|(\s{1,8}\|)|(\|\s{1,8})/.test( txt ) ) {
                        txt = txt.replace( /(\s{1,8}\|\s{1,8})|(\s{1,8}\|)|(\|\s{1,8})/g, '|' );
                    }

                    if ( /(\s{1,8}\|\s{1,8})|(\s{1,8}=)|(=\s{1,8})/.test( txt ) ) {
                        txt = txt.replace( /(\s{1,8}=\s{1,8})|(\s{1,8}=)|(=\s{1,8})/g, '=' );
                    }

                    if ( /\s{1,8}\}\}/.test( txt ) ) {
                        return txt.replace( /\s{1,8}\}\}/, '}}' );
                    }
                }
                
                return txt;
            },
            // Replace a "formatnum" template by an "Unité" one, when relevant.
            replaceFormatnumByUnit = function ( txt ) {
                if ( txt ) {
                    if ( txt.toLowerCase().startsWith( '{{nombre|' ) ) {
                        txt = txt.replace( '{{nombre|', '{{formatnum:' );
                    }

                    if ( txt.indexOf( 'm²' ) > 0 ) {
                        txt = txt.replace( 'm²', 'm2' );
                    }
                }

                if ( txt && txt.toLowerCase().startsWith( '{{formatnum:' ) ) {
                    // Switch to decimal french notation.
                    txt = txt.replace( '&nbsp;', ' ' ).replace( /(\d)\.(\d)/g, '$1,$2' );

                    // {{unité|10000 à 15000 tonnes}} pour {{formatnum:10000}} à {{Unité|15000 tonnes}}
                    if ( txt.search( /\s(à|entre|et|ou)\s/i ) >= 0 ) {
                        txt = '{{unité|' + txt.replace( /(\{\{formatnum:)|(\{\{(unité|nombre)\|)/ig, '' )
                                               .replace( /\}\}/g, '' ).replace( '  ', ' ') + '}}';
                    }
                    else {
                        //Ex.: "{{formatnum:5000}} euros" into "{{unité|5000 euros}}".
                        txt = txt.split( ':' )[1].replace( /\}\}\s{1,3}/, ' ' );

                        // Format the current text with the "Unité" template.
                        txt = '{{unité|' + txt + '}}';
                    }

                    if ( txt.split( '|' ).length >= 3 ) {
                        return removePipes( txt );
                    }
                }
                
                return txt;
            },
            // Add a "formule chimique" template (ex. : "C<sub>7</sub>H<sub>5</sub>O" devient "{{formule chimique|C|7|H|5|O}}").
            addFormuleChimiqueTemplate = function ( txt ) {
                if ( txt && txt.indexOf( '<sub>' ) > 0 ) {
                    txt = '{{formule chimique|' + txt.replace( /<\/?sub>/g, '|' ) + '}}';
                    txt = txt.replace( '|}}', '}}' ).replace( '=', '{{=}}' );
                }
                
                return txt;
            },
            // Replace a "{{...}} ABC" expression by "{{...|ABC}}".
            compactTemplate = function ( txt ) {
                if ( txt && txt.startsWith( '{{' ) ) {
                    //Ex.: "{{2e}} étape" into "{{2e|étape}}".
                    return txt.replace( /\}\}\s{0,2}/, '|' ).concat( '}}' );
                }

                return txt;
            },
            // Replace a "p.(/page) nnn" expression by "{{p.|nnn}} (note: "p", "pp" or "pp." are converted to "p.").
            addPageTemplate = function ( txt ) {
                if ( txt && txt.startsWith( 'p' ) ) {
                    //Ex.: "p. 254" into "{{p.|254}}".
                    return txt.replace( /page(s)?/, 'p.' ).replace( /pp?\.?\s{0,2}/i, '{{p.|' ).concat( '}}' );
                }

                return txt;
            },
            // format a list according to french typographical conventions.
            formatList = function ( txt ) {
                if ( txt && txt.startsWith( '*' ) ) {
                    txt = txt.replace( /^\*\s{0,2}([^\s])/mg, function ( match, capture1 ) {
                        return '* ' + capture1.toLowerCase();
                    } ).replace( /\s{0,3}[\.,;\s]\s{0,3}$/mg, ' ;' )
                       .replace( /([^\.,;\n])$/mg, '$1 ;' );

                    return txt.substr( 0, txt.lastIndexOf( ' ;' ) ) + '.';
                }

                return txt;
            },
            // Extract a list of related articles from a string contening some internal links.
            makeRelatedArticlesList = function ( txt ) {
                if ( txt ) {
                    var startLinkTagIdx = 0, endLinkTagIdx = 0, internalLink, linksList = '';

                    // Add a "." just before the template's end tag.
                    while ( startLinkTagIdx >= 0 ) {
                        startLinkTagIdx = txt.indexOf( '[[', endLinkTagIdx );

                        if ( startLinkTagIdx >= 0 ) {
                            endLinkTagIdx = txt.indexOf( ']]', startLinkTagIdx + 2 );

                            if ( endLinkTagIdx >= 0 ) {
                                internalLink = txt.substring( startLinkTagIdx, endLinkTagIdx + 2 );
                                internalLink = removeInternalLink( internalLink, true );
                                internalLink = internalLink.slice( 0, 1 ).toUpperCase() + internalLink.slice( 1 );
                                linksList += '\n* [[' + internalLink + ']]';
                            }
                        }
                    }
                    
                    if ( linksList ) {
                        return linksList.replace( '\n', '' );
                    }
                }

                return txt;
            },
            // format a gallery according to french typographical conventions and wiki standards.
            formatGallery = function ( txt ) {
                if ( txt ) {
                    txt = txt.replace( /\s{1,3}$/mg, '' )
                             .replace( /\s?\|\s/mg, '|' );

                    if ( txt.search( /center\s?>/i ) >= 0 ) {
                        txt = txt.replace( /\.?\s{0,3}<\/center\s?>/gi, '.</center>' );
                    }

                    txt = txt.replace( /([^>\.])$/mg, '$1.' );
                }

                return txt;
            },
            // Format an interlanguage link (e.g.: "[[:en:Abc|Cba" to "{{lien|langue=en|trad=Abc|fr=Cba...").
            formatInterlangLink = function ( txt ) {
                if ( txt && txt.startsWith( '[[:' ) && txt.endsWith( ']]' ) ) {
                    // No alternate text to the wiki link.
                    if ( txt.indexOf( '|' ) < 0 ) {
                        return txt.replace( /\[\[:([a-z]{2}):/, '{lien|langue=$1|trad=' ).replace( ']]', '}}' );
                    }

                    txt.replace( /\[\[:([a-z]{2}):([^\|]+)\|([^\]]+)\]\]/i,
                        function ( match, capture1, capture2, capture3 ) {
                            txt = '{{lien|langue=' + capture1 + '|trad=' + capture2;

                        if ( capture2.toLowerCase() != capture3.toLowerCase() ) {
                            txt = txt + '|fr=' + capture3;
                        }

                        txt = txt + '}}';
                    } );
                }

                return txt;
            },
            // Format century.
            addCenturyTemplate = function ( txt ) {
                var centuries = '', first =true;

                if ( txt && txt.search( /\set\s/i ) >= 0 ) {
                    // Handle two centuries in one template (ex.: "XIe et XIIe siècles" becomes "{{s2-|XI|XII}}").
                    if ( txt && txt.search( /[IXV]/ ) >= 0 ) {
                        txt.replace( /[IXV]{1,6}/g,
                            function ( match ) {
                                if ( first ) {
                                    centuries = '{{s2-|' + match + '|';
                                    first = false;
                                }
                                else {
                                    centuries += match + '}}';
                                }
                        } );
                    }

                    txt = centuries;
                }
                else if ( txt && txt.search( /[IXV]/ ) >= 0 ) {
                // Handle one century (ex.: "XIe siècle" becomes "{{s-|XI}}").
                    txt.replace( /[IXV]{1,6}/,
                        function ( match ) {
                            txt = '{{s-|' + match + '}}';
                    } );
                }

                return txt;
            },
            // Add "date" template to a selected text or globally (ex.: "1er mai [[1981]]" becomes "{{date|1 mai [[1981}}").
            addDateTemplate = function ( editorText, templateName ) {
                var datePattern = /(\s|\()(\{\{1er\}\}|[1-9]|[1-3][0-9])\s[adfjmnos][aceéouv][cinprtûv][beilmnorstv]{0,6}\s[1-9][0-9]{2,3}/gi,
                alterDatePattern = /(\d\d?)(\/|-)(\d\d?)(\/|-)(\d{2,4})/;

                // In case of a selected text.
                if ( globalAction === false ) {
                    if ( editorText ) {
                        // Clean dates like "31/05/1940" or "05/12/2010".
                        if ( editorText.search( /(\d\d?)[\/-](\d\d?)[\/-](\d{4})/i ) >= 0 ) {
                            editorText = editorText.replace( /(\d\d?)[\/-](\d\d?)[\/-](\d{4})/i, function ( match, capture1, capture2, capture3 ) {
                            return capture1 + ' ' + mw.libs.frMonths[ parseInt( capture2 ) - 1 ] + ' ' + capture3;
                            } );
                        }

                        // Clean dates like "{{1er}} octobre 1940".
                        if ( editorText.search( /(\{\{1er\}\})|(1er)/i ) >= 0 ) {
                            editorText = editorText.replace( /(\{\{1er\}\})|(1er)/i, '1' );
                        }

                        // Remove wiki link tags for the year.
                        if ( editorText.search( /\[\[(\d{4})\]\]/ ) >= 0 ) {
                            editorText = editorText.replace( /\[\[(\d{4})\]\]/, '$1' );
                        }
                        
                        // Clean dates like "{{1er}} octobre 1940".
                        if ( editorText.search( alterDatePattern ) >= 0 ) {
                            editorText = editorText.replace( alterDatePattern, function ( match, capture1, capture2, capture3, capture4, capture5 ) {
                            return capture1 + ' ' + mw.libs.frMonths[ parseInt( capture3 ) - 1 ] + ' ' + capture5;
                            } );
                        }

                        editorText = toggleTextToLowerCase( editorText );

                        return mw.libs.fillTemplate( templateName, [ editorText ] );
                    }
                }
                else if ( editorText ) {
                    editorText = editorText.replace( datePattern, function ( match ) {
                        var newText = toggleTextToLowerCase( match.replace( /(\{\{1er\}\})|(1er)/i, '1' ) );

                        return " " + mw.libs.fillTemplate( templateName, [ newText.slice( 1 ) ] );
                    } );
                }

                return editorText;
            },
            // Add "formatnum" template to a selected text.
            formatNum = function ( txt ) {
                if ( txt ) {
                    // Format the number.
                    txt = txt.replace( /\s/g, '' ).replace( ',', '.' );
                    
                    if ( $.isNumeric( txt ) ) {
                        return '{{formatnum:' + txt + '}}';
                    }
                }

                return txt;
            },
            // Add "Dunit" template to a selected text (ex.: "20 × 15 m" becomes "{{dunité|20|15|m}}").
            // NB: expressions ending with an squared, cubed, etc. unit like "10 × 10 km2" is not handled...
            addDunitTemplate = function ( txt ) {
                var lastChar, unit;

                if ( txt && txt.search( /\s?[Xx×]\s?/ ) > 0 ) {

                    // Format numbers.
                    txt = txt.replace( /(\d)\s(\d)/g, '$1$2' );
                    txt = '{{dunité|' + txt.replace( /\s?[Xx×]\s?/, '|' );
                    lastChar = txt.slice( txt.length - 1 );

                    // No unit at then end of the string.
                    if ( $.isNumeric( lastChar ) ) {
                        return txt + '}}';
                    }

                    // A unit is available at the end of the string.
                    unit = txt.slice( txt.lastIndexOf( ' ' ) ).trim();
                    txt += '}}';

                    return txt.replace( ' ' + unit + '}}', '|' + unit + '}}' );
                }
            },
            // Toggle a selected text to lower case.
            toggleTextToLowerCase = function ( txt ) {
                if ( txt ) {
                    return txt.toLowerCase();
                }

                return txt;
            },
            // Change ''«...»'' into « ''...'' ».
            arrangeQuotes = function ( txt ) {
                if ( txt ) {
                    if ( ( txt.startsWith( "''«" ) && txt.endsWith( "»''" ) ) 
                        || ( txt.startsWith( '"«' ) && txt.endsWith( '»"' ) ) 
                        || ( txt.startsWith( '``«' ) && txt.endsWith( '»´´' ) ) 
                        || ( txt.startsWith( '‘‘«' ) && txt.endsWith( '»’’' ) ) 
                        || ( txt.startsWith( '“«' ) && txt.endsWith( '»”' ) ) ) {
                        return txt.replace( /["'‘`“]{1,2}«\s?/, "« ''" ).replace( /\s?»["'’´”]{1,2}/, "'' »" );
                    }
                }

                return txt;
            },
            // Add "<ref>... .</ref>" tags to a selected text.
            addReferenceTags = function ( txt ) {
                if ( txt ) {
                    txt = txt.trim();

                    if ( txt.startsWith( '(' ) && txt.endsWith( ')' ) ) {
                        txt = txt.substr( 1, txt.length - 2 );
                    }

                    return '<ref>' + txt + '.</ref>';
                }

                return txt;
            },
            // 'Encapsulate a selected texte in the "{{incise|...}}" template.'
            addInciseTemplate = function ( txt ) {
                if ( txt ) {
                    txt = txt.trim();

                if ( txt.search( /^[\-–—]/ ) === 0 ) {
                    // Note: three variations of « tiret cadratin » are checked.
                    txt = txt.replace( /^[\-–—]\s{0,3}/, '{{incise|' );

                    if ( txt.search( /[\-–—]$/ ) === txt.length - 1 ) {
                        return txt.replace( /\s{0,3}[\-–—]$/, '}}' );
                    }

                    if ( txt.endsWith( '.' ) ) {
                        return txt.replace( /\.$/, '|stop}}.' );
                    }

                    return txt + '}}';
                }
                }

                return txt;
            },
            // Surround a selected text by "{{" and "}}".
            addWikiTemplateTags = function ( txt ) {
                if ( txt ) {
                    return '{{' + txt + '}}';
                }

                return txt;
            },
            // Surround by "{{" and "}}" and replace the middle blank or "&nbsp;" by a "|".
            addWikiTemplateTagsAndMiddlePipe = function ( txt ) {
                if ( txt ) {
                    return addWikiTemplateTags( txt ).toLowerCase().replace( /(&nbsp;)|\s/, '|' );
                }

                return txt;
            },
            // Insert a parameter's name, if available, and a value to "|".
            addParamAndValue = function ( txt, paramName, paramValue ) {
                if ( txt == '|' ) {
                    if ( paramName ) {
                        return '|' + paramName + '=' + paramValue + '|';
                    }
                    else {
                        return '|' + paramValue + '|';
                    }
                }

                return txt;
            },
            // A function to replace english quotation marks by french ones or add french quotation marks.
            addFrenchQuotes = function ( txt ) {
                if ( txt) {
                    if ( txt.search( /^(‘‘)|(``)/ ) === 0 ) {
                        txt = '« ' + txt.substring( 2 );
                    }
                    else if ( txt.search( /^["“‘]/ ) === 0 ) {
                        txt = '« ' + txt.substring( 1 );
                    }
                    else {
                        return '« ' + txt.trim() + ' »';
                    }

                    if ( txt.search( /(’’)|(´´)$/ ) === txt.length - 1 ) {
                        txt = txt.substring( 0, txt.length - 2 ) + ' »';
                    }
                    else if ( txt.search( /["”’]$/ ) === txt.length - 1 ) {
                        txt = txt.substring( 0, txt.length - 1 ) + ' »';
                    }
                }

                return txt;
            },
            // Format a selected text with a template.
            addTemplate = function ( selectedText, tmplDescriptor ) {
                var paramValues = [];

                if ( selectedText ) {
                    selectedText = selectedText.trim();

                    if ( selectedText ) {
                        // tmplDescriptor.paramsCount = 1, the selected text is handled as one template's parameter.
                        // tmplDescriptor.paramsCount = -1, the selected text is a set of string tokens separated by a space character ; each token is a template's parameter value.
                        if ( tmplDescriptor.paramsCount == -1 ) {
                            if ( selectedText.indexOf( ' ' ) >= 0 ) {
                                paramValues = selectedText.split( ' ' );
                            }
                            else {
                                paramValues.push( selectedText );
                            }
                        }
                        else if ( tmplDescriptor.paramsCount == 1 ) {
                            paramValues.push( selectedText );
                        }

                        if ( tmplDescriptor.suffix ) {
                            paramValues.push( tmplDescriptor.suffix );
                        }

                        selectedText = mw.libs.fillTemplate( tmplDescriptor.name, paramValues );
                    }
                }

                return selectedText;
            },
            // Apply a template to the current selection.
            applyTemplate = function ( tmplDescriptor, currentSelection ) {
                if ( currentSelection && currentSelection.length > 0 ) {
                    if ( tmplDescriptor.cleanSelection !== null ) {
                        //Arrange the selected text before applying the template.
                        currentSelection = tmplDescriptor.cleanSelection( currentSelection );
                    }

                    if ( tmplDescriptor.addTmpl !== null ) {
                        // A non standard template's application on current selection.
                        if ( tmplDescriptor.addTmplDeferred ) {
                            // Get user's input first (the selection's update is deferred).
                            tmplDescriptor.addTmpl( currentSelection ).done( function ( txt ) {
                                updateSelection( txt );
                            } );
                        }
                        else {
                            updateSelection(  tmplDescriptor.addTmpl( currentSelection ) );
                        }
                    }
                    else {
                        // Standard template's application on current selection.
                        updateSelection( addTemplate( currentSelection, tmplDescriptor ) );
                    }
                }
            },
            // Update a selected text.
            updateSelection = function ( newText ) {
                if ( newText ) {
                    //Replace the selected text with the new text.
                    $wikiEditor.textSelection( 'encapsulateSelection', { pre: newText, replace: true } );
                }
            },
            ddlWikitextOptions = [],
            ddlInternalLinkOptions = [],
            ddlTemplatesOptions = [],
            ddlBasicOption = {
                label: '',
                action: {
                    type: '',
                    options: {}
                }
            },
            // Some wikitext blocks ready to be added in article.
            wikitextOptionsList = [
                { label: '|frmt pdf', wikitext: '|format=pdf' },
                { label: '|auteur insti.', wikitext: '|auteur institutionnel=' },
                { label: '|éditeur', wikitext: '|éditeur=' },
                { label: '|langue en', wikitext: '|langue=en' },
                { label: 'Réf. date', wikitext: '|année=|mois=|jour=' },
                { label: 'Access date', wikitext: 'consulté le=' + mw.libs.getCurrentDate() },
                { label: '{{pdf}}', wikitext: '{{pdf}} ' },
                { label: '|redresse', wikitext: '|redresse' },
                { label: 'Notes et réfs', wikitext: '== Notes et références ==\n{{Références}}' },
                { label: 'Notes et réfs+', wikitext: '== Notes et références ==\n=== Notes lexicales bilingues ===\n{{Références|groupe=l}}\n\n=== Références ===\n{{Références}}' },
                { label: 'See also', wikitext: '== Voir aussi ==\n=== Articles connexes ===' },
                { label: 'Liens externes', wikitext: '=== Liens externes ===' },
                { label: '{{Officiel}}', wikitext: '* {{Site officiel|langue=|url=|titre=Site officiel d}}' },
                { label: 'Géo > Démo', wikitext: '== Géographie ==\n=== Démographie ===' },
                { label: 'Histo + Éco', wikitext: '== Histoire ==\n== Économie ==' }
            ],
            // Some internal links ready to be added in article.
            internalLinkOptionsList = [
                { label: 'AFP', wikitext: 'Agence France-Presse|AFP' },
                { label: 'Le Monde', wikitext: 'Le Monde' },
                { label: 'Libération', wikitext: 'Libération (journal)|Libération' },
                { label: "L'Obs", wikitext: "L'Obs" },
                { label: 'Le Figaro', wikitext: 'Le Figaro' },
                { label: 'Le Parisien', wikitext: 'Le Parisien' },
                { label: 'France Info', wikitext: 'France Info (offre globale)|France Info' },
                { label: 'Le Point', wikitext: 'Le Point' },
                { label: "L'Express", wikitext: "L'Express" },
                { label: 'Les Échos', wikitext: 'Les Échos' },
                { label: 'France 3', wikitext: 'France 3' },
                { label: 'Sud Ouest', wikitext: 'Sud Ouest' },
                { label: 'Ouest-France', wikitext: 'Ouest-France' },
                { label: 'La Dépêche', wikitext: 'La Dépêche du Midi' },
                { label: 'NYT', wikitext: 'The New York Times' },
                { label: 'Le HuffPost', wikitext: 'Le HuffPost' },
                { label: 'NASA', wikitext: 'National Aeronautics and Space Administration|NASA' }
            ],
            // Add a dropdownlist to the wikiEditor's toolbar.
            addDropdownlistToToolbar = function ( ddlName, ddlLabel, options ) {
                var toolsObject = {};

                if ( ddlName && options && options.length > 0 ) {
                    toolsObject[ ddlName ] = { label: ddlLabel,
                        type: 'select',
                        list: options,
                        icon: 'none'
                    };

                    $wikiEditor.wikiEditor( 'addToToolbar', {
                        'section': 'main',
                        'group': 'insert',
                        'tools': toolsObject
                    } );
                }
            };

            // Build the list of options for the "wikitext" dropdown list.
            wikitextOptionsList.forEach( function ( wikitextOption, idx ) {
                var optionObject = $.extend( true, {}, ddlBasicOption );

                optionObject.label = wikitextOption.label;
                optionObject.action.type = 'encapsulate';
                optionObject.action.options = {
                        pre: wikitextOption.wikitext,
                };

                ddlWikitextOptions[ idx ] = optionObject;
            } );

            // Build the list of options for the "internal links" dropdown list.
            internalLinkOptionsList.forEach( function ( internalLinkOption, idx ) {
                var optionObject = $.extend( true, {}, ddlBasicOption );

                optionObject.label = internalLinkOption.label;
                optionObject.action.type = 'replace';
                optionObject.action.options = {
                        pre: '[[',
                        peri: internalLinkOption.wikitext,
                        post: ']]'
                };
                ddlInternalLinkOptions[ idx ] = optionObject;
            } );

            // Set the buttons to be inserted in the wikiEditor's toolbar.
            tmplDescriptors.forEach( function ( tmplDescriptor, idx ) {
                var groupNamePrefix = 'templates-grp', optionObject;

                optionObject = Object.assign( 
                    {},
                    basicToolbarButton,
                    {
                    label: tmplDescriptor.linkTitle,
                    action: {
                        type: 'callback',
                        execute: function () {
                            var currentSelection = $wikiEditor.textSelection( 'getSelection' );

                            if ( tmplDescriptor.global ) {
                                if ( currentSelection ) {
                                    // Action on the current selected text.
                                    updateSelection( tmplDescriptor.globalAction( currentSelection ) );
                                }
                                else {
                                    // Action on the whole editor's text.
                                    globalAction = true;

                                    $wikiEditor.val( tmplDescriptor.globalAction( $wikiEditor.val() ) );

                                    globalAction = false;
                                }
                            }
                            else {
                                // Action on a selected text.
                                applyTemplate( tmplDescriptor, currentSelection );
                            }
                        }
                    }
                } );

                if ( tmplDescriptor.asButton ) {
                    // Build a toolbar's button.
                    toolbarButtonArray[ 'tmplButton' + toolbarButtonsCount ] = optionObject;

                    toolbarButtonIds.push( idx );

                    toolbarButtonsCount++;
                }
                else {
                    // Add options to the "ddlTemplates" dropdownlist.
                    ddlTemplatesOptions[ idx - toolbarButtonsCount ] = optionObject;
                }
                
                // Create a buttons' group.
                if ( toolbarButtonsCount > 0 
                    && ( toolbarButtonsCount % buttonsPerGroup == 0
                        || idx == tmplDescriptors.length - 1 ) ) {
                    toolbarButtonGroups[ groupNamePrefix + groupsCount ] = { tools: toolbarButtonArray };
                    toolbarButtonArray = {};

                    groupsCount++;
                }
            } );

            if ( ddlWikitextOptions.length > 0 ) {
                // Add the "ddlWikitext" dropdownlist to the main toolbar.
                addDropdownlistToToolbar( 'ddlWikitext', 'Wikitext', ddlWikitextOptions );
            }

            if ( ddlInternalLinkOptions.length > 0 ) {
                // Add the "ddlInternalLinks" dropdownlist to the main toolbar.
                addDropdownlistToToolbar( 'ddlInternalLinks', 'InternalLinks', ddlInternalLinkOptions );
            }

            if ( ddlTemplatesOptions.length > 0) {
                // Add the "ddlTemplates" dropdownlist to the main toolbar.
                addDropdownlistToToolbar( 'ddlTemplates', 'Templates', ddlTemplatesOptions );
            }

            // Add a new toolbar section with some groups of buttons.
            $wikiEditor.wikiEditor( 'addToToolbar', {
                sections: {
                    templatesRibbon: { // Add a new section.
                        type: 'toolbar',
                        label: 'Modèles',
                        filters: [ 'body.ns-0' ],    // Only display the toolbar on the main namespace.
                        groups: toolbarButtonGroups
                    }
                }
            } );

            for ( var rowsCount = 1, offset = 0; rowsCount < groupsCount ; rowsCount++ ) {
                offset = buttonsPerGroup * ( rowsCount - 1 );
                divGroup = 'div.group-templates-grp' + rowsCount + ' > a';

                // Remove standard formatting of the toolbar's buttons.
                $( divGroup ).removeClass( 'tool tool-button' ).removeAttr( 'style' ).not( 'a:last' ).after( '<b> | </b>' );

                // Add a tooltip to each toolbar's links.
                $( divGroup ).each( function ( elmtIdx, elmt ) { // Warning: Functions declared within loops referencing an outer scoped variable may lead to confusing semantics. ($, tmplDescriptors, toolbarButtonIds, offset)
                    $( elmt ).attr( 'title', tmplDescriptors[ toolbarButtonIds[ elmtIdx + offset ] ].tooltip );
                } );
            }
        },
        DesignHack = function () {
            // Remove useless summary box's warnings.
            $( '#editpage-copywarn' ).remove();
            $( '#specialcharsets' ).remove();
            // Remove useless toolbar's options.
            $( '#wikiEditor-section-secondary' ).remove(); // Preview button.
            $( '#wikiEditor-section-advanced' ).remove();
            $( '#wikiEditor-section-characters' ).remove();
            $( '#wikiEditor-section-help' ).remove();
            $( 'span.tab.tab-advanced' ).remove();
            $( 'span.tab.tab-characters' ).remove();
            $( 'span.tab.tab-help' ).remove();
            $( '#wikiEditor-section-main > div.group.group-codemirror' ).remove();
            $( '#wikiEditor-section-main > div.group.group-insert' ).children( 'span' ).last().remove();
            $( '#wikiEditor-section-main > div.group.group-insert' ).children( 'span' ).last().remove();

            // Scroll the navigator's viewport to the wikiEditor element.
            $( 'body, html' ).animate( { scrollTop: $( '#editform' ).offset().top }, 'fast', function() {
                // Hide the left menu.
                $( '#mw-panel' ).attr( 'style','display: none;' );
                $( '#mw-navigation' ).attr( 'style','display: none;' );
                //$( 'div.vector-main-menu-container' ).attr( 'style','display: none;' );
                $( '#content' ).attr( 'style', 'width: 98%; margin-left: 0; padding: 2' );
            } );
        };

        $( document ).ready( function () {
            mw.loader.using( 'user.options' ).then( function () {
                // This can be the string "0" if the user disabled the preference ([[phab:T54542#555387]])
                if ( mw.user.options.get( 'usebetatoolbar' ) == 1 ) {
                    // Enable the wikiEditor's toolbar customization.
                    $.when(
                        mw.loader.using( 'jquery.textSelection' ),
                        mw.loader.using( 'ext.wikiEditor' )
                    ).then( customizeToolbar ).then( DesignHack );
                }
            } );
        } );
    } ) ( mediaWiki, jQuery );
}

//</nowiki>