Subversion Repositories Applications.dictionnaire

Rev

Rev 12 | Rev 15 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 12 Rev 14
1
/*
1
/*
2
  Surligneur de mot (glossaire)
2
  Surligneur de mot (glossaire)
3
 
3
 
4
  0) tout d'abord la liste des mots est récupée (getMotsADefinitions) et stockée dans motsAyantDefinition
4
  0) tout d'abord la liste des mots est récupée (getMotsADefinitions) et stockée dans motsAyantDefinition
5
 
5
 
6
  1) chaque mot de la liste subie une transformation progressive en pattern:
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")
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
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}
9
  * les autres mots se voit ajoutée la possibilité de matcher sur \\w{0,2}
10
 
10
 
11
  2) puis plusieurs RegExp sont baties à partir de la liste de pattern préliminaires
11
  2) puis plusieurs RegExp sont baties à partir de la liste de pattern préliminaires
12
  * regexp_complete permet d'assurer que le mot:
12
  * regexp_complete permet d'assurer que le mot:
13
  ** commence en début de ligne
13
  ** commence en début de ligne
14
  ** suit un espace
14
  ** suit un espace
15
  ** ne suit pas un caractère
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 ]
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
 
17
 
18
  * regexp_thin permet de s'assurer que le mot:
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>
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>
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)
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
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
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.
24
  test d'inclusion dans .definition_term était faire plus tardivement.
25
 
25
 
26
  3) remplacerDefinitions() prend la liste des nodes susceptibles de nécessiter un traitement
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)
27
  et ignore ceux ayant déjà été traités [max_passages] fois (4 par défaut)
28
 
28
 
29
  4) pour chaque node:
29
  4) pour chaque node:
30
  * le compteur de passage est incrémenté
30
  * le compteur de passage est incrémenté
31
  * nesseciteSubstitution() est appellé
31
  * nesseciteSubstitution() est appellé
32
 
32
 
33
  == nesseciteSubstitution() ==:
33
  == nesseciteSubstitution() ==:
34
  ** Utilise regexp_thin pour extraire les mots restant à définir ainsi qu'une partie de leur contexte.
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
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
36
  ** exlue les faux positifs possibles de regexp_thin
37
  ** renvoi les matches de regexp_thin (qui présente plus de contexte)
37
  ** renvoi les matches de regexp_thin (qui présente plus de contexte)
38
  =============================
38
  =============================
39
 
39
 
40
  * pour chaque correspondance retournée par nesseciteSubstitution(),
40
  * pour chaque correspondance retournée par nesseciteSubstitution(),
41
  le mot d'origine (tel que dans motsAyantDefinition)
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).
42
  est déterminé en utilisant le levenstein (un cache est mis en place, cf texte_to_mot() dans helpers.js).
43
 
43
 
44
  * rechercherEtRemplacerMotParDefinition() est appelée pour effectuer la substitution dans le node
44
  * rechercherEtRemplacerMotParDefinition() est appelée pour effectuer la substitution dans le node
45
 
45
 
46
 
46
 
47
  Contraintes rencontrées:
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.
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
49
  2) vérifier les bordures
50
  3) vérifier bordure d'accents:
50
  3) vérifier bordure d'accents:
51
  \\b(é) dans une regexp ne peut matcher une chaîne JS UTF-8
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
52
  http://stackoverflow.com/questions/2881445/utf-8-word-boundary-regex-in-javascript
53
*/
53
*/
54
 
54
 
55
var dictionnaire = new Array();
55
var dictionnaire = new Array();
56
var motsAyantDefinition = null;
56
var motsAyantDefinition = null;
57
var mouseX = null;
57
var mouseX = null;
58
var mouseY = null;
58
var mouseY = null;
59
var active = false;
59
var active = false;
60
var set = '#zone-droite p, #zone-droite span:not(.definition_term), #zone-droite td, #zone-droite pre, #zone-droite div, #zone-droite li';
60
var set = '#zone-droite p, #zone-droite span:not(.definition_term), #zone-droite td, #zone-droite pre, #zone-droite div, #zone-droite li';
61
 
61
 
