Subversion Repositories Applications.dictionnaire

Rev

Rev 7 | Rev 11 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
10 raphael 1
/*
2
  Surligneur de mot (glossaire)
3
 
4
  0) tout d'abord la liste des mots est récupée (getMotsADefinitions) et stockée dans motsAyantDefinition
5
 
6
  1) chaque mot de la liste subie une transformation progressive en pattern:
7
  * les mots de moins de 3 caractères sont inchangés (exemple: "nu")
8
  * les mots finissant en "eux/euse" sont généricisés
9
  * les autres mots se voit ajoutée la possibilité de matcher sur \\w{0,2}
10
 
11
  2) puis plusieurs RegExp sont baties à partir de la liste de pattern préliminaires
12
  * regexp_complete permet d'assurer que le mot:
13
  ** commence en début de ligne
14
  ** suit un espace
15
  ** ne suit pas un caractère
16
  [ mais ne s'assure pas que le mot ne suit PAS un caractère accentué, cf notes à propos de \\b et l'UTF-8 ]
17
 
18
  * regexp_thin permet de s'assurer que le mot:
19
  ** ne se trouve pas déjà encapsulé dans un <span rel="" class="definition_term">X</span>
20
  ** ne se trouve pas comme attribut "rel" d'un <span rel="X" class="definition_term"></span>
21
  ** n'est pas directement précédé d'un accent (parmis ceux utilisés dans la BDD, cf exclureSpan)
22
  ** n'est pas directement précédé d'un caractère alphabétique
23
  Et tente d'extraire une partie de contexte significative (jusqu'à 24 caractère car initialement le
24
  test d'inclusion dans .definition_term était faire plus tardivement.
25
 
26
  3) remplacerDefinitions() prend la liste des nodes susceptibles de nécessiter un traitement
27
  et ignore ceux ayant déjà été traités [max_passages] fois (4 par défaut)
28
 
29
  4) pour chaque node:
30
  * le compteur de passage est incrémenté
31
  * nesseciteSubstitution() est appellé
32
 
33
  == nesseciteSubstitution() ==:
34
  ** Utilise regexp_thin pour extraire les mots restant à définir ainsi qu'une partie de leur contexte.
35
  ** Utilise regexp_complete pour extraire les mots au plus proche de leur bodures avec un moindre risque d'erreur
36
  ** exlue les faux positifs possibles de regexp_thin
37
  ** renvoi les matches de regexp_thin (qui présente plus de contexte)
38
  =============================
39
 
40
  * pour chaque correspondance retournée par nesseciteSubstitution(),
41
  le mot d'origine (tel que dans motsAyantDefinition)
42
  est déterminé en utilisant le levenstein (un cache est mis en place, cf texte_to_mot() dans helpers.js).
43
 
44
  * rechercherEtRemplacerMotParDefinition() est appelée pour effectuer la substitution dans le node
45
 
46
 
47
  Contraintes rencontrées:
48
  1) exclure les mots déjà défini via la regexp général plutôt qu'au coup par coup.
49
  2) vérifier les bordures
50
  3) vérifier bordure d'accents:
51
  \\b(é) dans une regexp ne peut matcher une chaîne JS UTF-8
52
  http://stackoverflow.com/questions/2881445/utf-8-word-boundary-regex-in-javascript
53
*/
54
 
1 aurelien 55
var dictionnaire = new Array();
3 aurelien 56
var motsAyantDefinition = null;
1 aurelien 57
var mouseX = null;
58
var mouseY = null;
3 aurelien 59
var active = false;
10 raphael 60
var set = 'p, span:not(.definition_term), td, pre, div, li';
1 aurelien 61
 
10 raphael 62
// Note: utiliser \\b plutôt que \\W pour matcher les bordures de mots
63
// en incluant début et fin de ligne
64
var regexp_complete = null;
1 aurelien 65
 
10 raphael 66
var regexp_context = null;
67
var regexp_thin = null;
68
 
69
// nombre maximum de passages de la détection sur un node donné
70
var max_passages = 4;
71
 
72
// echo $(mysql<<<"SELECT cle FROM definitions"|tr "[a-z,-]" o|sed -e 's/o//g' -e "s/\(.\)/\1\n/g"|sort -u)
73
// ç é è ê î ï ô (2013/07/03)
74
// TODO: http://xregexp.com/plugins/
75
var	exclureSpan = '([^' +
76
	'(?:class="definition_term">)' + // mot contextualisé déjà dictionnarisé
77
	'(?:rel=")' + // la valeur du rel="" d'un mot déjà dictionnarisé
78
	'çéèêîïô' + // les accents, non-traités dans la regexp générale et qui ne doivent pas
79
	// être pris pour des bordures de mots
80
	'\\w' +
81
	']|^){1,24}';
82
 
83
 
