Subversion Repositories Applications.dictionnaire

Rev

Rev 12 | Rev 15 | 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;
12 raphael 60
var set = '#zone-droite p, #zone-droite span:not(.definition_term), #zone-droite td, #zone-droite pre, #zone-droite div, #zone-droite 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;
65
var regexp_thin = null;
66
 
67
// nombre maximum de passages de la détection sur un node donné
68
var max_passages = 4;
69
 
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)
72
// TODO: http://xregexp.com/plugins/
73
var	exclureSpan = '([^' +
74
	'(?:class="definition_term">)' + // mot contextualisé 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
77
	// être pris pour des bordures de mots
78
	'\\w' +
79
	']|^){1,24}';
80
 
81
 
3 aurelien 82
function afficherLienDefinitions() {
83
	html = '<div id="conteneur_activation_definition"><a href="#">rechercher les définitions</a></div>';
84
	$('#conteneur_activation_definition').live('click', function(event) {
85
		event.preventDefault();
10 raphael 86
		//supprimerToutesDefinitions();
3 aurelien 87
		if(motsAyantDefinition == null) {
88
			getMotsADefinitions();
10 raphael 89
			ajouterListenerDefinitions();
3 aurelien 90
		} else {
7 raphael 91
			$(set).remplacerDefinitions(motsAyantDefinition);
3 aurelien 92
		}
93
	});
94
	$('body').append(html);
95
}
96
 
97
$.fn.remplacerDefinitions = function(mots) {
10 raphael 98
    this.each(function() {
99
        $(this).contents().filter(function() {
100
        	return (this.nodeType == 3 && ! (($(this).data("count") || 0) > max_passages));
101
        }).each(function() {
102
			$(this).data("count", ($(this).data("count") || 0) + 1);
103
			ttexte = texte = $(this).text();
104
			matches_list = nesseciteSubstitution(texte)[0];
105
			if(!matches_list) return true; // TODO: data("count") = max_passages ?
106
 
107
			for(i in matches_list) {
108
				couple = matches_list[i];
109
				mot_contextualise = couple[2];
110
				cle = texte_to_mot(mot_contextualise);
111
 
112
				if(! (ttexte = rechercherEtRemplacerMotParDefinition(texte, cle, couple))) break;
113
				texte = ttexte;
114
			}
115
			$(this).replaceWith(texte);
116
		});
117
	});
118
	return this;
1 aurelien 119
}
120
 
10 raphael 121
function rechercherEtRemplacerMotParDefinition(texte, mot, couple) {
122
	var full_contexte = couple[0],
123
	prefix_solo = couple[1],
124
	mot_contextualise = couple[2];
3 aurelien 125
 
10 raphael 126
	// cf exclureSpan
127
	if(new RegExp('((?:class="definition_term">)|(?:rel=")|(?:[çéèêîïô]))(' + mot + "\\w{0,2}" + ')' + "\\b", "ig").test(full_contexte)) return;
128
 
129
	templateMotADefinition = formaterTemplateMotADefinition(mot_contextualise, mot);
130
	bloc_replace = full_contexte.replace(mot_contextualise, templateMotADefinition);
131
	return texte.replace(full_contexte, bloc_replace);
3 aurelien 132
}
133
 
10 raphael 134
 
135
function nesseciteSubstitution(texte) {
136
	var liste = [], liste_complete = [];
137
 
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.
140
	// Eg: subsessiles match "sessile" et non "subsessile".
11 raphael 141
	while( (matches_c = regexp_thin.exec(texte)) ) liste.push(matches_c);
10 raphael 142
	if(liste.length == 0) return [null];
143
 
144
	// regexp de validation des bordure de mots, ne conserve que des matches correct
145
	// vis-à-vis de la bordure gauche
11 raphael 146
	while( (matches_c = regexp_complete.exec(texte)) ) liste_complete.push(matches_c[1]);
10 raphael 147
 
148
	// si une match n'est pas dans le tableau des mots autorisés (indexOf)
149
	for(var i = 0; i < liste.length; i++) {
150
		if($.inArray(liste[i][2], liste_complete) == -1) liste.splice(i, 1);
151
	}
152
 
153
	return [liste, liste_complete]
154
}
155
 
3 aurelien 156
function getMotsADefinitions() {
1 aurelien 157
	$.ajax({
10 raphael 158
		url: URL_BASE_SERVICE+'mots/', // provient de dictionnaire.config.js
1 aurelien 159
		success: function(data) {
160
			motsAyantDefinition = data;
10 raphael 161
			batirRegexp(motsAyantDefinition);
7 raphael 162
			$(set).remplacerDefinitions(motsAyantDefinition);
1 aurelien 163
		},
164
		dataType: "JSON",
165
		global: false
166
	});
167
}
168
 
