Usor:ElioPrrl/Scanilles.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/*
* Permet de montrer dans un texte les erreurs les plus communes.
*
* Auteur: Phe
*
* TODO:
*
* Permettre des auto-corrections.
*
* Le support en mode édition est très expérimental, il manque un bouton refresh à
* la liste des erreurs pour éviter d'avoir à faire un preview quand on a corrigé.
* Certaines erreurs ne seront visible que dans le html, « . <minuscule> » par
* exemple s'il y a y un \n dans le wikicode entre le . et la minuscule.
* Le wikicode n'est pas strippé ce qui doit produire des tas de faux positifs.
*
* En mode édition cliquer sur un lien se positionne toujours sur la première
* erreur.
*
* En mode édition il arrive fréquemment qu'une erreur détecté ne puisse
* être localisé dans la boîte d'édition.
*
* Le contenu d'une textarea est un nœud texte qui est splitter en plusieurs
* nœud html, le browser à l'air assez futé pour comprendre qu'un textarea ne
* peut contenir qu'un nœud texte et ignore les trucs insérer mais ça ne
* va pas du tout (incompatibilité avec le gadget auteur form par exemple -->
* ns:104 ignoré pour l'instant).
*
*/
/*
* http://stackoverflow.com/questions/5458655/jquery-scroll-textarea-to-given-position
*
* Construit un div avec la même font que la textarea pouvoir calculer la position de scroll.
*/
jQuery.fn.scrollToText = function(search) {
var text = $(this).text();
var charNo = text.indexOf(search);
var anch = '<span id="anch"></span>';
text = text.substring(0, charNo) + anch + text.substring(charNo);
var copyDiv = $('<div></div>')
.append(text.replace(/\n/g, '<br />'))
.css('width', $(this).innerWidth()) // width without scrollbar
.css('font-size', $(this).css('font-size'))
.css('font-family', $(this).css('font-family'))
.css('padding', $(this).css('padding'));
copyDiv.insertAfter($(this));
var pos = copyDiv.find('SPAN#anch').offset().top - copyDiv.find('SPAN#anch').closest('DIV').offset().top;
pos = pos - Math.round($(this).innerHeight() / 2);
$(this).scrollTop(pos);
copyDiv.remove();
};
select_multiple_text = {
precompile_regexp : function (search) {
var temp = [];
for (var i in search)
temp.push(new RegExp(search[i], "g"));
return temp;
},
build_popup : function() {
var html = "";
$('span.Erreurs-communes').each(function (index, el) {
//$(this).removeAttr('id');
html += "<li><a href='javascript:select_multiple_text.scroll_to_pos(" + index + ");'>" + $(this).text() + "</a></li>";
$(this).attr('id', 'Err-' + index);
});
return html;
},
last_scroll_pos : 0,
scroll_to_pos : function(index) {
var found = false;
if ($.inArray(mw.config.get('wgAction'), [ 'submit', 'edit' ]) != -1) {
var textbox = document.getElementById("wpTextbox1");
if (textbox) {
var text = $("#Err-" + index).text();
var pos = textbox.value.indexOf(text);
if (pos != -1) {
textbox.selectionStart = pos;
textbox.selectionEnd = pos + text.length;
textbox.focus();
$('#wpTextbox1').scrollToText(text);
found = true;
}
}
}
if (!found) {
$("#Err-" + select_multiple_text.last_scroll_pos).css("background-color", "");
window.location.hash = "#Err-" + index;
$("#Err-" + index).css("background-color", "#00FF00");
select_multiple_text.last_scroll_pos = index;
// index * 2 + 1 is voodoo making assumption on the html struct of the
// popup, see build_popup(), there is surely a simpler and safer way.
$('#err-communes-popup').find('*:eq(' + (index * 2 + 1) + ')').focus();
}
},
close_popup : function() {
$("#err_summary").remove();
return;
},
open_popup : function() {
if ($("#err_summary").remove().length)
return;
var elt = $("<div id='err_summary'></div>");
elt.css({
"position" : "fixed",
"min-width" : "16em",
"max-width" : "35em",
"max-height" : "35em",
"right" : "0.5em",
"background-color" : "#FFFFFF",
"z-index" : 1,
"border" : "1px solid",
"padding" : "8px"
});
var str = '<div style="float:right;"><a href="javascript:select_multiple_text.close_popup();"><img src="//upload.wikimedia.org/wikipedia/commons/9/97/WikEd_close.png"/></a></div><h4>'+ 'Erreur(s) possible' + ' :</h4><div style="overflow:auto;min-width:16em;max-width:35em;max-height:32em;"><ul id="err-communes-popup">' + select_multiple_text.build_popup() + '</ul></div>';
elt.html(str);
$("#bodyContent").prepend(elt);
},
match : function (text, regexp) {
var m = text.match(regexp);
if (m) {
var start_match = text.search(regexp);
var end_match = start_match + m[0].length;
return [ start_match, end_match, m[0] ];
}
return null;
},
change_text_node : function(el, search, first) {
for (var cur = first; cur < search.length; ++cur) {
var match = this.match(el.nodeValue, search[cur]);
if (match) {
var tail = $(el).clone()[0];
el.nodeValue = el.nodeValue.substring(0, match[0]);
tail.nodeValue = tail.nodeValue.substring(match[1]);
// FIXME: how to setup the text part of the span w/o injecting it in html ?
$(el).after(tail).after($("<span class='Erreurs-communes'>" + match[2] + "</span>"));
select_multiple_text.change_text_node(el, search, cur + 1);
select_multiple_text.change_text_node(tail, search, cur);
}
}
},
/* CODE EXPÉRIMENTAL POUR SURLIGNER LES SCANILLES DÈS LE MODE ÉDITION
match_text : function (text, search) {
// Il faut faire ressembler le wikicode à du html, il manque plein de
// truc encore...
text = text.replace(/'''/g, "");
text = text.replace(/''/g, "");
// Kludge
text = text.replace(/\n\n/g, "#<foo>#");
text = text.replace(/\n/g, " ");
text = text.replace(/#<foo>#/g, "\n\n");
// Indispensable pour éviter un timeout du script sur une grosse page contenant
// beaucoup de wikicode.
var new_text = '';
var last_match = 0;
var splitter = new RegExp("<math>.*</math>|<[a-zA-z0-9 =\"']>|[</[a-zA-z0-9 =\"']+>|style=\".*\"| |—|<!--.*-->|\n:[:]*|\n;[;]*|[[][[].*]]", "gm");
while ((result = splitter.exec(text)) != null) {
new_text += text.slice(last_match, splitter.lastIndex - result[0].length);
for (var i = 0; i < result[0].length; ++i)
new_text += ' ';
last_match = splitter.lastIndex;
}
new_text += text.slice(last_match);
text = new_text;
// Les nœud invisible servent à stocker à stocker les erreurs trouvés de
// façon à réutiliser le même code que lorsque wgAction == submit.
var $node = $('#bodyContent');
for (var i = 0; i < search.length; ++i) {
var pos = 0;
while ((match = this.match(text.slice(pos), search[i])) != null) {
pos += match[1];
$node.append($("<span class='Erreurs-communes' style='display:none;'>" + match[2] + "</span>"));
}
}
}, */
// Predicate to filter page we act on based upon a user filter and some other criteria.
filter_page : function(user_filter) {
if ($.inArray(mw.config.get("wgAction"), [ 'view', 'submit' ]) == -1)
return false;
if (!user_filter.test(mw.config.get("wgPageName")))
return false;
if (/\.(css|js)$/.test(mw.config.get("wgPageName")))
return false;
if (/^Wikisource:/.test(mw.config.get("wgPageName")))
return false;
if ($("#wikiDiff").length)
return false;
/* FIXME: filtre les cats à cause de Page:pagename et Livre:livre.djvu
* mais c'est trop drastique comme solution */
if (mw.config.get("wgNamespaceNumber") == 14)
return false;
if (mw.config.get("wgNamespaceNumber") == 6)
return false;
if (mw.config.get("wgNamespaceNumber") == 112)
return false;
if (mw.config.get("wgNamespaceNumber") == 828)
return false;
if (mw.config.get("wgNamespaceNumber") == 2600) // Topic ou Sujet
return false;
if (mw.config.get("wgNamespaceNumber") == 2 && mw.config.get("wgPageName").indexOf('/') == -1)
return false;
if (mw.config.get("wgNamespaceNumber") % 2 != 0)
return false;
/* Incompatibilité avec le gadget auteur form, voir plus haut. */
if (mw.config.get("wgNamespaceNumber") == 102 && mw.config.get("wgAction") == 'edit')
return false;
return true;
},
filter_node : function() {
if ($(this).parent().hasClass('Erreurs-communes'))
return false;
return this.nodeType == 3;
},
get_text_nodes : function () {
var content_id = "#mw-content-text";
if ($("#wikiPreview").length)
content_id = "#wikiPreview";
// TODO: Bencher sur //fr.wikisource.org/wiki/L’Encyclopédie/Volume_1
// ou tout autre page qui contient un grand nombre de nœud.
return $(content_id)
.find("*")
.not($(".no_erreurs_communes, .no_erreurs_communes *"))
.not($(".diff *"))
.not($(".printfooter *"))
.not($(".previewnote *"))
.not($("#modernisations *"))
.not($("#authorityControl *"))
.not($("#dynamic_links *"))
.not($("#contentSub *"))
.not($("#ws-data *"))
.not($("pre, pre *"))
.not($("code, code *"))
.not($("script"))
.not($("style")) // TemplateStyles
.not($(".mwe-math-element *"))
.not($(".fn, .fn *"))
.not($(".reference *"))
.not($(".abbr, .abbr *"))
.not($(":not(:lang(la))"))
.contents()
.filter(select_multiple_text.filter_node);
},
portlet_link_added : false,
exec : function(user_filter, search) {
if (!select_multiple_text.filter_page(user_filter))
return;
search = select_multiple_text.precompile_regexp(search);
var text_nodes = select_multiple_text.get_text_nodes();
$.each(text_nodes, function(i, el) {
select_multiple_text.change_text_node(el, search, 0);
});
var error_count = $('span.Erreurs-communes').length;
if (error_count) {
if (!select_multiple_text.portlet_link_added) {
mw.loader.using('mediawiki.util', function() {
mw.util.addPortletLink ("p-tb", "javascript:select_multiple_text.open_popup();",
"", "option-commons-errors", "");
});
}
$('#option-commons-errors a').text('Erreur(s) possible' + " (" + error_count + ")");
select_multiple_text.portlet_link_added = true;
}
},
default_select : function() {
// Ces variables servent simplement à abréger et à rendre plus parlant le code ci-dessous //
/* MINUSCULE, MAJUSCULE, LETTRE
(à utiliser entre crochets) */
var char_min = 'a-zœæſ';
var char_maj = 'A-Zή';
var char = char_min + char_maj;
/* PONCTUATION française
(à utiliser entre parenthèses) */
var ponct_forte = '[\\.]\\s+|\\s+[\\!\\?\\«\\»]\\s+' ;
var ponct_faible = '$|[,\\)\\]]\\s+|\\s+[\\(\\[]|\\s+[\\;\\:—]\\s+' ;
var ponct = "^|" + ponct_forte + '|' + ponct_faible ;
/* TEST DE DÉBUT DE MOT (à mettre en début de regex)
(respectivement qui commence par une consonne, par une voyelle, par une lettre quelconque) */
var deb_mot_cons = '(?<=\\s|^|\\(|\\[|\\>)';
var deb_mot_voy = '(?<=\\s|^|\\(|\\[|\\>|’)';
var deb_mot = '(?<=\\s|^|\\(|\\[|\\>|’|\\-)';
/* TEST DE FIN DE MOT
(à mettre en fin de regex) */
var fin_mot = '(?=\\s|,|\\.|\\)|\\]|\\<|\\||\\-)';
/* LISTES DES EXPRESSIONS DOUTEUSES À SURLIGNER :
dans les commentaires, « *patati < patata » doit se lire : « surlignez Patati, qui est probablement une erreur pour Patata » */
select_multiple_text.exec(/.*/,
[
/* PONCTUATION */
"('|‘|’{2,})", // problèmes d'apostrophes
"\\.{2,}", // suites de points
"[!:,;?][,;]+", // suites de signes de ponctuation interdites
"\\t", // tabulations
"\\.+…+|…+\\.+", // mélanges de points et points de suspension
"(\\{\\{|\\}\\})", // modèles mal fermés
'\\"', // guillemets droits
'«\\s*«|»\\s*»', // doubles guillemets
"[\\^~–\\@„■•]", // symboles typographiques impossibles en français
"\\s+[\\.\\,\\)\\]]", // mauvais espacement avant la ponctuation basse
"[\\(\\[]\\s+", // mauvais espacement après la ponctuation basse
"[" + char + "][\\!\\?\\:\\;]", // mauvais espacement avant la ponctuation haute
"[\\!\\?\\;\\:\\.,][" + char + "]", // mauvais espacement après la ponctuation haute
"(\\s’|’ )", // mauvais espacement autour des apostrophes
"([\\s\\,\\!\\?]\\-|(?<!(\\s|^)[A-Z])\\.\\-|\\-\\s+[^$])", // mauvais espacement autour des traits-d'union
"(?<!\\s|^)—", // mauvais espacement avant les tirets cadratins
"(—(?!\\s|\\,|$)|[^\\u00A0]—,)", // mauvais espacement après les tirets cadratins
"(?<!\\s|^)\\&", // mauvais espacement avant les esperluettes
"\\&(?!c\\.|\\s|\\,|$)", // mauvais espacement après les esperluettes
// SUCCESSIONS DE LETTRES SUSPECTES
"([" + char_min + "-][" + char_min + "][^0-9A-Zpvcgm°.][.] [" + char_min + "]" + "|" + "(ma|ta|sa|ce|se|ni|va|pu|et|le|la|de|du|un|te|je|tu|il|me|ne|ou|où|ok|à|y|a)[.] [" + char_min + "])", // point suivi d'une minuscule (si précédé d'un mot de plus de trois lettres)
"([A-ZÀ-Üa-zà-ÿ][Α-Ωα-ω]|[Α-Ωα-ω][A-ZÀ-Üa-zà-ÿ]|[A-ZÀ-Üa-zà-ÿ][0-9]|[0-9][A-ZÀ-Üa-zà-ÿ]|[Ѐ-ӿ])", // mélanges d'alphabets
"[£$€ϐϵϑϰϕϖϱ]", // symboles mathématiques et monétaires
"[" + char_min + "][" + char_maj + "]+", // majuscules dans mot en minuscules
"(’[0-9]|[0-9]’)", // apostrophes au lieu de prime (ou chiffre au lieu de lettre)
"[fffiflffifflſtst]", // ligatures
"[ȦȧÀàĖėÈèİıÌìíȮȯÒòç]", // diacritiques
"(?!nec)(?<=^|\\s)(['’" + char + "]+)\\s+\\1(\\s|\\.|\\…|,)", // doublons
"([abdefghjklmnopqrstuvwyzà-ÿçœæſñABDEFGHJKLMNOPQRSTUVWYZÀ-ÜÇŒÆÑ])(\\1\\1+)", // lettres triples
"cbe", // *-cbe- < -che-
"cn[lt]", // *-cnl- < -enl-, *-cnt- < -ent-
"nl" + fin_mot, // *-nl < -nt en fin de mot
"œ" + fin_mot, // *-œ < -æ en fin de mot
"[Qq]n", // *-qn- < -qu-
"(?<=" + ponct_forte + ")" + "11" + fin_mot, // *11 < Ii
]);
}
};
if ( !mw.user.isAnon() ) {
$(document).ready(select_multiple_text.default_select);
}