User:Nonexyst/CommonErrors.js

/* * 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, «. » 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 = ' '; text = text.substring(0, charNo) + anch + text.substring(charNo);

var copyDiv = $(' ') .append(text.replace(/\n/g, ' '))                   .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.common-errors').each(function (index, el) {           //$(this).removeAttr('id');            html += "" + $(this).text + "";            $(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 = $(" "); 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 = '  '+ 'Возможные ошибки' + ' : ' + select_multiple_text.build_popup + ' ';

elt.html(str);

$("#mw-content-text").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($(" " + match[2] + " "));

select_multiple_text.change_text_node(el, search, cur + 1); select_multiple_text.change_text_node(tail, search, cur); }       }    },

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, "# #"); text = text.replace(/\n/g, " "); text = text.replace(/# #/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("$$.*$$|<[a-zA-z0-9 =\"']>|[|style=\".*\"| |&mdash;||\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 = $('#mw-content-text'); 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($(" " + match[2] + " ")); }       }    },

// 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', 'edit' ]) == -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") == 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('common-errors')) 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 *")) .not($(".diff *")) .not($(".printfooter *")) .not($(".previewnote *")) .not($("#modernisations *")) .not($("#authorityControl *")) .not($("#dynamic_links *")) .not($("#contentSub *")) .not($("#ws-data *")) .not($("pre")) .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);

if (mw.config.get('wgAction') == 'edit') { if ($('#wpTextbox1').length) { this.match_text($('#wpTextbox1')[0].value, search); }       } else { 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.common-errors').length;

if (error_count) { if (!select_multiple_text.portlet_link_added) { mw.util.addPortletLink ("p-tb", "javascript:select_multiple_text.open_popup;",                               "", "option-commons-errors", ""); }

$('#option-commons-errors a').text('Возможные ошибки' + " (" + error_count + ")");

select_multiple_text.portlet_link_added = true; }   },

default_select : function { /* il faut une var qui définissent une espace, sécable, non sécable * etc. et qui fonctionne avec les entités html comme */       var char_lat_min = 'a-zéèàùâêîôûŷäëïöüÿçœæſñ'; var char_lat_maj = 'A-ZÉÈÀÂÙÊÎÔÛŶÄËÏÖÏŸÇŒÆÑ'; var char_lat = char_lat_min + char_lat_maj; var char_cyr_min = 'а-яёѳѣіѵ'; var char_cyr_maj = 'А-ЯЁѲѢІѴ'; var char_cyr = char_cyr_min + char_cyr_maj; var char_min = char_lat_min + char_cyr_min; var char_maj = char_lat_maj + char_cyr_maj; var char = char_min + char_maj;

var bw = '([^' + char + ']|^)'; var ew = '([^' + char + ']|$)'; var ch = '[' + char + ']';

select_multiple_text.exec(/.*/,           [            "[^.][.][.][.](?![.])",            "'", "‘", "\"",            /* devrait inclure \\. mais match etc., et toutes les abbrevs, trop de             * faux positifs */            "[!:,;?][,;]+",            //"[" + char_min + "][!?:;]",            "[!?;:.,][" + char + "]",            "[ ][\\.,!?;:»]",            //"[\\.?!] 11[^0-9]",            "[0-9]" + ch,            ch + "[0-9]",            //"1e[^r]", "[!1][a]",            "[ \n-]i[[t][ ,.]",            /* . и слово, начинающееся с маленькой буквы */            "[" + char + "]{2,4}[.] [" + char_min + "]",            "(^A | A )",            "([^vmhp]\\. |[?!:] |^)(II|ll)(?!([Ie.]|$))",            "[^  \xa0\\d\n]—",            "—[^  \xa0\u2007\\d\n]",            "\\d—[^\\d]",            "[^\\d]—\\d",            "[ ]-",            "-[ ]",            "[ ]’",            "’[ ]",            "[" + char_lat + "]+[" + char_cyr + "]+",            "[" + char_cyr + "]+[" + char_lat + "]+"            ]);    } };

$(document).ready(select_multiple_text.default_select);