/*
 * Fonction addslashes(texte)
 * Réplique de la fonction php à l'aide d'expressions régulières.
 */

function addslashes(str) {

	return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');

}

/*
 * Fonction lookup(inputString,input_id)
 * Cette fonction est appelée lors d'un relâchement d'une touche. Sur le keyup du champ input texte sur lequel porte l'autocomplétion
 * Résultat de la fonction :
 * La fonction effectue une reqûete ajax qui retourne la liste des valeurs possibles en fonction de ce qui est entré dans le champ input text
 * Arguments en entrée :
 * - inputString : le contenu de l'input texte sur lequel porte l'autocomplétion
 * - input_id : l'id de l'input texte sur lequel porte l'autocomplétion
 * - php : le chemin du fichier php que l'on va appeler et qui va nous retourner les réponses. Ex. : 'ajax/php'
 * - multiple : mode d'autocomplétion. true ou false;
 */

function lookup(inputString,input_id,php,multiple) {
	
	// Si le champ caché (pour les flèches) n'est pas vide, on ne fait rien : c'est qu'on a appuyé sur une flèche ou tab
	var valeur_cache=document.getElementById(input_id+'_'+'cache').value;
	if (valeur_cache=='') {
		if (multiple==true) {
		// On décortique ici l'inputString pour ne garder que le dernier mot
			var espace=strrpos(inputString,' ');
			if (espace===false) { espace=-1; }
			var guillemet=strrpos(inputString,'"');
			if (guillemet===false) { guillemet=-1; }
			var plus=strrpos(inputString,'+');
			if (plus===false) { plus=-1; }
			var position=Math.max(espace,guillemet,plus);
			if (position==-1) {
				var nouveau_mot=inputString;
			} else {
				var nouveau_mot=inputString.substring(position+1);
			}
			// Pour la gestion des espaces
			var passage=1;
			 if ((inputString.substring(inputString.length-1)==' ')||(inputString.substring(inputString.length-1)=='+')||(inputString.substring(inputString.length-1)=='"')) {
				passage=0;
			 }
			
		// Si multiple est faux, on garde l'inputString tel quel, on n'a qu'une unique autocomplétion
		} else {
			var nouveau_mot=inputString;
			var passage=1;
		}
		// Si le champ texte est vide ou si le dernier caractère st un espace, +, ou " 
		if ((inputString.length == 0)||(passage==0)) { 
				$('#'+input_id+'_'+'suggestions').hide(); // on cache les suggestions
		// Sinon on procède à la requête ajax en post et on traite la réponse
		} else { 
			$.post(php, {queryString: ""+nouveau_mot+"",id_input: ""+input_id+"",Auto_multiple: ""+multiple+""}, function(data){ 
				// Si il y a un retour, on traite la réponse
				if(data.length>0) {
					// a - On éclate les résultats dans un tableau tableau_data (le séparateur est *,* , fixé dans le fichier php)
						var tableau_data=data.split('*,*');
					// b - On supprime tout le contenu de la liste ul input_id_autoSuggestionsList
						var liste_ul=document.getElementById(input_id+'_'+'autoSuggestionsList');
						liste_ul.innerHTML='';
					// c - Pour chaque élément du tableau, on crée un élément dom <li>textNode</li> avec fill() pour onclick, que l'on place dans la liste ul input_id_autoSuggestionsList		
						for (i=0;i<tableau_data.length;i++) {
							var li=document.createElement('li');
							var texte_li=document.createTextNode(tableau_data[i]);
							li.setAttribute('class','normal');
							li.setAttribute('onClick','fill(\''+addslashes(tableau_data[i])+'\',\''+input_id+'\','+multiple+')');
							li.appendChild(texte_li);
							if (document.all) { // Syntaxe particulière à IE
								li.setAttribute('className','normal');
								// On fixe la largeur du li à 100% pour IE
								li.style.setAttribute('cssText','width:100%');
								li.onclick = function() {
									fill(this.innerHTML,input_id,multiple);
								}
								// On met aussi des évènements onmouseover et onmouseout pour changer la classe puisque li:hover n'est pas reconnu par IE...
								li.onmouseover = function() {
									// On affecte la classe current_ie si on était déjà en current avec les flèches
									if (this.getAttribute('className')=='normal') {
										this.setAttribute('className','current');
									} else {
										this.setAttribute('className','current_ie');
									}
								}
								li.onmouseout = function() {
									// Si la classe était égale à current_ie, alors on remet à current, car on était sur une position avec les flèches
									if (this.getAttribute('className')=='current_ie') {
										this.setAttribute('className','current');
									} else {
										this.setAttribute('className','normal');
									}
								}
							}
							liste_ul.appendChild(li);
						}
					// d - On affiche la liste
					$('#'+input_id+'_'+'suggestions').show();
				// Si il n'y a pas de réponse, on cache simplement les suggestions
				} else {
					$('#'+input_id+'_'+'suggestions').hide(); 
				}
			});
		}
	}
}