1 aurelien 84
function supprimerAccents(str) {
85
    var rExps=[
10 raphael 86
		{re:/[\xC0-\xC6]/g, ch:'A'},
87
		{re:/[\xE0-\xE6]/g, ch:'a'},
88
		{re:/[\xC8-\xCB]/g, ch:'E'},
89
		{re:/[\xE8-\xEB]/g, ch:'e'},
90
		{re:/[\xCC-\xCF]/g, ch:'I'},
91
		{re:/[\xEC-\xEF]/g, ch:'i'},
92
		{re:/[\xD2-\xD6]/g, ch:'O'},
93
		{re:/[\xF2-\xF6]/g, ch:'o'},
94
		{re:/[\xD9-\xDC]/g, ch:'U'},
95
		{re:/[\xF9-\xFC]/g, ch:'u'},
96
		{re:/[\xD1]/g, ch:'N'},
97
		{re:/[\xF1]/g, ch:'n'} ];
1 aurelien 98
    for(var i=0, len=rExps.length; i<len; i++)
10 raphael 99
        str=str.replace(rExps[i].re, rExps[i].ch);
1 aurelien 100
    return str;
101
};
102
 
103
// c'est moche mais le mauvais support de l'unicode dans
104
// javascript ne me permet pas de faire mieux
105
function etendreChaineAccents(str) {
106
    return str.replace('a',"(a|à|á|â|ã|ä|å)")
10 raphael 107
		.replace('e',"(e|è|é|ê|ë)")
108
		.replace('i',"(i|ì|í|î|ï)")
109
		.replace('o',"(o|ò|ó|ô|õ|ö)")
110
		.replace('u',"(u|ù|ú|û|ü)")
111
		.replace('y',"(ýÿ)")
112
		.replace('a',"(a|à|á|â|ã|ä|å)")
113
		.replace('æ',"(ae|æ)")
114
		.replace('ç',"(ç|c)")
115
		.replace('ñ',"(ñ|n)")
116
		.replace('œ',"(œ|oe)");
1 aurelien 117
}
118
 
3 aurelien 119
function afficherLienDefinitions() {
120
	html = '<div id="conteneur_activation_definition"><a href="#">rechercher les définitions</a></div>';
121
	$('#conteneur_activation_definition').live('click', function(event) {
122
		event.preventDefault();
10 raphael 123
		//supprimerToutesDefinitions();
3 aurelien 124
		if(motsAyantDefinition == null) {
125
			getMotsADefinitions();
10 raphael 126
			ajouterListenerDefinitions();
3 aurelien 127
		} else {
7 raphael 128
			$(set).remplacerDefinitions(motsAyantDefinition);
3 aurelien 129
		}
130
	});
131
	$('body').append(html);
132
}
133
 
1 aurelien 134
function normaliserMotPourRecherche(str) {
135
	str = supprimerAccents(str);
136
	str = etendreChaineAccents(str);
137
	return str;
138
}
139
 
3 aurelien 140
$.fn.remplacerDefinitions = function(mots) {
10 raphael 141
    this.each(function() {
142
        $(this).contents().filter(function() {
143
        	return (this.nodeType == 3 && ! (($(this).data("count") || 0) > max_passages));
144
        }).each(function() {
145
			$(this).data("count", ($(this).data("count") || 0) + 1);
146
			ttexte = texte = $(this).text();
147
			matches_list = nesseciteSubstitution(texte)[0];
148
			if(!matches_list) return true; // TODO: data("count") = max_passages ?
149
 
150
			for(i in matches_list) {
151
				couple = matches_list[i];
152
				mot_contextualise = couple[2];
153
				cle = texte_to_mot(mot_contextualise);
154
 
155
				if(! (ttexte = rechercherEtRemplacerMotParDefinition(texte, cle, couple))) break;
156
				texte = ttexte;
157
			}
158
			$(this).replaceWith(texte);
159
		});
160
	});
161
	return this;
1 aurelien 162
}
163
 
10 raphael 164
function rechercherEtRemplacerMotParDefinition(texte, mot, couple) {
165
	var full_contexte = couple[0],
166
	prefix_solo = couple[1],
167
	mot_contextualise = couple[2];
3 aurelien 168
 
10 raphael 169
	// cf exclureSpan
170
	if(new RegExp('((?:class="definition_term">)|(?:rel=")|(?:[çéèêîïô]))(' + mot + "\\w{0,2}" + ')' + "\\b", "ig").test(full_contexte)) return;
171
 
172
	templateMotADefinition = formaterTemplateMotADefinition(mot_contextualise, mot);
173
	bloc_replace = full_contexte.replace(mot_contextualise, templateMotADefinition);
174
	return texte.replace(full_contexte, bloc_replace);
3 aurelien 175
}
176
 
10 raphael 177
 
178
function nesseciteSubstitution(texte) {
179
	var liste = [], liste_complete = [];
180
 
181
	// regexp d'extraction du contexte, sans limite aux bords de "mot",
182
	// peu amener des faux positifs, et notamment ne pas matcher au mieux.
183
	// Eg: subsessiles match "sessile" et non "subsessile".
184
	while( (matches_c = regexp_thin.exec(texte)) ) {
185
		//liste.push(getUnique(clean(matches_c)));
186
		liste.push(matches_c);
187
	}
188
 
189
	if(liste.length == 0) return [null];
190
 
191
	// regexp de validation des bordure de mots, ne conserve que des matches correct
192
	// vis-à-vis de la bordure gauche
193
	while( (matches_c = regexp_complete.exec(texte)) ) {
194
		liste_complete.push(matches_c[1]);
195
	}
196
 
197
	// si une match n'est pas dans le tableau des mots autorisés (indexOf)
198
	for(var i = 0; i < liste.length; i++) {
199
		if($.inArray(liste[i][2], liste_complete) == -1) liste.splice(i, 1);
200
	}
201
 
202
	return [liste, liste_complete]
203
}
204
 