62
// Note: utiliser \\b plutôt que \\W pour matcher les bordures de mots
62
// Note: utiliser \\b plutôt que \\W pour matcher les bordures de mots
63
// en incluant début et fin de ligne
63
// en incluant début et fin de ligne
64
var regexp_complete = null;
64
var regexp_complete = null;
65
var regexp_thin = null;
65
var regexp_thin = null;
66
 
66
 
67
// nombre maximum de passages de la détection sur un node donné
67
// nombre maximum de passages de la détection sur un node donné
68
var max_passages = 4;
68
var max_passages = 4;
69
 
69
 
70
// echo $(mysql<<<"SELECT cle FROM definitions"|tr "[a-z,-]" o|sed -e 's/o//g' -e "s/\(.\)/\1\n/g"|sort -u)
70
// echo $(mysql<<<"SELECT cle FROM definitions"|tr "[a-z,-]" o|sed -e 's/o//g' -e "s/\(.\)/\1\n/g"|sort -u)
71
// ç é è ê î ï ô (2013/07/03)
71
// ç é è ê î ï ô (2013/07/03)
72
// TODO: http://xregexp.com/plugins/
72
// TODO: http://xregexp.com/plugins/
73
var	exclureSpan = '([^' +
73
var	exclureSpan = '([^' +
74
	'(?:class="definition_term">)' + // mot contextualisé déjà dictionnarisé
74
	'(?:class="definition_term">)' + // mot contextualisé déjà dictionnarisé
75
	'(?:rel=")' + // la valeur du rel="" d'un mot déjà dictionnarisé
75
	'(?:rel=")' + // la valeur du rel="" d'un mot déjà dictionnarisé
76
	'çéèêîïô' + // les accents, non-traités dans la regexp générale et qui ne doivent pas
76
	'çéèêîïô' + // les accents, non-traités dans la regexp générale et qui ne doivent pas
77
	// être pris pour des bordures de mots
77
	// être pris pour des bordures de mots
78
	'\\w' +
78
	'\\w' +
79
	']|^){1,24}';
79
	']|^){1,24}';
80
 
80
 
81
 
81
 
82
function afficherLienDefinitions() {
82
function afficherLienDefinitions() {
83
	html = '<div id="conteneur_activation_definition"><a href="#">rechercher les définitions</a></div>';
83
	html = '<div id="conteneur_activation_definition"><a href="#">rechercher les définitions</a></div>';
84
	$('#conteneur_activation_definition').live('click', function(event) {
84
	$('#conteneur_activation_definition').live('click', function(event) {
85
		event.preventDefault();
85
		event.preventDefault();
86
		//supprimerToutesDefinitions();
86
		//supprimerToutesDefinitions();
87
		if(motsAyantDefinition == null) {
87
		if(motsAyantDefinition == null) {
88
			getMotsADefinitions();
88
			getMotsADefinitions();
89
			ajouterListenerDefinitions();
89
			ajouterListenerDefinitions();
90
		} else {
90
		} else {
91
			$(set).remplacerDefinitions(motsAyantDefinition);
91
			$(set).remplacerDefinitions(motsAyantDefinition);
92
		}
92
		}
93
	});
93
	});
94
	$('body').append(html);
94
	$('body').append(html);
95
}
95
}
96
 
96
 
97
$.fn.remplacerDefinitions = function(mots) {
97
$.fn.remplacerDefinitions = function(mots) {
98
    this.each(function() {
98
    this.each(function() {
99
        $(this).contents().filter(function() {
99
        $(this).contents().filter(function() {
100
        	return (this.nodeType == 3 && ! (($(this).data("count") || 0) > max_passages));
100
        	return (this.nodeType == 3 && ! (($(this).data("count") || 0) > max_passages));
101
        }).each(function() {
101
        }).each(function() {
102
			$(this).data("count", ($(this).data("count") || 0) + 1);
102
			$(this).data("count", ($(this).data("count") || 0) + 1);
103
			ttexte = texte = $(this).text();
103
			ttexte = texte = $(this).text();
104
			matches_list = nesseciteSubstitution(texte)[0];
104
			matches_list = nesseciteSubstitution(texte)[0];
105
			if(!matches_list) return true; // TODO: data("count") = max_passages ?
105
			if(!matches_list) return true; // TODO: data("count") = max_passages ?
106
 
106
 
107
			for(i in matches_list) {
107
			for(i in matches_list) {
108
				couple = matches_list[i];
108
				couple = matches_list[i];
109
				mot_contextualise = couple[2];
109
				mot_contextualise = couple[2];
110
				cle = texte_to_mot(mot_contextualise);
110
				cle = texte_to_mot(mot_contextualise);
111
 
111
 
112
				if(! (ttexte = rechercherEtRemplacerMotParDefinition(texte, cle, couple))) break;
112
				if(! (ttexte = rechercherEtRemplacerMotParDefinition(texte, cle, couple))) break;
113
				texte = ttexte;
113
				texte = ttexte;
114
			}
114
			}
115
			$(this).replaceWith(texte);
115
			$(this).replaceWith(texte);
116
		});
116
		});
117
	});
117
	});
118
	return this;
118
	return this;
119
}
119
}
120
 