/*
 * Fonction fill(thisValue,input_id, multiple)
 * Cette fonction est appelée lors d'un clic sur une valeur de la liste proposée pour l'autocomplétion
 * Résultat de la fonction :
 * Elle récupère la valeur, la place dans le champ caché, puis appelle la fonction autocomplete_fleche en simulant un appui sur entrée.
 * Elle remet la valeur du champ caché à '' après tout ça et redonne le focus au champ input
 * Arguments en entrée :
 * - thisValue : définie dans le php appelé en ajax, thisValue est la valeur de la liste sur laquelle on clique => type String
 * - input_id : id du champ input sur lequel on place l'autocomplétion
 * - multiple : mode d'autocomplétion. true ou false;
 */
 
function fill(thisValue,input_id,multiple) {
	
	// On supprime la valeur du champ caché car si l'on perd le focus alors qu'on avait choisit une valeur avec les flèches, il ne faut pas que ça la prenne en compte
	document.getElementById(input_id+'_'+'cache').value='';
	// On remplit alors le champ caché avec la bonne valeur si l'on vient ici en cliquant
	var liste=document.getElementById(input_id+'_'+'autoSuggestionsList').getElementsByTagName('li');
	for (i=0;i<liste.length;i++) {
		if  (liste[i].innerHTML==thisValue) {
			document.getElementById(input_id+'_'+'cache').value=i;
		}
	}
	// Et on simule un keypress sur Tab comme si on avait ça en ayant sélectionné la valeur sur laquelle on vient de cliquer
	autocomplete_fleche('',input_id,multiple,true);
}

/*
 * Fonction strrpos(haystack, needle,[offset])
 * Cette fonction est une réplique de la fonction bien connue php strrpos()
 * Résultat de la fonction : 
 * La fonction permet de trouver la dernière occurence d'une chaine dans une autre.
 * Ex. : strrpos('pomme et chocolat','o') renvoie 13
 * Arguments en entrée : 
 * - haystack : la chaine de texte dans laquelle on va rechercher la dernière occurence
 * - needle : la chaine de texte à rechercher dans haystack
 * - offset : ?
 */
	
	
function strrpos (haystack, needle, offset) {

    var i = -1;
    if (offset) {
        i = (haystack+'').slice(offset).lastIndexOf(needle); 
		if (i !== -1) {
            i += offset;
        }
    }    else {
        i = (haystack+'').lastIndexOf(needle);
    }
    return i >= 0 ? i : false;
}

/*
 * Fonction autocomplete_fleche(e,input_id,clique)
 * Cette fonction est exécutée lors d'un onkeypress sur le champ input, donc lorsqu'on appui sur une touche
 * Cette fonction est aussi exécutée lors d'un clic sur une valeur de la liste déroulante, pour donnder le même effet qu'un appui sur Entrée
 * Résultat de la fonction :
 * La fonction va décider des évènements à venir lorsqu'on appui sur une touche : Flèches haut, bas, touche Entrée, et toutes les autres touches.
 * Elle permet notamment de se déplacer dans la liste des réponses à l'aide du clavier et d'ajouter une valeur à l'input (et non remplacer) à l'aide de la touche Entrée
 * Comme dans toute cette autocomplétion, il est possible d'ajouter plusieurs valeurs, séparées par des espaces, " ou + (pour les choix logiques)
 * L'autocomplétion recommencera pour chaque mot
 * Arguments en entrée :
 * -  e : la touche (codée) sur laquelle on appui. Elle sera décodée par e.keyCode
 * - input_id : l'id de l'input texte correspondant au champ à autocompléter.
 * - multiple : mode d'autocomplétion. true ou false;
 * - clique : valeur non renseignée lors de l'appui sur une touche, mais égale à 'oui' si la fonction est exécutée à partir d'un clic.
 */

