Subversion Repositories Applications.dictionnaire

Rev

Rev 7 | Rev 11 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 7 Rev 10
Line -... Line 1...
-
 
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
var dictionnaire = new Array();
55
var dictionnaire = new Array();
2
var motsAyantDefinition = null;
56
var motsAyantDefinition = null;
3
var mouseX = null;
57
var mouseX = null;
4
var mouseY = null;
58
var mouseY = null;
5
var active = false;
59
var active = false;
6
var set = 'p, span, td, pre, div';
60
var set = 'p, span:not(.definition_term), td, pre, div, li';
-
 
61
 
-
 
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;
-
 
65
 
-
 
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}';
Line 7... Line -...
7
 
-
 
8
function getUrlBaseService() {
-
 
9
	// provient de dictionnaire.config.js
-
 
10
	return URL_BASE_SERVICE;
-
 
Line 11... Line 82...
11
}
82
 
12
 
83
 
13
function supprimerAccents(str) { 
84
function supprimerAccents(str) { 
14
    var rExps=[ 
85
    var rExps=[ 
15
    {re:/[\xC0-\xC6]/g, ch:'A'}, 
86
		{re:/[\xC0-\xC6]/g, ch:'A'}, 
16
    {re:/[\xE0-\xE6]/g, ch:'a'}, 
87
		{re:/[\xE0-\xE6]/g, ch:'a'}, 
17
    {re:/[\xC8-\xCB]/g, ch:'E'}, 
88
		{re:/[\xC8-\xCB]/g, ch:'E'}, 
18
    {re:/[\xE8-\xEB]/g, ch:'e'}, 
89
		{re:/[\xE8-\xEB]/g, ch:'e'}, 
19
    {re:/[\xCC-\xCF]/g, ch:'I'}, 
90
		{re:/[\xCC-\xCF]/g, ch:'I'}, 
20
    {re:/[\xEC-\xEF]/g, ch:'i'}, 
91
		{re:/[\xEC-\xEF]/g, ch:'i'}, 
21
    {re:/[\xD2-\xD6]/g, ch:'O'}, 
92
		{re:/[\xD2-\xD6]/g, ch:'O'}, 
22
    {re:/[\xF2-\xF6]/g, ch:'o'}, 
93
		{re:/[\xF2-\xF6]/g, ch:'o'}, 
23
    {re:/[\xD9-\xDC]/g, ch:'U'}, 
94
		{re:/[\xD9-\xDC]/g, ch:'U'}, 
24
    {re:/[\xF9-\xFC]/g, ch:'u'}, 
95
		{re:/[\xF9-\xFC]/g, ch:'u'}, 
25
    {re:/[\xD1]/g, ch:'N'}, 
96
		{re:/[\xD1]/g, ch:'N'}, 
26
    {re:/[\xF1]/g, ch:'n'} ]; 
97
		{re:/[\xF1]/g, ch:'n'} ]; 
27
    for(var i=0, len=rExps.length; i<len; i++) 
98
    for(var i=0, len=rExps.length; i<len; i++) 
28
            str=str.replace(rExps[i].re, rExps[i].ch);
99
        str=str.replace(rExps[i].re, rExps[i].ch);
Line 29... Line 100...
29
    return str; 
100
    return str; 
30
};
101
};
31
 
102
 
32
// c'est moche mais le mauvais support de l'unicode dans 
103
// c'est moche mais le mauvais support de l'unicode dans 
33
// javascript ne me permet pas de faire mieux
104
// javascript ne me permet pas de faire mieux
34
function etendreChaineAccents(str) {
105
function etendreChaineAccents(str) {
35
    return str.replace('a',"(a|à|á|â|ã|ä|å)")
106
    return str.replace('a',"(a|à|á|â|ã|ä|å)")
36
    .replace('e',"(e|è|é|ê|ë)")
107
		.replace('e',"(e|è|é|ê|ë)")
37
    .replace('i',"(i|ì|í|î|ï)")
108
		.replace('i',"(i|ì|í|î|ï)")
38
    .replace('o',"(o|ò|ó|ô|õ|ö)")
109
		.replace('o',"(o|ò|ó|ô|õ|ö)")
39
    .replace('u',"(u|ù|ú|û|ü)")
110
		.replace('u',"(u|ù|ú|û|ü)")
40
    .replace('y',"(ýÿ)")
111
		.replace('y',"(ýÿ)")
41
    .replace('a',"(a|à|á|â|ã|ä|å)")
112
		.replace('a',"(a|à|á|â|ã|ä|å)")
42
    .replace('æ',"(ae|æ)")
113
		.replace('æ',"(ae|æ)")
43
    .replace('ç',"(ç|c)")
114
		.replace('ç',"(ç|c)")
Line 44... Line 115...
44
    .replace('ñ',"(ñ|n)") 
115
		.replace('ñ',"(ñ|n)") 
45
    .replace('œ',"(œ|oe)");  
116
		.replace('œ',"(œ|oe)");  
46
}
117
}
47
 