120
 
121
function rechercherEtRemplacerMotParDefinition(texte, mot, couple) {
121
function rechercherEtRemplacerMotParDefinition(texte, mot, couple) {
122
	var full_contexte = couple[0],
122
	var full_contexte = couple[0],
123
	prefix_solo = couple[1],
123
	prefix_solo = couple[1],
124
	mot_contextualise = couple[2];
124
	mot_contextualise = couple[2];
125
 
125
 
126
	// cf exclureSpan
126
	// cf exclureSpan
127
	if(new RegExp('((?:class="definition_term">)|(?:rel=")|(?:[çéèêîïô]))(' + mot + "\\w{0,2}" + ')' + "\\b", "ig").test(full_contexte)) return;
127
	if(new RegExp('((?:class="definition_term">)|(?:rel=")|(?:[çéèêîïô]))(' + mot + "\\w{0,2}" + ')' + "\\b", "ig").test(full_contexte)) return;
128
 
128
 
129
	templateMotADefinition = formaterTemplateMotADefinition(mot_contextualise, mot);
129
	templateMotADefinition = formaterTemplateMotADefinition(mot_contextualise, mot);
130
	bloc_replace = full_contexte.replace(mot_contextualise, templateMotADefinition);
130
	bloc_replace = full_contexte.replace(mot_contextualise, templateMotADefinition);
131
	return texte.replace(full_contexte, bloc_replace);
131
	return texte.replace(full_contexte, bloc_replace);
132
}
132
}
133
 
133
 
134
 
134
 
135
function nesseciteSubstitution(texte) {
135
function nesseciteSubstitution(texte) {
136
	var liste = [], liste_complete = [];
136
	var liste = [], liste_complete = [];
137
 
137
 
138
	// regexp d'extraction du contexte, sans limite aux bords de "mot",
138
	// regexp d'extraction du contexte, sans limite aux bords de "mot",
139
	// peu amener des faux positifs, et notamment ne pas matcher au mieux.
139
	// peu amener des faux positifs, et notamment ne pas matcher au mieux.
140
	// Eg: subsessiles match "sessile" et non "subsessile".
140
	// Eg: subsessiles match "sessile" et non "subsessile".
141
	while( (matches_c = regexp_thin.exec(texte)) ) liste.push(matches_c);
141
	while( (matches_c = regexp_thin.exec(texte)) ) liste.push(matches_c);
142
	if(liste.length == 0) return [null];
142
	if(liste.length == 0) return [null];
143
 
143
 
144
	// regexp de validation des bordure de mots, ne conserve que des matches correct
144
	// regexp de validation des bordure de mots, ne conserve que des matches correct
145
	// vis-à-vis de la bordure gauche
145
	// vis-à-vis de la bordure gauche
146
	while( (matches_c = regexp_complete.exec(texte)) ) liste_complete.push(matches_c[1]);
146
	while( (matches_c = regexp_complete.exec(texte)) ) liste_complete.push(matches_c[1]);
147
 
147
 
148
	// si une match n'est pas dans le tableau des mots autorisés (indexOf)
148
	// si une match n'est pas dans le tableau des mots autorisés (indexOf)
149
	for(var i = 0; i < liste.length; i++) {
149
	for(var i = 0; i < liste.length; i++) {
150
		if($.inArray(liste[i][2], liste_complete) == -1) liste.splice(i, 1);
150
		if($.inArray(liste[i][2], liste_complete) == -1) liste.splice(i, 1);
151
	}
151
	}
152
 
152
 
153
	return [liste, liste_complete]
153
	return [liste, liste_complete]
154
}
154
}
155
 