function autocomplete_fleche(e,input_id,multiple,clique) {
		if (e.keyCode==38) { // Touche haut
			// 1 - On cherche la taille de la liste
			var liste=document.getElementById(input_id+'_'+'autoSuggestionsList').getElementsByTagName('li');
			var taille_liste=liste.length;
			// 2 - On récupère la valeur du champ caché correspondant à la position d'un li dans la liste (début à 0) (si ""), on est dans l'input text
			var champ_cache=document.getElementById(input_id+'_'+'cache');
			var valeur_cache=champ_cache.value;
			// 3 - On effectue les tests
			if (valeur_cache=='')  { // On est dans le champ input texte non caché : on ne fait rien
				
			} else if (valeur_cache==0) {  // On est à la première valeur : on boucle
				// a - On remplace la valeur dans le champ cache
				champ_cache.value=taille_liste*1-1;
				// b - On modifie la classe du li sur lequel on se trouvait
				liste[0].className='normal';
				// c - On modifie la classe du li sur lequel on se trouve
				liste[taille_liste*1-1].className='current';
			} else { // On est au milieu
				// a - On remplace la valeur dans le champ cache
				champ_cache.value=valeur_cache*1-1;
				// b - On modifie la classe du li sur lequel on se trouvait
				liste[champ_cache.value*1+1].className='normal';
				// c - On modifie la classe du li sur lequel on se trouve
				liste[champ_cache.value].className='current';
			}
			return false;
		} else if (e.keyCode==40) {  // Touche bas
			// 1 - On cherche la taille de la liste
			var liste=document.getElementById(input_id+'_'+'autoSuggestionsList').getElementsByTagName('li');
			var taille_liste=liste.length;
			// 2 - On récupère la valeur du champ caché correspondant à la position d'un li dans la liste (début à 0) (si ""), on est dans l'input text
			var champ_cache=document.getElementById(input_id+'_'+'cache');
			var valeur_cache=champ_cache.value;
			// 3 - On effectue les tests
			if (valeur_cache=='') { // On est dans le champ input texte non caché
				// a - On remplace la valeur dans le champ cache
				champ_cache.value=0;
				// b - On modifie la classe du li sur lequel on se trouve
				liste[0].className='current';
			} else if (valeur_cache==(taille_liste*1-1)) { // On est à la dernière valeur : on boucle
				// a - On remplace la valeur dans le champ cache
				champ_cache.value=0;
				// b - On modifie la classe du li sur lequel on se trouvait
				liste[taille_liste*1-1].className='normal';
				// c - On modifie la classe du li sur lequel on se trouve
				liste[0].className='current';
			} else { // On est au milieu
				// a - On remplace la valeur dans le champ cache
				champ_cache.value=valeur_cache*1+1;;
				// b - On modifie la classe du li sur lequel on se trouvait
				liste[champ_cache.value*1-1].className='normal';
				// c - On modifie la classe du li sur lequel on se trouve
				liste[champ_cache.value].className='current';				
			}
		} else if ((e.keyCode==13)||(clique==true)) { // Touche Entrée
			// 1 - On récupère la valeur du champ caché correspondant à la position d'un li dans la liste (début à 0) (si ""), on est dans l'input text
			var champ_cache=document.getElementById(input_id+'_'+'cache');
			var valeur_cache=champ_cache.value;
			// 2 - Si cette valeur est non nulle, on remplace l'input text par
			// la valeur du choix sélectionné avec les flèches + ce qu'il y avait avant
			if (valeur_cache!='') {
				// a - On récupère la valeur du texte précédent
				var texte_ancien=document.getElementById(input_id).value;
				// b - On récupère la liste de choix
				var liste=document.getElementById(input_id+'_'+'autoSuggestionsList').getElementsByTagName('li');
				// c - On regarde si on est en mode multiple true ou false
				if (multiple==true) {
					// i - On cherche le dernier séparateur de texte (après espace, " ou + )
					var espace=strrpos(texte_ancien,' ');
					if (espace===false) { espace=-1; }
					var guillemet=strrpos(document.getElementById(input_id).value,'"');
					if (guillemet===false) { guillemet=-1; }
					var plus=strrpos(document.getElementById(input_id).value,'+');
					if (plus===false) { plus=-1; }
					var position=Math.max(espace,guillemet,plus);
					// ii - On fabrique la nouvelle chaine texte
						// A - On stocke la chaine à ajouter
						var chaine_a_ajouter=liste[valeur_cache].innerHTML
						// B - On regarde si il y a un (ou des) espaces dedans. Si oui, on ajoute des guillemets autour (ou juste un après, s'il y en avait déjà un avant)
						if (strrpos(chaine_a_ajouter,' ')) {
							// Si pas de guillemet avant, on en ajoute 1 de chaque côté
							if (texte_ancien.substring(position,position+1)!='"') {
								chaine_a_ajouter='"'+chaine_a_ajouter+'"';
							// Si guillemet avant, on en ajoute seulement 1 après
							} else {
								chaine_a_ajouter=chaine_a_ajouter+'"';
							}
						}
						// C - On colle l'ancien texte avec le bout de chaine à ajouter
						if (position==-1) {
							var nouveau_texte=chaine_a_ajouter;
						} else {
							var nouveau_texte=texte_ancien.substring(0,position+1)+chaine_a_ajouter;
						}
				// Si on est en mode multiple false, donc en autocomplétion normale sur un seul mot, l'ancien texte est simplement remplacé par le nouveau
				} else {
					var nouveau_texte=liste[valeur_cache].innerHTML;
				}
				// e - On remplace la valeur dans l'input texte
				document.getElementById(input_id).value=nouveau_texte;
				// f - On cache la liste de suggestions
				$('#'+input_id+'_'+'suggestions').hide();
				// g - On remet la valeur du champ caché à ''
				document.getElementById(input_id+'_'+'cache').value='';
				// g - On redonne le focus au champ de recherche
				document.getElementById(input_id).focus();
				
			}
		} else { // Toutes les autres touches
			// a - On remet la valeur du champ caché à ''
			document.getElementById(input_id+'_'+'cache').value='';
		}
		return false;
}