10 raphael 169
function batirRegexp(mots) {
170
	var restr = '';
171
	$.each(mots, function(index, mot) {
172
		if(restr != '') restr += '|';
173
 
174
		if(mot.length < 3) {
175
			restr += mot;
176
		}
177
		// gestion masculin/féminin en "eux/euse"
178
		else if(mot.substr(-1) == 'x') {
179
			restr += mot.slice(0, -1) + "(?:x|se|ses)";
180
		}
181
		else if(mot.substr(-4) == 'euse') { // TODO: fix DB
182
			restr += mot.slice(0, -2) + "(?:x|se|ses)";
183
		}
184
		else
185
			restr += mot + "\\w{0,2}";
186
	});
187
 
188
	regexp_complete = new RegExp("(?:\\s|^|\\W)(" + restr + ")\\b", "ig"); // accents
189
	regexp_thin = new RegExp(exclureSpan + "(" + restr + ")\\b", "ig");
190
}
191
 
192
function formaterTemplateMotADefinition(motOriginal, motSimplifie) {
1 aurelien 193
	definitionHtml = '<span rel="'+motSimplifie+'" class="definition_term">'
10 raphael 194
		+motOriginal+
195
		'</span>';
1 aurelien 196
	return definitionHtml;
197
}
198
 
199
function ajouterListenerDefinitions() {
200
	$('.definition_term').live('mouseover mouseout', function(event) {
10 raphael 201
		if (event.type == 'mouseover') {
202
			event.preventDefault();
203
			afficherDefinition($(this));
204
		} else {
205
			cacherPopupsDefinitions();
206
		}
1 aurelien 207
	});
208
}
209
 
210
function afficherDefinition(element) {
211
	mot = element.attr('rel');
212
	if(dictionnaire[mot] != null) {
3 aurelien 213
		element.after(formaterDefinition(element));
1 aurelien 214
		afficherPopupDefinition();
215
	} else {
216
		chargerDefinitionDistante(element);
217
	}
218
}
219
 
3 aurelien 220
function chargerDefinitionDistante(element) {
221
	date = new Date();
5 aurelien 222
	mot = element.attr('rel');
1 aurelien 223
	$.ajax({
10 raphael 224
		url: URL_BASE_SERVICE+'def/'+mot, // provient de dictionnaire.config.js
1 aurelien 225
		success: function(data) {
226
			retour = data;
227
			definition = retour.valeur;
228
			dictionnaire[mot] = definition;
3 aurelien 229
			element.after(formaterDefinition(element));
1 aurelien 230
			afficherPopupDefinition();
231
		},
232
		dataType: "JSON",
11 raphael 233
		global: false,
234
		async: false
1 aurelien 235
	});
236
}
237
 
238
function afficherPopupDefinition() {
239
	$(".definition_container").css({'top':mouseY + 20,'left':mouseX - 10}).fadeIn('slow');
240
}
241
 
242
function cacherPopupsDefinitions() {
243
	$(".definition_container").remove();
244
}
245
 
246
function formaterDefinition(element) {
247
	mot = element.attr('rel');
248
	data = dictionnaire[mot];
249
	defHtml = '<div class="definition_container">'+
10 raphael 250
		'<span class="definition_container_fleche"></span>'+
251
		'<span class="definition">'+data+'</span>'+
252
		'</div>';
1 aurelien 253
	return defHtml;
254
}
255
 
256
function supprimerToutesDefinitions() {
257
	$('.definition_term').each(function() {
258
		$(this).replaceWith($(this).html());
259
	});
260
	cacherPopupsDefinitions();
261
}
262
 
263
$(document).bind('mousemove', function(e){
264
	mouseX = e.pageX;
265
    mouseY = e.pageY - $(window).scrollTop();
266
});
267
 
14 aurelien 268
$(document).ajaxStop(function() {
269
	$(set).remplacerDefinitions(motsAyantDefinition);
270
});
271
 
1 aurelien 272
$(document).ready(function() {
10 raphael 273
	$.getScript(URL_BASE_SERVICE + "../../../scripts/helpers.js");
5 aurelien 274
	getMotsADefinitions();
10 raphael 275
	ajouterListenerDefinitions();
3 aurelien 276
	afficherLienDefinitions();
1 aurelien 277
});