118
 
48
function afficherLienDefinitions() {
119
function afficherLienDefinitions() {
49
	html = '<div id="conteneur_activation_definition"><a href="#">rechercher les définitions</a></div>';
120
	html = '<div id="conteneur_activation_definition"><a href="#">rechercher les définitions</a></div>';
50
	$('#conteneur_activation_definition').live('click', function(event) {
121
	$('#conteneur_activation_definition').live('click', function(event) {
-
 
122
		event.preventDefault();
51
		event.preventDefault();
123
		//supprimerToutesDefinitions();
52
		supprimerToutesDefinitions();
124
		if(motsAyantDefinition == null) {
53
		if(motsAyantDefinition == null) {
125
			getMotsADefinitions();
54
			getMotsADefinitions();
126
			ajouterListenerDefinitions();
55
		} else {
127
		} else {
Line 64... Line 136...
64
	str = etendreChaineAccents(str);
136
	str = etendreChaineAccents(str);
65
	return str;
137
	return str;
66
}
138
}
Line 67... Line 139...
67
 
139
 
68
$.fn.remplacerDefinitions = function(mots) {
140
$.fn.remplacerDefinitions = function(mots) {
69
    this.each(function() { 	
141
    this.each(function() {
70
          $(this).contents().filter(function() { 
142
        $(this).contents().filter(function() {
71
        	  return this.nodeType == 3;   
143
        	return (this.nodeType == 3 && ! (($(this).data("count") || 0) > max_passages));
72
          }).each(function() {
144
        }).each(function() {
73
        	  element = $(this);
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 ?
74
        	  texte = element.text();
149
 
-
 
150
			for(i in matches_list) {
-
 
151
				couple = matches_list[i];
75
        	  if(texte != "") {
152
				mot_contextualise = couple[2];
-
 
153
				cle = texte_to_mot(mot_contextualise);
76
	        	  $.each(mots, function(index, valeur) {	
154
 
77
	        		  texte = rechercherEtRemplacerMotParDefinition(texte, valeur);
155
				if(! (ttexte = rechercherEtRemplacerMotParDefinition(texte, cle, couple))) break;
-
 
156
				texte = ttexte;
78
	        	  });	
157
			}
79
	        	  element.replaceWith(texte);
-
 
80
        	  }
158
			$(this).replaceWith(texte);
81
          });
159
		});
82
    });
160
	});
83
    return this;
161
	return this;
84
}
162
}
85
 
163
 
86
function rechercherEtRemplacerMotParDefinition(texte, mot) {
164
function rechercherEtRemplacerMotParDefinition(texte, mot, couple) {
87
	exclureSpan = '[^(?:class="definition_term">)]';
-
 
88
    regExp = new RegExp(exclureSpan+"[ |,|-|;|.]+("+mot+")[a-zA-Z]{1}", 'ig');
165
	var full_contexte = couple[0],
89
  	termeDansTexte = regExp.exec(texte);  	
166
	prefix_solo = couple[1],
-
 
167
	mot_contextualise = couple[2];
90
  	if(termeDansTexte != null && termeDansTexte.length > 1) {
168
 
91
	  	motOriginal = termeDansTexte[1];
169
	// cf exclureSpan
92
		templateMotADefinition = formaterTemplateMotADefinition(motOriginal);
-
 
93
		texte = texte.replace(motOriginal, templateMotADefinition);
-
 
Line -... Line 170...
-
 
170
	if(new RegExp('((?:class="definition_term">)|(?:rel=")|(?:[çéèêîïô]))(' + mot + "\\w{0,2}" + ')' + "\\b", "ig").test(full_contexte)) return;
-
 
171
 
94
  	}
172
	templateMotADefinition = formaterTemplateMotADefinition(mot_contextualise, mot);
95
 
173
	bloc_replace = full_contexte.replace(mot_contextualise, templateMotADefinition);
Line -... Line 174...
-
 
174
	return texte.replace(full_contexte, bloc_replace);
-
 
175
}
-
 
176
 
-
 
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]
96
  	return texte;
203
}
97
}
204
 
98
 
205
 
99
function getMotsADefinitions() {
206
function getMotsADefinitions() {
100
	$.ajax({
207
	$.ajax({
-
 
208
		url: URL_BASE_SERVICE+'mots/', // provient de dictionnaire.config.js
101
		url: getUrlBaseService()+'mots/',
209
		success: function(data) {
-
 
210
			motsAyantDefinition = data;
102
		success: function(data) {
211
			batirRegexp(motsAyantDefinition);
103
			motsAyantDefinition = data;
212
			$(set).remplacerDefinitions(motsAyantDefinition);
104
			$(set).remplacerDefinitions(motsAyantDefinition);
213
 
105
		},
214
		},
106
		dataType: "JSON",
-
 
107
		global: false
-
 
108
	});
215
		dataType: "JSON",
Line 109... Line 216...
109
	
216
		global: false
-
 
217
	});
-
 
218
}
-
 
219
 
-
 
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
 
110
	ajouterListenerDefinitions();
239
	regexp_complete = new RegExp("(?:\\s|^|\\W)(" + restr + ")\\b", "ig"); // accents
111
}
240
	regexp_thin = new RegExp(exclureSpan + "(" + restr + ")\\b", "ig");
112
 
241
}
113
function formaterTemplateMotADefinition(motOriginal) {
242
 
114
	motSimplifie = supprimerAccents(motOriginal);
243
function formaterTemplateMotADefinition(motOriginal, motSimplifie) {
115
	definitionHtml = '<span rel="'+motSimplifie+'" class="definition_term">'
244
	definitionHtml = '<span rel="'+motSimplifie+'" class="definition_term">'
Line 116... Line 245...
116
	+motOriginal+
245
		+motOriginal+
117
	'</span>';
246
		'</span>';
118
	return definitionHtml;
247
	return definitionHtml;
119
}
248
}
120
 
249
 
121
function ajouterListenerDefinitions() {
250
function ajouterListenerDefinitions() {
122
	$('.definition_term').live('mouseover mouseout', function(event) {
251
	$('.definition_term').live('mouseover mouseout', function(event) {
123
		  if (event.type == 'mouseover') {
252
		if (event.type == 'mouseover') {
124
			  event.preventDefault();
253
			event.preventDefault();
125
			  afficherDefinition($(this));
254
			afficherDefinition($(this));
Line 126... Line 255...
126
		  } else {
255
		} else {
127
			  cacherPopupsDefinitions();
256
			cacherPopupsDefinitions();
Line 141... Line 270...
141
 
270
 
142
function chargerDefinitionDistante(element) {
271
function chargerDefinitionDistante(element) {
143
	date = new Date();
272
	date = new Date();
144
	mot = element.attr('rel');
273
	mot = element.attr('rel');
145
	$.ajax({
274
	$.ajax({
146
		url: getUrlBaseService()+'def/'+mot,
275
		url: URL_BASE_SERVICE+'def/'+mot, // provient de dictionnaire.config.js
147
		success: function(data) {
276
		success: function(data) {
148
			retour = data;
277
			retour = data;
149
			definition = retour.valeur;
278
			definition = retour.valeur;
150
			dictionnaire[mot] = definition;
279
			dictionnaire[mot] = definition;
Line 166... Line 295...
166
 
295
 
167
function formaterDefinition(element) {	
296
function formaterDefinition(element) {	
168
	mot = element.attr('rel');
297
	mot = element.attr('rel');
169
	data = dictionnaire[mot];
298
	data = dictionnaire[mot];
170
	defHtml = '<div class="definition_container">'+
299
	defHtml = '<div class="definition_container">'+
171
				'<span class="definition_container_fleche"></span>'+
300
		'<span class="definition_container_fleche"></span>'+
172
				'<span class="definition">'+data+'</span>'+
301
		'<span class="definition">'+data+'</span>'+
173
				'</div>';
302
		'</div>';
174
	return defHtml;
303
	return defHtml;
Line 175... Line 304...
175
}
304
}
176
 
305
 
Line 185... Line 314...
185
	mouseX = e.pageX;
314
	mouseX = e.pageX;
186
    mouseY = e.pageY - $(window).scrollTop();
315
    mouseY = e.pageY - $(window).scrollTop();
187
});
316
});
Line 188... Line 317...
188
 
317
 
-
 
318
$(document).ready(function() {
189
$(document).ready(function() {
319
	$.getScript(URL_BASE_SERVICE + "../../../scripts/helpers.js");
-
 
320
	getMotsADefinitions();
190
	getMotsADefinitions();
321
	ajouterListenerDefinitions();
191
	afficherLienDefinitions();
322
	afficherLienDefinitions();
192
});
-