

/**
 * Module ev.completion :
 * définition des fonctions générales utilisées par toutes
 * les versions de completion (MEV, MEH...)
 */
(function() {
	// raccourci vers window
	var WIN = this,
			// Raccourci vers document
			DOC = WIN.document,
			// synonymes de true et false
			FALSE = !WIN, TRUE = !FALSE,
			// Raccourci vers éléments utiles
			genericNavigator = WIN.genericNavigator,
			MSIE = WIN.MSIE,
			MSIE6_LINE_WIDTH = WIN.MSIE6_LINE_WIDTH,
			MSIE6_LINE_HEIGHT = WIN.MSIE6_LINE_HEIGHT,
			// expressions regulières
			ST = /^sai(|n|nt(e?))/,
			SAINT = /^st/,
			SAINTE = /^ste/,
			REP = /rep\.?/,
			REG_SAINT = new RegExp('(sai(|n|nt(e?)))', 'gi'),
			REG_ST = new RegExp('(st )', 'gi'),
			REG_STE = new RegExp('(ste )', 'gi');

	/**
	 * Le formatage consiste à:
	 * - convertir en low case
	 * - supprimer toute l'accentuation
	 * ATTENTION: Ce formatage est normalisé, IL NE DOIT PAS ETRE MODIFIE
	 **/
	function replaceAccents(text) {
		text = text.toLowerCase();
		text = text.replace(/à/g, 'a');
		text = text.replace(/â/g, 'a');
		text = text.replace(/ä/g, 'a');
		text = text.replace(/å/g, 'a');
		text = text.replace(/ã/g, 'a');
		text = text.replace(/á/g, 'a');
		text = text.replace(/é/g, 'e');
		text = text.replace(/ê/g, 'e');
		text = text.replace(/è/g, 'e');
		text = text.replace(/ë/g, 'e');
		text = text.replace(/ï/g, 'i');
		text = text.replace(/î/g, 'i');
		text = text.replace(/í/g, 'i');
		text = text.replace(/ì/g, 'i');
		text = text.replace(/ö/g, 'o');
		text = text.replace(/ô/g, 'o');
		text = text.replace(/ò/g, 'o');
		text = text.replace(/ø/g, 'o');
		text = text.replace(/ó/g, 'o');
		text = text.replace(/ù/g, 'u');
		text = text.replace(/û/g, 'u');
		text = text.replace(/ü/g, 'u');
		text = text.replace(/ç/g, 'c');
		text = text.replace(/ñ/g, 'n');
		return text;
	}

	/**
	 * Le formatage consiste à:
	 * - convertir en low case
	 * - supprimer toute l'accentuation et typographie particulière
	 * - remplacer tous les signes autres que a-z 0-9 et le signe espace en espaces
	 * - compacter les séquences d'espaces en un seul
	 * - trimer
	 * ATTENTION: Ce formatage est normalisé, IL NE DOIT PAS ETRE MODIFIE
	 **/
	function formateText(text) {
		text = replaceAccents(text);
		text = text.replace(/[^a-z0-9 ]/g, ' ');
		text = text.replace(/\s\s+/g, ' ');
		text = text.replace(/^\s+/, '');
		//  text=text.replace(/\s+$/, "");
		return text;
	}

	/**
	 * Cette fonction utilisée par les matching, teste si le texte correspond à l'expression régulière. Cette opération utilise pour text les règles de
	 * formatage définies par la fonction formateText.
	 * - text: le texte à matcher
	 * - regExp: l'expression régulière à tester
	 * Retourne un booléen.
	 **/
	function textMatch(text, regExp) {
		text = formateText(text);
		if (ST.test(text)) {
			if (text.replace(ST, 'st' + RegExp.$2).match(regExp)) {return TRUE;}
		}
		if (SAINT.test(text)) {
			if (text.replace(SAINT, 'saint' + RegExp.$2).match(regExp)) {return TRUE;}
		}
		if (SAINTE.test(text)) {
			if (text.replace(SAINT, 'sainte' + RegExp.$2).match(regExp)) {return TRUE;}
		}
		if (REP.test(text)) {
			if (text.replace(REP, 'republique').match(regExp)) {return TRUE;}
			if (text.replace(REP, 'republic').match(regExp)) {return TRUE;}
			if (text.replace(REP, 'republica').match(regExp)) {return TRUE;}
			if (text.replace(REP, 'republik').match(regExp)) {return TRUE;}
		}
		return text.match(regExp);
	}

	/**
	 * Cette fonction permet l'extraction du prefixe (3 premiers caractères) d'une chaine après formatage.
	 */
	function getPrefix(text) {
		text = formateText(text);
		if (text.length < 3) {return null;}
		text = text.substring(0, 3);
		return text;
	}

	/**
	 * Selon la touche ayant généré l'evenement 'event' donné,
	 * on retourne vrai ou faux pour dire s'il est nécessaire de
	 * mettre à jour une liste de propositions.
	 */
	function shouldUpdatePropositions(event) {
		// Esquive de quelques touches...
		switch (event.keyCode) {
			case 9: // TAB
			case 13:// ENTER
			case 16:// SHIFT
			case 17:// CONTROL
			case 18:// ALT
			case 27:// ECHAP
			//case 32:// ESPACE
			case 37:// GAUCHE
			case 39:// DROITE
			case 38:// HAUT
			case 40:// BAS
				return FALSE;
			default:
				// Pour toute autre touche on met à jour la liste de propositions
				return TRUE;
		}
	}

	/**
	 * Cette méthode permet de centraliser le fonctionnement des touches du clavier sur les completions ME.
	 * Elle gère l'évènement 'onkeyup' des champs INPUT TEXT sur lesquels la completion est activée.
	 * La valeur de retour permet de préciser si l'on doit laisser remonter l'évenement au niveau du navigateur (oui : true ; non : false)
	 * Pour IE le système est différent et on doit modifier l'évènement lorsque celui-ci doit être bloqué (car il ne gère pas et n'accepte pas la valeur de retour)
	 */
	function completionToucheDown(completion, event) {
		var blockEvent = FALSE;

		if (completion.status === 'off') {
			// Si la complétion est inactive on remonte l'événement au navigateur sauf si la touche du bas est enfoncee.
			if (event.keyCode === 40) {
				completion.updatePropositions(event);
				blockEvent = TRUE;
			}
		}
		else switch (event.keyCode) {
			case 38:// HAUT
				completion.decreaseSelectedProposition();
				completion.updatePage();
				blockEvent = TRUE;
				break;
			case 40:// BAS
				completion.increaseSelectedProposition();
				completion.updatePage();
				blockEvent = TRUE;
				break;

			case 13:// validation par ENTER
			case 9:// validation par TAB
				if (completion.lineSelected >= 0 && completion.lineSelected < completion.lines.length) {
					// En fait, hors bug, la seule valeur possible hors ces bornes est -1 qui est la valeur par défaut qui correspond au fait que l'utilisateur
					// n'a pas touché les flêches du clavier
					completion.validLine(completion.lineSelected);
					// on ne laisse remonter l'événement au navigateur que si la ligne est validée avec TAB
					blockEvent = event.keyCode === 13;
				}
				else {
					// Dans l'autre cas, aucune sélection n'a été faite dans la ligne, et on ne doit tenir compte que de ce qui a été tapé dans le champ texte
					completion.hidePropositions();
					// On ne peut pas vider le champ (comme ligne en commentaire) dans ce cas là (on risque de supprimer une donnée insérée par la completion lors d'un choix précédent).
					// A la place, cela est effectué lors de l'insertion d'un caractère ('updatePropositions()').
					//completion.elementData.value="";
					// Et on remonte biensûr l'événement (comme si la completion n'existait pas)
				}
				break;
			case 27:// ECHAP : masquage des propositions
				completion.hidePropositions();
				blockEvent = TRUE;
				break;
			default:
				break;
		}

		// Toute autre touche : on ne fait rien on transmet l'événement au navigateur et on attends l'événement 'onkeyup' pour mettre à jour les propositions [cf. completion.toucheUp()]

		// Traitement spécifique pour le blocage de l'évènement sur IE
		if (blockEvent && genericNavigator.navigator.id === MSIE) {
			event.cancelBubble = TRUE;
			event.returnValue = FALSE;
		}

		return !blockEvent;
	}

	/**
	 * Permet d'afficher l'element de propositions s'il existe (quelque soit le navigateur)
	 */
	function showPropositionsElement(element, nb_lines) {
		if (!element) {return;}
		element.style.visibility = 'visible';
		if (genericNavigator.navigator.id === MSIE && genericNavigator.navigator.version <= 6.9) {
			var iframe = DOC.createElement('IFRAME');
			iframe.style.display = 'none';
			iframe.style.display = 'block';
			iframe.style.position = 'absolute';
			iframe.style.top = '0';
			iframe.style.left = '0';
			iframe.style.zIndex = '-1';
			iframe.style.filter = 'mask()';
			iframe.style.width = (MSIE6_LINE_WIDTH + 10) + 'px';
			iframe.style.height = (MSIE6_LINE_HEIGHT * nb_lines + 10) + 'px';
			iframe.style.background = 'none';
			iframe.frameBorder = 0;
			iframe.scrolling = 'no';
			element.appendChild(iframe);
		}
	}

	/**
	 * Permet de masquer l'element de propositions s'il existe (quelque soit le navigateur)
	 */
	function hidePropositionsElement(element) {
		if (!element) {return;}
		element.style.visibility = 'hidden';
	}

	/**
	 * Cette méthode permet l'ajout du synonyme dans la proposition si la difference entre les sommes des codes de caractères du synomyme et du nom est supérieure à 75%.
	 * L'ajout n'est effectué que si la chaine sans accent ne contient pas la saisie de l'utilisateur et les differentes expressions régulières pour les "saint(e)".
	 * @param {!Object} proposition proposition à affiché.
	 * @param {!string} chaine chaine de caractères affiché.
	 * @param {!string} chaineSansAccent chaine de caractères affiché sans les accents.
	 * @param {!RegExp} regExp expression régulière qui commence par la chaine de caractères rentré par l'utilisateur.
	 * @param {!RegExp} reg expression régulière, insensible à la case, de la chaine de caractères rentré par l'utilisateur.
	 **/
	function ajoutSynonyme(proposition, chaine, chaineSansAccent, regExp, reg) {
		if (!chaineSansAccent.match(reg) && !chaineSansAccent.match(REG_SAINT) && !chaineSansAccent.match(REG_ST) && !chaineSansAccent.match(REG_STE)) {
			var syn = proposition.retournMatch(regExp), f, entierNom, entierSyn, entier;
			if (syn) {
				entierNom = 0;
				for (f = 0; f < proposition.nom.length; f++) {
					entierNom = entierNom + proposition.nom.charCodeAt(f);
				}
				entierSyn = 0;
				for (f = 0; f < syn.length; f++) {
					entierSyn = entierSyn + syn.charCodeAt(f);
				}
				entier = 0;
				if (entierNom < entierSyn) {
					entier = entierNom / entierSyn;
				}
				if (entierSyn < entierNom) {
					entier = entierSyn / entierNom;
				}
				if (entier < 0.75) {
					chaine = proposition.nom + ' [' + syn + '], ' + proposition.pays;
				}
			}
		}
		return chaine;
	}

	/**
	 * Modification du DOM contenant la proposition pour mettre en évidance le corespondance avec le texte saisie.
	 * @param {!string} chaine chaine de caractères affiché.
	 * @param {!string} chaineSansAccent chaine de caractères affiché sans les accents.
	 * @param {!string} elementTextValue chaine de caractères saisie par l'utilisateur.
	 * @param {!Element} propositionDiv div contenant la proposition affiché.
	 * @param {!RegExp} reg expression régulière, insensible à la case, de la chaine de caractères rentré par l'utilisateur.
	 **/
	function modifDomWhithSearch(chaine, chaineSansAccent, elementTextValue, propositionDiv, reg) {
		// Suppression des tirets dans la chaine sans les accents et du texte saisie pour simplifier la correspondance.
		elementTextValue = elementTextValue.replace(/-/g, ' ');
		chaineSansAccent = chaineSansAccent.replace(/-/g, ' ');

		// Suppression des espaces multiples dans le texte saisie.
		elementTextValue = elementTextValue.replace(/\s\s+/g, ' ');

		var positionChar = [], f, quantiteDiese;
		// Initilisation du tableau qui permet de savoir si un caractère doit être mis en évidence.
		for (f = 0; f < chaineSansAccent.length; f++) {
			positionChar[f] = FALSE;
		}
		// Recherche des caractères qui doivent être mis en évidence par rapport au texte saisie par l'utilisateur.
		if (chaineSansAccent.match(reg)) {
			quantiteDiese = 0;
			chaineSansAccent = chaineSansAccent.replace(reg, '#$1');

			for (f = 0; f < chaineSansAccent.length; f++) {
				if (chaineSansAccent.charAt(f) === '#') {
					positionChar[f - quantiteDiese] = TRUE;
					quantiteDiese++;
				}
			}
		}
		// Recherche des caractères qui doivent être mis en évidence par rapport au texte saisie par l'utilisateur si il est de type "saint(e)".
		if (elementTextValue.match(REG_SAINT) || elementTextValue.match(REG_ST) || elementTextValue.match(REG_STE)) {
			if (chaine.match(REG_ST)) {
				quantiteDiese = 0;
				chaineSansAccent = chaine.replace(REG_ST, '#$1');
				for (f = 0; f < chaineSansAccent.length; f++) {
					if (chaineSansAccent.charAt(f) === '#') {
						positionChar[f - quantiteDiese] = TRUE;
						quantiteDiese++;
					}
				}
			}
			if (chaine.match(REG_STE)) {
				quantiteDiese = 0;
				chaineSansAccent = chaine.replace(REG_STE, '#$1');
				for (f = 0; f < chaineSansAccent.length; f++) {
					if (chaineSansAccent.charAt(f) === '#') {
						positionChar[f - quantiteDiese] = TRUE;
						quantiteDiese++;
					}
				}
			}
		}
		var div = DOC.createElement('div');
		div.appendChild(DOC.createTextNode(''));
		// Modification du DOM en fonction des caractères à mettre en évidence.
		var txt = '';
		for (f = 0; f < chaine.length; f++) {
			if (positionChar[f]) {
				if (f) {
					propositionDiv.appendChild(DOC.createTextNode(txt));
				}
				// Modification de la longuer du texte à mettre en evidence pour les recherche de type "saint(e)".
				var nbCaracSaisie = elementTextValue.replace(/^\s+/g, '').length;
				txt = chaine.substr(f, nbCaracSaisie);
				if (elementTextValue.match(REG_SAINT) && txt.match(REG_ST)) {
					if (nbCaracSaisie === 4) {
						nbCaracSaisie = nbCaracSaisie - 2;
					}
					if (nbCaracSaisie > 4) {
						nbCaracSaisie = nbCaracSaisie - 3;
					}
				}
				if (elementTextValue.match(REG_SAINT) && txt.match(REG_STE)) {
					if (nbCaracSaisie === 4) {
						nbCaracSaisie = nbCaracSaisie - 1;
					}
					if (nbCaracSaisie > 4 && nbCaracSaisie < 7) {
						nbCaracSaisie = nbCaracSaisie - 2;
					}
					if (nbCaracSaisie > 6) {
						nbCaracSaisie = nbCaracSaisie - 3;
					}
				}
				// Création d'un node pour la mise en évidence dans les propositions.
				txt = chaine.substr(f, nbCaracSaisie);
				var strong = DOC.createElement('strong');
				strong.appendChild(DOC.createTextNode(txt));
				propositionDiv.appendChild(strong);
				f = f + nbCaracSaisie - 1;
				txt = '';
			}else {
				txt += chaine.charAt(f);
			}
		}
		propositionDiv.appendChild(DOC.createTextNode(txt));
	}

	// compatibilité // FIXME à virer et faire un objet propre ev.completion={...} à la place ^^
	WIN.replaceAccents = replaceAccents;
	WIN.formateText = formateText;
	WIN.textMatch = textMatch;
	WIN.getPrefix = getPrefix;
	WIN.shouldUpdatePropositions = shouldUpdatePropositions;
	WIN.completionToucheDown = completionToucheDown;
	WIN.showPropositionsElement = showPropositionsElement;
	WIN.hidePropositionsElement = hidePropositionsElement;
	WIN.ajoutSynonyme = ajoutSynonyme;
	WIN.modifDomWhithSearch = modifDomWhithSearch;
}());