/*
 * Fonction autocompletion()
 * Cette fonction est celle qui sera lancée après le chargement de la page.
 * C'est elle qui va céer la div d'autosuggestion et la liste ul
 * C'est elle qui va définir quelles fonctions lancer sur quels évènements
 * Arguments en entrée :
 * - input_id : l'id du champ input text sur lequel porte l'autocomplétion
 * - chemin_php_ajax : le chemin du php qui sera appelé lors de la requête ajax (le php qui nous renvoie les valeurs possibles dans une liste)
 * - multiple : détermine si l'autocomplétion recommence à chaque espace, " ou + (true) ou ne se fait qu'une fois seulement pour l'ensemble du champ (false);
 */

function autocompletion(input_id,chemin_php_ajax,multiple) {

	// Pour le jquery
	var input_id_jquery='input#'+input_id;
	
	// Récupération de la position de l'input qui va nous permettre de placer correctement notre div de résultats
	
		// On récupère la position relative du bas de l'élément input ( par rapport à l'élément parent ) 
		var haut=$(input_id_jquery).position().top;
		var hauteur=$(input_id_jquery).outerHeight();
		var bas=haut*1+hauteur*1;
		// On récupère la position relative gauche de l'élément input ( par rapport à l'élément parent ) 
		var gauche=$(input_id_jquery).position().left;
		if (document.all) { //IE
			// On récupère la largeur de l'élément input 
			var largeur=$(input_id_jquery).outerWidth();
		} else { // Les autres navigateurs
			// On récupère la largeur de l'élément input 
			var largeur=$(input_id_jquery).width();
			// On réajuste pour les bordures
			largeur=largeur+2;
		}
	// On crée maintenant les éléments du dom qui vont nous servir pour cette page
	// a - L'input cache qui va se trouver sous l'input normal (pour les flèches)
		// i - On crée l'objet input
		var champ_cache=document.createElement('input');
		// ii - On définit son identifiant id, son type et sa valeur initiale
		champ_cache.setAttribute('id',input_id+'_cache');
		champ_cache.setAttribute('type','hidden');
		champ_cache.setAttribute('value','');
		// iii - On le ratache au dom ( dans le formulaire où se situe l'input )
		var formulaire=document.getElementById(input_id).parentNode;
		formulaire.appendChild(champ_cache);
	// b - La div contenant les suggestions (que l'on va placer en absolute (par rapport à l'élément parent, donc le formulaire, bizarre...) grâce aux coordonnées calculées au-dessus
		var div_suggestions=document.createElement('div');
		div_suggestions.setAttribute('id',input_id+'_suggestions');
		div_suggestions.setAttribute('class','suggestionsBox');
		div_suggestions.setAttribute('style','display:none;position:absolute;top:'+bas+'px;left:'+gauche+'px;width:'+largeur+'px;');
		if (document.all) { // Syntaxe spécifique à IE
			div_suggestions.setAttribute('className','suggestionsBox');
			div_suggestions.style.setAttribute('cssText','display:none;position:absolute;top:'+bas+'px;left:'+gauche+'px;width:'+largeur+'px;');
		}
		formulaire.appendChild(div_suggestions);
	// c - On crée la liste ul à l'intérieur de la div
		var liste_suggestions=document.createElement('ul');
		liste_suggestions.setAttribute('id',input_id+'_autoSuggestionsList');
		liste_suggestions.setAttribute('class','suggestionList');
		if (document.all) { // Syntaxe spécifique à IE			
			liste_suggestions.setAttribute('className','suggestionList');
		}
		div_suggestions.appendChild(liste_suggestions);

	
	// On passe aux évènements qui vont faire fonctionner l'autocomplétion sur l'input
	$(input_id_jquery).keydown(function(e) {
		autocomplete_fleche(e,this.id,multiple,false);
	});
	
	$(input_id_jquery).keyup( function() { // si on presse une touche du clavier en étant dans le champ texte qui a pour id inputString
		lookup($(this).val(),this.id,chemin_php_ajax,multiple);
	});
}
