MediaWiki:Gadget-translation editor.js
Remarque: après avoir sauvegardé, vous devez vider le cache de votre navigateur pour que les changements prennent effet. Mozilla, cliquez sur Actualiser (ou ctrl-r). Internet Explorer / Opera: ctrl-f5. Safari: cmd-r. Konqueror ctrl-r.
/**
* Assistant pour l'ajout de traductions
*
* Adapté de la version suédoise du gadget
* [[sv:MediaWiki:Gadget-translation editor.js]] (oldid=1687182)
*
* elle-même adaptée de la version anglaise
* [[en:User:Conrad.Irwin/editor.js]]
*
* Auteur : [[sv:User:Skalman]]
* Adaptation : [[fr:User:Automatik]] ([[Utilisateur:Automatik/translation editor.js/Adaptation]])
*
* [[Catégorie:JavaScript du Wiktionnaire|translation editor]]
*/
/* global $, mw, editor, silentFailStorage, CrTr_ChangerLiensRouges, wgPageName */
( function( mw, $ ) { // Closure (fermée à la toute fin du script)
'use strict';
var
// Tableau de correspondance langue <-> code langue
// que l'on va charger à la prise de focus sur un formulaire
tab_langues,
// Id récupéré par la fonction add_heading_updater() pour
// typer correctement les résumés d'édition
heading_id_counter = 0;
if (editor.enabled) {
$('div.translations')
.each(function (i) {
add_translation_form(this, i);
add_heading_updater(this);
});
}
// Ce qu'on appelle 'table' est en fait un 'div' sur fr.wikt
// mais peu importe (ça a l'apparence d'un tableau de valeurs)
function get_translation_table_index(table) {
return $.inArray(table, $('div.translations'));
}
function get_error_html(message) {
return '<img src="//upload.wikimedia.org/wikipedia/commons/4/4e/MW-Icon-AlertMark.png"> ' + message;
}
function add_heading_updater(table) {
var id = heading_id_counter++;
var self = $(table).parent('.NavContent').prev('.NavHead');
var edit_head = $('<a>', {
href: '#',
text: '±',
'class': 'ed-edit-head',
title: 'Changer la description'
}).prependTo(self);
function remove_gloss_nodes() {
var nodes = [];
$.each(self[0].childNodes, function (i, node) {
if (node.className !== 'ed-edit-head' && node.className !== 'NavToggle') {
nodes.push(node);
}
});
$(nodes).detach();
return nodes;
}
var gloss_nodes;
edit_head.click(function (e) {
e.preventDefault();
if (self.find('form').length) {
self.find('form').remove();
self.append(gloss_nodes);
return;
}
edit_head.text('Chargement...');
editor.wikitext()
.then(function (wikitext) {
edit_head.text('±');
gloss_nodes = remove_gloss_nodes();
var prev_gloss_nodes = gloss_nodes;
var gloss = translation.get_gloss(wikitext, get_translation_table_index(table));
var gloss_to_handle = gloss.standard ? gloss.text : gloss.trans_top;
var form = $('<form>', { html:
'<label>Définition: <input name="gloss"></label>' +
'<button type="submit">Prévisualiser</button> ' +
'<span class="ed-loading">Chargement...</span>' +
'<span class="ed-errors"></span>'
});
function error(msg) {
form.find('.ed-errors')
.html(get_error_html(msg));
}
self.append(form);
form.find('input')
.val(gloss_to_handle)
.focus();
form.click(function (e) {
e.stopPropagation();
}).submit(function (e) {
e.preventDefault();
if (this.gloss.value === gloss_to_handle) {
error('La description n\'a pas changé');
return;
}
var gloss_wikitext = $.trim(this.gloss.value);
if (!translation.is_trans_top(gloss_wikitext) && translation.contains_wikitext(gloss_wikitext)) {
error('La description n\'a pas un format correct : elle contient du wikitexte ([]{}#|)' +
' (alors que {{trad-début}} est absent)');
return;
}
form.find('.ed-loading').show();
$.when(
parse_wikitext(translation.make_trans_top(gloss_wikitext)),
// get wikitext again in case it has changed since last time
editor.wikitext()
).done(function (gloss_html, wikitext) {
gloss_html = $(gloss_html);
var prev_class = self.parent('.NavFrame').attr('class');
var new_class = gloss_html.filter('.NavFrame').attr('class');
gloss_html = gloss_html.find('.NavHead').contents();
form.remove();
wikitext = translation.set_gloss(
wikitext,
get_translation_table_index(table),
gloss_wikitext
);
editor.edit({
wikitext: wikitext,
summary: 'en-tête : "' + gloss_wikitext + '"',
summary_type: 'gloss' + id,
redo: function () {
remove_gloss_nodes();
$('<span>', {
'class': 'ed-added',
html: gloss_html
}).appendTo(self) ;
if (prev_class !== new_class) {
self.parent('.NavFrame').attr('class', new_class);
}
},
undo: function () {
remove_gloss_nodes();
self.append(prev_gloss_nodes);
if (prev_class !== new_class) {
self.parent('.NavFrame').attr('class', prev_class);
}
}
});
});
});
});
});
}
function add_translation_form(table) {
var
self = $(table),
boite = self.parent().parent();
// si il n'y a aucune traduction dans la boite, on rajoute le 'ul'
// pour pouvoir y attacher le formulaire
if ( self.children().length === 0 || self.children().first().prop('nodeName').toLowerCase() !== 'ul' ) {
self.append( $( '<ul></ul>' ) );
}
var lang_meta = {
'': '', // par défaut, ni genre ni nombre à cocher
'allemand': 'm f n p',
'arabe': 'trans',
'catalan': 'm f p',
'chinois': 'trans tradi',
'coréen': 'trans tradi',
'danois': 'c n p',
'espagnol': 'm f p',
'féroïen': 'm f n',
'géorgien': 'trans',
'gaélique écossais': 'm f d p',
'gaélique irlandais': 'm f p',
'galicien': 'm f p',
'gallois': 'm f p',
'grec': 'trans',
'grec ancien': 'trans',
'hébreu': 'm f trans',
'inuktitut': 'trans',
'islandais': 'm f n p',
'italien': 'm f p',
'japonais': 'trans',
'khmer': 'trans',
'laotien': 'trans',
'latin': 'm f n p',
'letton': 'm f p',
'macédonien': 'trans',
'mirandais': 'm f p',
'néerlandais': 'm f n p',
'népalais': 'trans',
'norvégien (bokmål)': 'm f n p',
'norvégien (nynorsk)': 'm f n p',
'polonais': 'm f n p',
'portugais': 'm f p',
'roumain': 'm f n p',
'russe': 'm f n trans',
'suédois': 'c n',
'slovaque': 'm f n',
'slovène': 'm f n',
'tamoul': 'trans',
'tatare': 'trans',
'thaï': 'trans',
'tibétain': 'trans',
'ukrainien': 'trans',
'vieil irlandais': 'm f n d p',
'vieux norrois': 'm f n p'
};
var options = $.map({
gender: {
m: 'masc.',
f: 'fém.',
mf: 'masc. & fém.',
n: 'neutre',
c: 'commun',
s: 'singulier',
d: 'duel',
p: 'pluriel'
}
}, function (items, name) {
items = $.map(items, function (text, value) {
return '<label class="ed-' + value + '">' +
'<input type="radio" name="' + name + '" value="' + value + '">' +
text +
'</label>';
});
return '<p class="ed-options ed-' + name + '">' + items.join(' ') + '</p>';
}).join('') +
'<p class="ed-options"><label class="ed-trans">Translittération : <input name="trans" placeholder="ex : khimera pour химера"></label></p>' +
'<p class="ed-options"><label class="ed-tradi">Écriture traditionnelle : <input name="tradi" placeholder="ex : 軍團 pour 군단"></label></p>';
var form = $($.parseHTML('<form>' +
'<p><label><span class="txt-label">Ajouter une traduction en </span>' +
'<input class="ed-lang-name" name="lang_name" size="13" title="Nom de langue (anglais,...)" placeholder="langue à choisir"></label> : ' +
'<input class="ed-word" name="word" size="20" title="traduction" placeholder="traduction"> ' +
'<button type="submit">Prévisualiser</button> ' +
'<a href="#" class="ed-more">Plus</a></p>' +
options +
'<div class="ed-errors"></div>' +
'</form>'));
// Make radio buttons deselectable
form.find(':radio').click(function last_click(e) {
if (last_click[this.name] === this) {
last_click[this.name] = null;
this.checked = false;
} else {
last_click[this.name] = this;
}
});
var show_all_opts = false;
form.find('.ed-lang-name')
// remplacement - si nécessaire - du code langue en nom de langue
.blur(remplace_code_langue)
.blur(update_options_visibility)
// If the item exists, the value will be used as the value,
// otherwise it's 'null', which empties (the already empty)
// text field.
.val(silentFailStorage.getItem('trans-lang'));
form.find('.ed-more').click(function (e) {
e.preventDefault();
show_all_opts = !show_all_opts;
$(this).text(show_all_opts ? 'Moins' : 'Plus');
update_options_visibility();
});
update_options_visibility();
function update_options_visibility() {
var elems = form.find('.ed-options label');
if (show_all_opts) {
elems.show();
// On affiche le champ pour l'écriture traditionnelle
// uniquement en chinois et en coréen
var lang_name = form.find('.ed-lang-name').val();
if ($.inArray(lang_name, ['chinois', 'coréen']) === -1) {
form.find('.ed-tradi').hide();
}
} else {
var opts = lang_meta[form[0].lang_name.value] || lang_meta[''];
elems
.hide()
.filter('.ed-' + opts.replace(/ /g, ', .ed-')).show();
}
}
// fonction pour déterminer si une chaine
// correspond à un nom de langue dans le tableau
function is_lang_name(str) {
if (typeof tab_langues === 'undefined') {
return undefined;
}
for (var code in tab_langues) {
if (tab_langues[code] === str) {
return true;
}
}
return false;
}
// fonction de remplacement code langue -> nom de langue
// lancée à la perte de focus du champ du code langue
function remplace_code_langue() {
var elem = form.find('.ed-lang-name');
var val = elem.val();
if (val === '') {
elem.attr('title', 'Nom de langue (ex : anglais)');
return;
}
// Si le nom de langue est donné avec une majuscule initale, on l'enlève
if (val.charAt(0).toLowerCase() !== val.charAt(0)) {
val = val.charAt(0).toLowerCase() + val.substr(1);
elem.val(val);
}
if (is_lang_name(val)) {
return;
}
if (tab_langues.hasOwnProperty(val)) {
elem.val(tab_langues[val]);
return;
}
elem.attr('title', 'Nom de langue (ex : anglais)');
}
$(form)
.appendTo(boite);
boite.find('input').focus(function () {
editor.init();
// On charge le tableau de correspondance langues <-> codes langue
// si ça n'est pas déjà fait
if (typeof tab_langues === 'undefined') {
var api = new mw.Api();
return api.get({
action: 'query',
format: 'json',
titles: 'Utilisateur:Automatik/langues.json',
prop: 'revisions',
rvprop: 'content'
}).then(function (data) {
// Get the first (and only) page from data.query.pages
for (var pageid in data.query.pages) break;
tab_langues = JSON.parse(data.query.pages[pageid].revisions[0]['*']);
// On crée le tableau des noms de langues pour l'autocomplétion
var tab_lang_names = [];
for (var code in tab_langues) {
tab_lang_names.push(tab_langues[code]);
}
// Autocomplétion du nom de langue
mw.loader.using('jquery.ui.autocomplete', function() {
$('.ed-lang-name').autocomplete({
source: function( request, response ) {
var matcher = new RegExp( "^" + $.ui.autocomplete.escapeRegex( request.term ), "i" );
response( $.grep( tab_lang_names, function( item ){
return matcher.test( item );
}) );
},
minLength: 2
});
});
});
}
});
form.submit(function (e) {
e.preventDefault();
// On "trime" les valeurs afin d'enlever les éventuels espaces superflus autour de la chaine (copiée-collée depuis un texte...)
var lang_name = $.trim(this.lang_name.value);
var word = $.trim(this.word.value);
var gender = form.find('.ed-gender input:checked').prop('checked', false).val();
var trans = $.trim(this.trans.value);
var tradi = $.trim(this.tradi.value);
silentFailStorage.setItem('trans-lang', lang_name);
if (!lang_name) {
show_error(new NoInputError('lang-name'));
return;
} else if (!word) {
show_error(new NoInputError('word'));
return;
} else if (lang_name === 'français') {
show_error(new BadLangNameError());
return;
} else if (translation.re_wikitext.test(word)) {
show_error(new BadFormatError());
return;
}
var word_options = {
lang_name: lang_name,
lang_code: null,
word: word,
exists: null,
gender: gender,
trans: trans,
tradi: tradi
};
function show_error(e) {
form.find('.ed-error').removeClass('ed-error');
if (!e) {
form.find('.ed-errors').empty();
return;
}
if (e instanceof NoLangTplError) {
form.find('.ed-lang-name').addClass('ed-error').focus();
e = 'La langue « ' + e.lang_name + ' » n\'est pas définie.';
} else if (e instanceof NoInputError) {
form.find('.ed-' + e.input).addClass('ed-error').focus();
if (e.input === 'lang-name') {
e = 'Entrez le nom de langue (anglais, suédois,...)';
} else if (e.input === 'word') {
e = 'Entrez la traduction';
}
} else if (e instanceof BadLangNameError) {
form.find('.ed-lang-name').addClass('ed-error').focus();
e = 'Il n\'est pas possible d\'ajouter une traduction en français. ' +
'À la place, veuillez utiliser la section « Synonymes » ou ' +
'« Variantes dialectales ».';
} else if (e instanceof BadFormatError) {
form.find('.ed-word').addClass('ed-error').focus();
e = 'La traduction n\'est pas dans un format correct : ' +
'elle contient du wikitexte ([]{}|=)';
} else if (e instanceof HttpError) {
e = 'Vous ne pouvez pas charger la traduction. Êtes-vous en ligne ?';
}
form.find('.ed-errors').html(get_error_html(e));
}
var lang_code;
// on convertit le nom de langue en code
if (typeof tab_langues !== 'undefined') {
// Le tableau des langues contient-il le nom de langues
// donné ?
for (var code in tab_langues) {
if (tab_langues[code] === lang_name) {
lang_code = code;
break;
}
}
// Si le nom de langue donné n'est pas répertorié
// on regarde si ce n'est pas un code langue
if (lang_code === undefined) {
if (tab_langues[lang_name]) {
lang_code = lang_name;
lang_name = tab_langues[lang_code];
word_options.lang_name = lang_name;
} else {
show_error(new NoLangTplError(lang_name));
return;
}
}
} else {
throw new Error('Le tableau des langues n\'est pas défini.');
}
word_options.lang_code = lang_code;
$.when(
// word_html
page_exists(lang_code, word)
.then(function (page_exists) {
word_options.exists = page_exists;
return parse_wikitext(translation.get_formatted_word(word_options))
.then(function (html) {
return html;
});
}),
// wikitext
editor.wikitext()
).fail(function (error) {
if (error === 'http') {
// jQuery HTTP error
show_error(new HttpError());
} else {
show_error(error);
}
}).done(function (word_html, wikitext) {
show_error(false);
silentFailStorage.setItem('trans-lang', lang_name);
form[0].word.value = '';
form[0].trans.value = '';
form[0].tradi.value = '';
var added_elem;
var index = get_translation_table_index(table);
wikitext = translation.add(wikitext, index, word_options);
editor.edit({
summary: '+' + lang_name + ' : [[' + word + ']]',
summary_type: 'trad',
wikitext: wikitext,
redo: function () {
var translations = self.find('ul > li');
translation.add_to_list({
items: translations,
add_only_item: function () {
added_elem = $('<li>', { html: '<span class="trad-' + lang_code +
'">' + lang_name.charAt(0).toUpperCase() + lang_name.substr(1) + '</span> : ' + word_html });
self.find('ul').prepend(added_elem);
},
equal_or_before: function (item) {
var match = /^\s*(.{3,})/.exec($(item).children().first().text());
if (match) {
// conventions internationales en premier
if (match[1] === 'Conventions internationales') return 'before';
// on ignore les {{ébauche-trad}}
if (match[1] === 'Traductions manquantes.') return false;
if (sortkey(match[1].toLowerCase()) === sortkey(lang_name.toLowerCase())) {
return 'equal';
} else if (sortkey(match[1].toLowerCase()) < sortkey(lang_name.toLowerCase())) {
return 'before';
}
}
return false;
},
add_to_item: function (item) {
added_elem = $('<span>', { html: ', ' + word_html})
.appendTo(item);
},
add_after: function (item) {
added_elem = $('<li>', { html: '<span class="trad-' +
lang_code + '">' + lang_name.charAt(0).toUpperCase() +
lang_name.substr(1) + '</span> : ' + word_html })
.insertAfter(item);
},
add_before: function (item) {
added_elem = $('<li>', { html: '<span class="trad-' +
lang_code + '">' + lang_name.charAt(0).toUpperCase() +
lang_name.substr(1) + '</span> : ' + word_html });
added_elem.insertBefore(item);
},
});
added_elem.addClass('ed-added');
// Si le gadget de création de traductions est activé,
// on colore directement en bleu les traductions à créer
// pour éviter d'avoir à recharger la page
if (window.CrTr_ChangerLiensRouges) window.CrTr_ChangerLiensRouges();
},
undo: function () {
added_elem.remove();
}
});
});
});
}
function parse_wikitext(wikitext) {
return new mw.Api().get({
action: 'parse',
text: '<div>' + wikitext + '</div>',
title: wgPageName
}).then(function (data) {
var html = data.parse.text['*'];
// Get only the parts between <div> and </div>
html = html.substring(
html.indexOf('<div>') + '<div>'.length,
html.lastIndexOf('</div>')
);
return $.trim(html);
});
}
function page_exists(lang_code, page) {
var wm_liens = {
'cmn': 'zh',
'fra-nor': 'nrm',
'gsw': 'als',
'ko-Hani': 'ko',
'lzh': 'zh-classical',
'nan': 'zh-min-nan',
'nb': 'no',
'rup': 'roa-rup',
'yue': 'zh-yue'
};
if (wm_liens.hasOwnProperty(lang_code))
lang_code = wm_liens[lang_code];
var def = $.Deferred();
$.ajax({
url: '//' + lang_code + '.wiktionary.org/w/api.php?origin=' + location.protocol + '//' + location.host,
data: {
action: 'query',
titles: page,
format: 'json'
},
dataType: 'json'
}).fail(function () {
def.resolve('nowikt');
}).then(function (data) {
def.resolve(!data.query.pages[-1]);
});
return def.promise();
}
// Ajout pour le Wiktionnaire francophone,
// adapté de [[MediaWiki:Gadget-CommonWikt.js]], fonction CleDeTri
function sortkey(word) {
var key = word.toLowerCase();
key = key.replace( /[àáâãäå]/g, "a" );
key = key.replace( /[æ]/g, "ae" );
key = key.replace( /[çćċč]/g, "c" );
key = key.replace( /[ĉ]/g, "cx" );
key = key.replace( /[èéêë]/g, "e" );
key = key.replace( /[ĝ]/g, "gx" );
key = key.replace( /[ĥ]/g, "hx" );
key = key.replace( /[ìíîï]/g, "i" );
key = key.replace( /[ĵ]/g, "jx" );
key = key.replace( /[ñ]/g, "n" );
key = key.replace( /[òóôõö]/g, "o" );
key = key.replace( /[œ]/g, "oe" );
key = key.replace( /[òóôõö]/g, "o" );
key = key.replace( /[ŝ]/g, "sx" );
key = key.replace( /[ùúûü]/g, "u" );
key = key.replace( /[ŭ]/g, "ux" );
key = key.replace( /[ýÿ]/g, "y" );
key = key.replace( /['’)(]/g, "" );
key = key.replace( /[-\/]/g, " " );
return key;
}
var translation = {
re_wikitext: /[[\]{}#|=]/,
contains_wikitext: function (str) {
return translation.re_wikitext.test(str);
},
re_gloss: /\{\{trad-début(.*)\}\}/g,
re_section: /(\{\{trad-début.*\}\})([\s\S]*?)(\{\{trad-fin\}\})/g,
is_trans_top: function (gloss) {
return gloss.replace(translation.re_gloss, '-') === '-';
},
make_trans_top: function (gloss) {
if (translation.is_trans_top(gloss)) {
return gloss;
} else {
return '{{trad-début|' + gloss + '}}';
}
},
get_gloss: function (wikitext, index) {
// s'il y a un commentaire, l'analyse ne va pas être possible
if (/\{\{trad-début\|.*?(?:<!--|-->)/.test(wikitext)) {
mw.notify('Le wikitexte contient un commentaire "<!--". ' +
'La page doit être modifiée manuellement.');
throw new Error('Le wikitexte contient un commentaire "<!--". ' +
'La page doit être modifiée manuellement.');
}
translation.re_gloss.lastIndex = 0;
for (var i = 0; i <= index; i++) {
var match = translation.re_gloss.exec(wikitext);
if (i === index && match) {
var standard = /^(|\|[^|=]*)$/.test(match[1]);
return {
trans_top: match[0],
text: standard ? match[1].substr(1) : void 0,
standard: standard
};
}
}
throw new Error('Le ' + (index+1) + '-ème {{trad-début}} n\'a pas été trouvé dans le wikitexte.');
},
set_gloss: function (wikitext, index, gloss) {
index++;
var count = 0;
return wikitext.replace(translation.re_gloss, function (match, p1, p2) {
count++;
if (count !== index) {
return match;
}
return translation.make_trans_top(gloss);
});
},
get_formatted_word: function (opts) {
var tpl = [
opts.exists === 'nowikt' ? 'trad--' : (opts.exists === true ? 'trad+' : 'trad-'),
opts.lang_code,
opts.word
];
opts.gender && tpl.push(opts.gender);
opts.trans && tpl.push('tr=' + opts.trans);
opts.tradi && tpl.push('tradi=' + opts.tradi);
return '{{' + tpl.join('|') + '}}';
},
// Options:
// - items: Array of items
// - equal_or_before: Function that returns either 'equal', 'before' or false
// - add_to_item: Adds a word to an item
// - add_after: Adds the item after an item
// - add_before: Adds the item before an item
add_to_list: function (opts) {
var items = opts.items;
if (!items.length) {
items[0] = opts.add_only_item();
return items;
}
for (var i = items.length - 1; i >= 0; i--) {
var eq_or_bef = opts.equal_or_before(items[i]);
if (eq_or_bef === 'equal') {
items[i] = opts.add_to_item(items[i]);
return items;
} else if (eq_or_bef === 'before') {
items[i] = opts.add_after(items[i]);
return items;
}
}
items[0] = opts.add_before(items[0]);
return items;
},
add: function (wikitext, index, opts) {
if (wikitext.match(translation.re_section)[index].indexOf('<!--') !== -1) {
mw.notify('Le wikitexte contient un commentaire "<!--". La page doit être modifiée manuellement.');
throw new Error('Le wikitexte contient un commentaire "<!--". La page doit être modifiée manuellement.');
}
index++;
var count = 0;
return wikitext.replace(translation.re_section, function (match, p1, p2, p3) {
count++;
if (count !== index) {
return match;
}
p2 = $.trim(p2);
var formatted_word = translation.get_formatted_word(opts);
var lines = translation.add_to_list({
// split into lines
items: p2 ? p2.split('\n') : [],
add_only_item: function () {
return '* {{T|' + opts.lang_code + '}} : ' + formatted_word;
},
equal_or_before: function (line) {
// Le regex suivant contient [(:] car on peut avoir un dialecte
// précisé entre parenthèses après le nom de langue
var match = /^\*\s*\{\{T\|([^}|]+?)\}\}\s*[(:]/.exec(line);
if (match) {
var code_langue = match[1];
if (code_langue === 'conv') {
return 'before'; // conventions internationales en premier
}
if (sortkey(tab_langues[code_langue]) === sortkey(opts.lang_name)) {
if (/^\*\s*\{\{T\|[^}|]+?\}\}\s*:\s*$/.exec(line)) {
mw.notify('Il n\'est pas possible d\'ajouter une traduction dans cette langue,' +
' car le format est inhabituel.');
throw new Error('Format incorrect : le wikicode des traductions dans cette langue ' +
'est inhabituel et la traduction ne peut donc pas être ajoutée par le gadget.');
}
return 'equal';
} else if (sortkey(tab_langues[code_langue]) < sortkey(opts.lang_name)) {
return 'before';
}
}
return false;
},
add_to_item: function (line) {
return line + ', ' + formatted_word;
},
add_before: function (line) {
return this.add_only_item() + '\n' + line;
},
add_after: function (line) {
return line + '\n' + this.add_only_item();
}
});
return p1 + '\n' + lines.join('\n') + '\n' + p3;
});
}
};
function extend_error(name, p1_name) {
function E(p1) {
this.message = p1;
if (p1_name) this[p1_name] = p1;
}
E.prototype = new Error();
E.prototype.constructor = E;
E.prototype.name = name;
return E;
}
var NoLangTplError = extend_error('NoLangTplError', 'lang_name');
var NoInputError = extend_error('NoInputError', 'input');
var BadLangNameError = extend_error('BadLangNameError');
var BadFormatError = extend_error('BadFormatError');
var HttpError = extend_error('HttpError');
// Export some useful components
window.translation = translation;
window.parse_wikitext = parse_wikitext;
window.add_heading_updater = add_heading_updater;
window.add_translation_form = add_translation_form;
} ( mediaWiki, jQuery ) ); // Fin de la closure ouverte au tout début