155
 
156
function getMotsADefinitions() {
156
function getMotsADefinitions() {
157
	$.ajax({
157
	$.ajax({
158
		url: URL_BASE_SERVICE+'mots/', // provient de dictionnaire.config.js
158
		url: URL_BASE_SERVICE+'mots/', // provient de dictionnaire.config.js
159
		success: function(data) {
159
		success: function(data) {
160
			motsAyantDefinition = data;
160
			motsAyantDefinition = data;
161
			batirRegexp(motsAyantDefinition);
161
			batirRegexp(motsAyantDefinition);
162
			$(set).remplacerDefinitions(motsAyantDefinition);
162
			$(set).remplacerDefinitions(motsAyantDefinition);
163
		},
163
		},
164
		dataType: "JSON",
164
		dataType: "JSON",
165
		global: false
165
		global: false
166
	});
166
	});
167
}
167
}
168
 
168
 
169
function batirRegexp(mots) {
169
function batirRegexp(mots) {
170
	var restr = '';
170
	var restr = '';
171
	$.each(mots, function(index, mot) {
171
	$.each(mots, function(index, mot) {
172
		if(restr != '') restr += '|';
172
		if(restr != '') restr += '|';
173
 
173
 
174
		if(mot.length < 3) {
174
		if(mot.length < 3) {
175
			restr += mot;
175
			restr += mot;
176
		}
176
		}
177
		// gestion masculin/féminin en "eux/euse"
177
		// gestion masculin/féminin en "eux/euse"
178
		else if(mot.substr(-1) == 'x') {
178
		else if(mot.substr(-1) == 'x') {
179
			restr += mot.slice(0, -1) + "(?:x|se|ses)";
179
			restr += mot.slice(0, -1) + "(?:x|se|ses)";
180
		}
180
		}
181
		else if(mot.substr(-4) == 'euse') { // TODO: fix DB
181
		else if(mot.substr(-4) == 'euse') { // TODO: fix DB
182
			restr += mot.slice(0, -2) + "(?:x|se|ses)";
182
			restr += mot.slice(0, -2) + "(?:x|se|ses)";
183
		}
183
		}
184
		else
184
		else
185
			restr += mot + "\\w{0,2}";
185
			restr += mot + "\\w{0,2}";
186
	});
186
	});
187
 
187
 
188
	regexp_complete = new RegExp("(?:\\s|^|\\W)(" + restr + ")\\b", "ig"); // accents
188
	regexp_complete = new RegExp("(?:\\s|^|\\W)(" + restr + ")\\b", "ig"); // accents
189
	regexp_thin = new RegExp(exclureSpan + "(" + restr + ")\\b", "ig");
189
	regexp_thin = new RegExp(exclureSpan + "(" + restr + ")\\b", "ig");
190
}
190
}
191
 
191
 
192
function formaterTemplateMotADefinition(motOriginal, motSimplifie) {
192
function formaterTemplateMotADefinition(motOriginal, motSimplifie) {
193
	definitionHtml = '<span rel="'+motSimplifie+'" class="definition_term">'
193
	definitionHtml = '<span rel="'+motSimplifie+'" class="definition_term">'
194
		+motOriginal+
194
		+motOriginal+
195
		'</span>';
195
		'</span>';
196
	return definitionHtml;
196
	return definitionHtml;
197
}
197
}
198
 
198
 
199
function ajouterListenerDefinitions() {
199
function ajouterListenerDefinitions() {
200
	$('.definition_term').live('mouseover mouseout', function(event) {
200
	$('.definition_term').live('mouseover mouseout', function(event) {
201
		if (event.type == 'mouseover') {
201
		if (event.type == 'mouseover') {
202
			event.preventDefault();
202
			event.preventDefault();
203
			afficherDefinition($(this));
203
			afficherDefinition($(this));
204
		} else {
204
		} else {
205
			cacherPopupsDefinitions();
205
			cacherPopupsDefinitions();
206
		}
206
		}
207
	});
207
	});
208
}
208
}
209
 
209
 