205
 
3 aurelien 206
function getMotsADefinitions() {
1 aurelien 207
	$.ajax({
10 raphael 208
		url: URL_BASE_SERVICE+'mots/', // provient de dictionnaire.config.js
1 aurelien 209
		success: function(data) {
210
			motsAyantDefinition = data;
10 raphael 211
			batirRegexp(motsAyantDefinition);
7 raphael 212
			$(set).remplacerDefinitions(motsAyantDefinition);
10 raphael 213
 
1 aurelien 214
		},
215
		dataType: "JSON",
216
		global: false
217
	});
218
}
219
 
10 raphael 220
function batirRegexp(mots) {
221
	var restr = '';
222
	$.each(mots, function(index, mot) {
223
		if(restr != '') restr += '|';
224
 
225
		if(mot.length < 3) {
226
			restr += mot;
227
		}
228
		// gestion masculin/féminin en "eux/euse"
229
		else if(mot.substr(-1) == 'x') {
230
			restr += mot.slice(0, -1) + "(?:x|se|ses)";
231
		}
232
		else if(mot.substr(-4) == 'euse') { // TODO: fix DB
233
			restr += mot.slice(0, -2) + "(?:x|se|ses)";
234
		}
235
		else
236
			restr += mot + "\\w{0,2}";
237
	});
238
 
239
	regexp_complete = new RegExp("(?:\\s|^|\\W)(" + restr + ")\\b", "ig"); // accents
240
	regexp_thin = new RegExp(exclureSpan + "(" + restr + ")\\b", "ig");
241
}
242
 
243
function formaterTemplateMotADefinition(motOriginal, motSimplifie) {
1 aurelien 244
	definitionHtml = '<span rel="'+motSimplifie+'" class="definition_term">'
10 raphael 245
		+motOriginal+
246
		'</span>';
1 aurelien 247
	return definitionHtml;
248
}
249
 
250
function ajouterListenerDefinitions() {
251
	$('.definition_term').live('mouseover mouseout', function(event) {
10 raphael 252
		if (event.type == 'mouseover') {
253
			event.preventDefault();
254
			afficherDefinition($(this));
255
		} else {
256
			cacherPopupsDefinitions();
257
		}
1 aurelien 258
	});
259
}
260
 
261
function afficherDefinition(element) {
262
	mot = element.attr('rel');
263
	if(dictionnaire[mot] != null) {
3 aurelien 264
		element.after(formaterDefinition(element));
1 aurelien 265
		afficherPopupDefinition();
266
	} else {
267
		chargerDefinitionDistante(element);
268
	}
269
}
270
 
3 aurelien 271
function chargerDefinitionDistante(element) {
272
	date = new Date();
5 aurelien 273
	mot = element.attr('rel');
1 aurelien 274
	$.ajax({
10 raphael 275
		url: URL_BASE_SERVICE+'def/'+mot, // provient de dictionnaire.config.js
1 aurelien 276
		success: function(data) {
277
			retour = data;
278
			definition = retour.valeur;
279
			dictionnaire[mot] = definition;
3 aurelien 280
			element.after(formaterDefinition(element));
1 aurelien 281
			afficherPopupDefinition();
282
		},
283
		dataType: "JSON",
284
		global: false
285
	});
286
}
287
 
288
function afficherPopupDefinition() {
289
	$(".definition_container").css({'top':mouseY + 20,'left':mouseX - 10}).fadeIn('slow');
290
}
291
 
292
function cacherPopupsDefinitions() {
293
	$(".definition_container").remove();
294
}
295
 
296
function formaterDefinition(element) {
297
	mot = element.attr('rel');
298
	data = dictionnaire[mot];
299
	defHtml = '<div class="definition_container">'+
10 raphael 300
		'<span class="definition_container_fleche"></span>'+
301
		'<span class="definition">'+data+'</span>'+
302
		'</div>';
1 aurelien 303
	return defHtml;
304
}
305
 
306
function supprimerToutesDefinitions() {
307
	$('.definition_term').each(function() {
308
		$(this).replaceWith($(this).html());
309
	});
310
	cacherPopupsDefinitions();
311
}
312
 
313
$(document).bind('mousemove', function(e){
314
	mouseX = e.pageX;
315
    mouseY = e.pageY - $(window).scrollTop();
316
});
317
 
318
$(document).ready(function() {
10 raphael 319
	$.getScript(URL_BASE_SERVICE + "../../../scripts/helpers.js");
5 aurelien 320
	getMotsADefinitions();
10 raphael 321
	ajouterListenerDefinitions();
3 aurelien 322
	afficherLienDefinitions();
1 aurelien 323
});