210
function afficherDefinition(element) {
210
function afficherDefinition(element) {
211
	mot = element.attr('rel');
211
	mot = element.attr('rel');
212
	if(dictionnaire[mot] != null) {
212
	if(dictionnaire[mot] != null) {
213
		element.after(formaterDefinition(element));
213
		element.after(formaterDefinition(element));
214
		afficherPopupDefinition();
214
		afficherPopupDefinition();
215
	} else {
215
	} else {
216
		chargerDefinitionDistante(element);
216
		chargerDefinitionDistante(element);
217
	}
217
	}
218
}
218
}
219
 
219
 
220
function chargerDefinitionDistante(element) {
220
function chargerDefinitionDistante(element) {
221
	date = new Date();
221
	date = new Date();
222
	mot = element.attr('rel');
222
	mot = element.attr('rel');
223
	$.ajax({
223
	$.ajax({
224
		url: URL_BASE_SERVICE+'def/'+mot, // provient de dictionnaire.config.js
224
		url: URL_BASE_SERVICE+'def/'+mot, // provient de dictionnaire.config.js
225
		success: function(data) {
225
		success: function(data) {
226
			retour = data;
226
			retour = data;
227
			definition = retour.valeur;
227
			definition = retour.valeur;
228
			dictionnaire[mot] = definition;
228
			dictionnaire[mot] = definition;
229
			element.after(formaterDefinition(element));
229
			element.after(formaterDefinition(element));
230
			afficherPopupDefinition();
230
			afficherPopupDefinition();
231
		},
231
		},
232
		dataType: "JSON",
232
		dataType: "JSON",
233
		global: false,
233
		global: false,
234
		async: false
234
		async: false
235
	});
235
	});
236
}
236
}
237
 
237
 
238
function afficherPopupDefinition() {
238
function afficherPopupDefinition() {
239
	$(".definition_container").css({'top':mouseY + 20,'left':mouseX - 10}).fadeIn('slow');
239
	$(".definition_container").css({'top':mouseY + 20,'left':mouseX - 10}).fadeIn('slow');
240
}
240
}
241
 
241
 
242
function cacherPopupsDefinitions() {
242
function cacherPopupsDefinitions() {
243
	$(".definition_container").remove();
243
	$(".definition_container").remove();
244
}
244
}
245
 
245
 
246
function formaterDefinition(element) {	
246
function formaterDefinition(element) {	
247
	mot = element.attr('rel');
247
	mot = element.attr('rel');
248
	data = dictionnaire[mot];
248
	data = dictionnaire[mot];
249
	defHtml = '<div class="definition_container">'+
249
	defHtml = '<div class="definition_container">'+
250
		'<span class="definition_container_fleche"></span>'+
250
		'<span class="definition_container_fleche"></span>'+
251
		'<span class="definition">'+data+'</span>'+
251
		'<span class="definition">'+data+'</span>'+
252
		'</div>';
252
		'</div>';
253
	return defHtml;
253
	return defHtml;
254
}
254
}
255
 
255
 
256
function supprimerToutesDefinitions() {
256
function supprimerToutesDefinitions() {
257
	$('.definition_term').each(function() {
257
	$('.definition_term').each(function() {
258
		$(this).replaceWith($(this).html());
258
		$(this).replaceWith($(this).html());
259
	});
259
	});
260
	cacherPopupsDefinitions();
260
	cacherPopupsDefinitions();
261
}
261
}
262
 
262
 
263
$(document).bind('mousemove', function(e){
263
$(document).bind('mousemove', function(e){
264
	mouseX = e.pageX;
264
	mouseX = e.pageX;
265
    mouseY = e.pageY - $(window).scrollTop();
265
    mouseY = e.pageY - $(window).scrollTop();
266
});
266
});
-
 
267
 
-
 
268
$(document).ajaxStop(function() {
-
 
269
	$(set).remplacerDefinitions(motsAyantDefinition);
-
 
270
});
267
 
271
 
268
$(document).ready(function() {
272
$(document).ready(function() {
269
	$.getScript(URL_BASE_SERVICE + "../../../scripts/helpers.js");
273
	$.getScript(URL_BASE_SERVICE + "../../../scripts/helpers.js");
270
	getMotsADefinitions();
274
	getMotsADefinitions();
271
	ajouterListenerDefinitions();
275
	ajouterListenerDefinitions();
272
	afficherLienDefinitions();
276
	afficherLienDefinitions();
273
});
277
});