/* JavaScript Document
*
* Gestion des erreurs de saisie
*
* Stéphane AIMAR
* @afij.org
* 
* Pour être utiliser il faut :
*   - /scripts/fonction.js (fonctions basiques)
*   - /css/gestion_erreurs.css (CSS pour les tooltips d'erreur et GE_erreurSaisie et GE_normalSaisie)
*   - un CSS définissant "GE_normalSaisie" et "GE_erreurSaisie"
*
* Syntaxe :
*	<input type="text" name="INPUT" id="INPUT">
*
*
*
* Paramètres de loadVariables :
*
*	ID						: id (au sens HTML) de l'erreur
*	VALUE					: valeur a respecter (vide ou expression régulière ou DATE, HEURE, MAIL, INT, FLOAT)
*	ERROR_TEXT				: message d'erreur
*	LISTE_CARS				: liste de caractères (expression régulière) valide ou invalide si ! présent en début d'expression régulière 
*	DISABLE_AUTO_VERIF		: desactive la vérif auto en passant par GE.checkAllErrors
*	X_POS					: décalage en X du label

 Pour Opera, sur la vérification des touches clavier il faut ajouter la récupération de l'évènement onKeyDown car il renvoie les keycode des fleches sur l'évènement onKeyPress

*/

	var _GE_aErrorArray = new Array();
	var _GE_className = "GE_normalSaisie";
	var _GE_errorClassName = "GE_erreurSaisie";
	var _GE_ToolTipClassName = "GE_cssToolTipErrorDisplay";
	
	var _GE_sAncre_name = "_ANCH_ERREUR";
	var _GE_sAncre = ""; 		// nom de l'ancre a laquelle faire scroller la page
	var _GE_TimeOutFade;	// Variable d'un fade pour pouvoir l'annuler
	var _keyCode = null;

	// Gestion Erreurs
	var GE = {
		loadVariables: function (aListe) {
			i=0;
			for (iID in aListe) {
				sID = aListe[iID][0];
				if (document.getElementById(sID)) {
					_GE_aErrorArray[sID] = {
						ID: sID,
						VALUE: aListe[iID][1],
						ERROR_TEXT: aListe[iID][2],
						LISTE_CARS: aListe[iID][3],
						DISABLE_AUTO_VERIF: aListe[iID][4],
						X_POS: aListe[iID][5],
						FORMAT: aListe[iID][1]};
					
					// Récupération du champ
					fieldElement = document.getElementById(sID);
					fieldElement.className = _GE_className;	// Css mis en place
					
					//Si il y a un texte d'erreur c'est qu'il y a gestion d'erreur si non, on n'ajoute rien
					if (aListe[iID][2])  {

						// Analyse du champs VALUE qui peut comporter une expression régulière du format de réponse souhaité
						switch (aListe[iID][1]) {
							case "DATE":
									_GE_aErrorArray[sID].VALUE = "[0-9]{2}/[0-9]{2}/[0-9]{4}";
									_GE_aErrorArray[sID].LISTE_CARS = "([0-9/])";
								break;
							case "HEURE":
									_GE_aErrorArray[sID].VALUE = "[0-9]{1,2}[Hh]{1}[0-9]{0,2}";
									_GE_aErrorArray[sID].LISTE_CARS = "([0-9hH])";
								break;
							case "MAIL":
									_GE_aErrorArray[sID].VALUE = "^[\_]*([a-zA-Z0-9]+(\.|\_*)?)+@([a-zA-Z0-9][a-zA-Z0-9\-]*(\.|\-*\.))+[a-zA-Z]{2,6}$";
									_GE_aErrorArray[sID].LISTE_CARS = "([a-z0-9\.|\_@-])";
								break;
							case "INT":
									_GE_aErrorArray[sID].VALUE = "^\\d+$";
									_GE_aErrorArray[sID].LISTE_CARS = "([0-9])";
								break;
							case "FLOAT":
									_GE_aErrorArray[sID].VALUE = "^\\d*\.{0,1}\\d+$";
									_GE_aErrorArray[sID].LISTE_CARS = "([0-9.])";
								break;
/*
							case "URL":
									_GE_aErrorArray[iID].VALUE = "";
									_GE_aErrorArray[iID].LISTE_CARS = "";
*/
						}						
	
						// Création du div d'erreur associé, il faut que l'élément soit dans un div ou li mais pas un spam ou un td (non reconnu par IE bug millénaire...)
						errorElement = document.createElement("div");
						errorElement.setAttribute("id", sID+"_error");
						errorElement.className = _GE_ToolTipClassName;
						// Ajout du div d'erreur après l'élément en se servent de l'élement parent du champ
						fieldElement.parentNode.appendChild(errorElement);

						// Mise en place des évènements
						_GE_setEvents(sID);
						
						if (_GE_aErrorArray[sID].LISTE_CARS) {
							var Expression = new RegExp("^!(.*)");
							
							// pour Opera qui ne reconnais pas bien la différence entre les touches fléchées et &(... au keyPress, je suis obligé de récupérer l'évènement keydown
							document.getElementById(sID).onkeydown = _GE_keyDownEvent;					
							
							// Si l'expression régulière commence par un ! (not) c'est une liste de caractères invalides
							if (Expression.test(_GE_aErrorArray[sID].LISTE_CARS)) {
								_GE_aErrorArray[sID].LISTE_CARS = Expression.exec(_GE_aErrorArray[sID].LISTE_CARS)[1];
								fieldElement.onkeypress = _GE_invalidsCars;
							} else // Si non c'est une liste de caractères valides
								fieldElement.onkeypress = _GE_validsCars;
						}
					}
				}
			}
		}, 
		
		checkAllErrors: function (bSendError) {
			// Vérifie touts les champs, sauf ceux sans erreurs (TEXT) et/ou avec DISABLE_AUTO_VERIF a false
			var sErrors = "";
			_GE_sAncre = "";
			
			for (iID in _GE_aErrorArray) {
				if (_GE_aErrorArray[iID].ERROR_TEXT != null)
					if ((!_GE_aErrorArray[iID].DISABLE_AUTO_VERIF))
						sErrors += _GE_checkError(iID);
			}
			
			if (bSendError)
				return sErrors;
			else
				return (sErrors == "");
		},
		
		checkError: function (sId) {
			return _GE_checkError(sId);
		},
		
		raiseError: function (sId, bColorInput, sErreur) {
			return _GE_raiseError(sId, bColorInput, sErreur);
		},
		
		closeError: function (sId, bColorInput) {
			_GE_closeError(sId, bColorInput);
		},
		
		getAncre: function () {
			return _GE_sAncre + _GE_sAncre_name;
			_GE_sAncre = "";
		},
/*
		validsCars: function (iID, eEvent) {
			return _GE_validsCars(iID, eEvent);
		},
		
		invalidsCars: function (iID, eEvent) {
			return _GE_invalidsCars(iID, eEvent);
		},
*/
		keyDownEvent: function (event) {
			return _GE_keyDownEvent (event);
		},
		
		getID: function (iNum) {
			return _GE_aErrorArray[iNum].ID;
		},
		
		checkHeure: function (sId, bColorInput) {
			return _GE_checkHeure(sId, bColorInput);
		},
		
		checkDate: function (sId, bColorInput) {
			return _GE_checkDate(sId, bColorInput);
		},
		
		checkPeriode: function (sIdFrom, sIdTo) {
			return __GE_checkPeriode(sIdFrom, sIdTo);
		},
		
		setAutoVerif: function (sId, bAutoVerif) {
			_GE_setAutoVerif(sId, bAutoVerif);
		},
		
		scrollTo: function () {
			_GE_scrollTo();
		}
	};


	function _GE_autoShowError() {
		_GE_checkError (this.id);
	}

	function _GE_hideError() {
		if (this.id) {
			var objError = document.getElementById(this.id+"_error");
			objError.innerHTML="";
			objError.style.display="none";
		}
	}

	/**
	 * récupère le keycode au keydown, utilisé pour opera qui ne fait pas la différence entre fleches et &(' sur le keypress 
	 * @param {Object} event
	 */
	function _GE_keyDownEvent(event) {
		if (window.event)
		   _keyCode = window.event.keyCode;
		else if (event)
		   _keyCode = event.which;
		else
		   return null;
		   
	}

// Fonction de test de valeur de saisie faisant apparaitre une erreur
	/**
	 * Vérifie le contenu d'un input en fonction des l'expression regex déclarée ou du type DATE/HEURE
	 * @param {Object} iID
	 */
	function _GE_checkError(iID) {
		
//		var objID		= _GE_aErrorArray[iID].ID;
		var valError	= _GE_aErrorArray[iID].VALUE;
		var sErreur		= _GE_aErrorArray[iID].ERROR_TEXT;
	 	var obj = document.getElementById(iID);
		var key;
		
		if (valError=="")
			Expression = new RegExp("(.+)");
		else
			Expression = new RegExp(valError);
		
		obj.value = trim(obj.value);
/*
		if (!Expression.test(obj.value)) {
			return _GE_raiseError(iID, true);
		} else {
			switch (_GE_aErrorArray[iID].FORMAT) {
				case "DATE":
						return _GE_checkDate(iID, true);
					break;
				case "HEURE":
						return _GE_checkHeure(iID, true);
					break;
				default :
					_GE_closeError(iID, true);
					return "";
			}
*/
/*			
			var objError = document.getElementById(objID+"_error");
			obj.className = "GE_normalSaisie";
			if (objError) {
				objError.style.display = "none";
				objError.innerHTML = "";
			}
*/
			// Renvoie de l'erreur sous format texte simple avec passage à la ligne
/*
		}
*/

		switch (_GE_aErrorArray[iID].FORMAT) {
			case "DATE":
					return _GE_checkDate(iID, true);
				break;
			case "HEURE":
					return _GE_checkHeure(iID, true);
				break;
			default :
				if (!Expression.test(obj.value)) {
					return _GE_raiseError(iID, true);
				} else {
					_GE_closeError(iID, true);
					return "";
				}
		}

	 };

	/**
	 * Affiche l'erreur associée a l'input d'ID sID
	 * @param {Object} sID
	 * @param {Object} bColorInput
	 */
	function _GE_raiseError(sID, bColorInput, sTextError) {
		var sErreur		= _GE_aErrorArray[sID].ERROR_TEXT;
		
		if (sTextError)
			sErreur = sTextError;

		if (_GE_sAncre == "")
				_GE_sAncre = sID;

		_GE_showError(sID, sErreur, bColorInput);

		return "- "+sErreur+"\n";
	}

	/**
	 * Vérifie si le caractère frappé est interdit
	 * @param {Event} eEvent Evènement
	 */
	function _GE_invalidsCars(eEvent) {
		var iID = this.id;
		var Expression = new RegExp(_GE_aErrorArray[iID].LISTE_CARS);
		var ObjID = _GE_aErrorArray[iID].ID;
		var bTextFormat = false;
		var key;

		if (eEvent!=null) {
			if (window.event)
				key = window.event.keyCode;
			else if (eEvent)
				key = eEvent.which;
			else
				return true;
			sValue = String.fromCharCode(key);
		} else {
			key=1;
			bTextFormat=true;
			sValue = document.getElementById(ObjID).value;
		}

	// Pour Opera qui renvoie un keycode équivalent aux fleches lors de l'appui sur &'( onbligé de voir le keycode de l'évènemen keydown
		if ((key >= 33) && (key <= 40) && (_keyCode != null) && (key == _keyCode))
				return true;
		
		if ((key!=0) && (key!=8) && (key!=9) && (key!=13) && (key!=27)) {
			
//			alert (key+"/"+keychar);
			if (Expression.test(sValue)){
				if (!bTextFormat)
					_GE_showError(iID, "Caract&egrave;re&nbsp;'<strong>"+String.fromCharCode(key)+"</strong>'&nbsp;interdit", true, true);
				else
					_GE_raiseError(iID);
				return false;
			} else
				_GE_closeError(iID, true);
		}
		return true;
	}

	/**
	 * Vérifie si le caractère frappé est autorisé
	 * @param {Event} eEvent Evènement
	 */
	function _GE_validsCars(eEvent) {
		var iID = this.id;
		var Expression = new RegExp(_GE_aErrorArray[iID].LISTE_CARS);
		var key;

		if (window.event)
		   key = window.event.keyCode;
		else if (eEvent)
		   key = eEvent.which;
		else
		   return true;
/*
		if ((_keyCode != null) && (_keyCode != key))
			key = _keyCode;
*/
	// Pour Opera qui renvoie un keycode équivalent aux fleches lors de l'appui sur &'( onbligé de voir le keycode de l'évènemen keydown
		if ((key >= 33) && (key <= 40) && (_keyCode != null) && (key == _keyCode))
				return true;

		if ((key!=0) && (key!=8) && (key!=9) && (key!=13) && (key!=27)) { // && (key!=37) && (key!=38) && (key!=39) && (key!=40)) {
			keychar = String.fromCharCode(key);
//	alert (_keyCode+"/"+key+"/"+keychar+"->"+Expression.test(keychar))
			if (!Expression.test(keychar)){
				_GE_showError(iID, "Caract&egrave;re&nbsp;'<strong>"+keychar+"</strong>'&nbsp;interdit", true, true);
				return false;
			} else
				_GE_closeError(iID, true);
		}
		return true;
	}

	/**
	 * Affiche l'erreur corespondante à sID
	 * @param {String} sID ID de l'erreur
	 * @param {String} sTextError Texte à afficher pour erreur
	 * @param {Boolean} bColorInput colorer l'input si il y a erreur true/false
	 * @param {Boolean} bCloseFade faire disparaitre l'erreur de façon progressive true/false
	 */
	function _GE_showError (sID, sTextError, bColorInput, bCloseFade) {
		var objErrorID = sID+"_error";
		var elemErreurDisplay = document.getElementById(objErrorID);
		var sHtmlTextError = "";

		_GE_aErrorArray[sID].fadeState = 0;

		if (_GE_TimeOutFade)
			clearTimeout(_GE_TimeOutFade);

		sHtmlTextError = sTextError.replace(/ /g, "&nbsp;");

		if (_GE_aErrorArray[sID].X_POS)
			elemErreurDisplay.style.left = _GE_aErrorArray[sID].X_POS+"px";

		sHTML = "<div><span>"+sHtmlTextError+"<a class=\"GE_BnClose\" alt=\"fermer\" href=\"javascript:GE.closeError('"+sID+"');\">X</a></span></div>";
		elemErreurDisplay.innerHTML = sHTML;
		
		elemErreurDisplay.style.opacity = "1";
		elemErreurDisplay.style.display = "block";

		if (bColorInput)
			document.getElementById(sID).className = _GE_errorClassName;

		// Si on ferme en disparaissant			
		if (bCloseFade)
			_GE_TimeOutFade = setTimeout("_GE_FadeError('"+sID+"')", 4000);
	}

	/**
	 * Fonction qui change l'opacité
	 * @param {String} sID ID de l'erreur
	 */
	function _GE_fadeElem(sID) {
		var elem = document.getElementById(sID+"_error");
		if (((elem.style.opacity >0) && (elem.style.opacity <1)) && _GE_aErrorArray[sID].fadeState == 1) {
			elem.style.opacity -= 1/100;
			setTimeout("_GE_fadeElem('"+sID+"')", elem.style.opacity*10);
		} else {
			_GE_aErrorArray[sID].fadeState = 0;
			elem.style.display = "none";
		}
			
	}
	
	/**
	 * Fait disparaitre progressivement une erreur
	 * @param {String} objID ID de l'erreur
	 */
	function _GE_FadeError(sID){
		var elem = document.getElementById(sID+"_error");
		_GE_aErrorArray[sID].fadeState = 1;

		elem.style.opacity = 0.99;
		_GE_fadeElem(sID);
	}

	/**
	 * Ferme un tooltip d'erreur
	 * @param {Object} iErreurIndex
	 * @param {Object} bColorInput
	 */
	function _GE_closeError(iErreurIndex, bColorInput) {
		var objErrorID	= _GE_aErrorArray[iErreurIndex].ID+"_error";
		var objError	= document.getElementById(objErrorID);

		if (objError.style.display != "none") {
			objError.innerHTML="";
			objError.style.display="none";
		}
		
		if (bColorInput)
			document.getElementById(_GE_aErrorArray[iErreurIndex].ID).className = "GE_normalSaisie";
	}


	/**
	 * Vérifie le format d'une heure
	 * @param {Object} objID ID de la textbox contenant l'information
	 * @param {Boolean} bColorInput Colore ou pas la textbox si il y a erreur
	 */
	function _GE_checkHeure(objID, bColorInput) {
		sHeure = document.getElementById(objID).value;
		var Expression = new RegExp("([0-9]{1,2})[Hh]{1}([0-9]{0,2})");
		aHeure = Expression.exec(sHeure);

		if (trim(sHeure) == "")
			return _GE_raiseError(objID, true);

		if (!aHeure)
			return _GE_raiseError(objID, true, "Format incorrect <strong>##H##</strong>");

		if (((aHeure[1]>=0) && (aHeure[1]<24)) && (((aHeure[2]>=0) && (aHeure[2]<60)) || (!aHeure[2]))){
			_GE_closeError(objID, bColorInput);
			return "";
		}else
			return _GE_raiseError(objID, true, "Heure invalide");
	}

	/**
	 * Vérifie le format d'une date
	 * @param {Object} objID ID de la textbox contenant l'information
	 * @param {Boolean} bColorInput Colore ou pas la textbox si il y a erreur
	 */
	function _GE_checkDate(objID, bColorInput) {
		sDate = document.getElementById(objID).value;
		var Expression = new RegExp("([0-9]{1,2})/([0-9]{1,2})/([0-9]{4})");
		aDate = Expression.exec(sDate);

		if (trim(sDate) == "")
			return _GE_raiseError(objID, true);

		if (!aDate)
			return _GE_raiseError(objID, true, "Format incorrect <strong>JJ/MM/AAAA</strong");

		dDateTest = new Date(aDate[3], aDate[2]-1, aDate[1]); // année, mois (0-11), jour

		iAnnee = dDateTest.getYear();
		if ((Math.abs(iAnnee)+"").length < 4) iAnnee = iAnnee + 1900

		if ((dDateTest.getDate() == eval(aDate[1])) && (dDateTest.getMonth() == eval(aDate[2])-1) && (iAnnee == eval(aDate[3]))) {
			_GE_closeError(objID, bColorInput);
			return "";
		}else
			return _GE_raiseError(objID, true, "Date incorrecte");
			
	}


	function __GE_checkPeriode (sIdFrom, sIdTo) {
		sDateFrom = document.getElementById(sIdFrom).value;
		aDateFrom = sDateFrom.split('/');
		dDateFrom = new Date(aDateFrom[2], aDateFrom[1], aDateFrom[0]);
		
		sDateTo = document.getElementById(sIdTo).value;
		aDateTo = sDateTo.split('/');
		dDateTo = new Date(aDateTo[2], aDateTo[1], aDateTo[0]);

		if (dDateTo.getTime() < dDateFrom.getTime())
			return _GE_raiseError(sIdTo, true, "Période incorrecte");
		else
			return "";

	}

	/**
	 * Supprime les espace en trop au début et à la fin
	 * @param {String} string Chaine à traiter
	 */
	function trim(string) {
		return string.replace(/(^\s*)|(\s*$)/g,'');
	} 

	/**
	 * Permet de modifier le statu de l'auto vérification d'un textbox
	 * @param {String} sId ID du champ
	 * @param {Boolean} bAutoVerif Vérification automatique true/false
	 */
	function _GE_setAutoVerif(sId, bAutoVerif) {
		_GE_aErrorArray[sId].DISABLE_AUTO_VERIF = !bAutoVerif;
		_GE_setEvents(sId);
	}
	
	/**
	 * Ajoute ou enlève les évènements liés au champ en fonction de NO_AUTO_VREFI
	 * @param {Object} sId ID du champ
	 */
	function _GE_setEvents(sId) {
		oElem = document.getElementById(sId);

		// Evènements classiques
		oElem.onfocus = _GE_hideError;

		// Evènements spéciaux
		if (_GE_aErrorArray[sId].DISABLE_AUTO_VERIF != true) {
			switch (oElem.type) {
				case "text" :
				case "password":
				case "textarea" :
						oElem.onblur = _GE_autoShowError;
					break;
				case "select-one" :
						oElem.onblur = _GE_autoShowError;
						oElem.onchange = _GE_autoShowError;
					break;
			}
		} else {
			_GE_closeError(sId, true);
			switch (oElem.type) {
				case "text" :
				case "textarea" :
						oElem.onblur = null;
					break;
				case "select-one" :
						oElem.onblur = null;
						oElem.onchange = null;
					break;
			}
		}
		
	}
	
	/**
	 * Permet de faire scroller le navigateur directement vers le (1er si pas de paramètres) champ qui pose problème 
	 * @param {Object} sId ID du champ
	 */
	function _GE_scrollTo(sID) {
		if (!sID)
			sID = _GE_sAncre;
		document.getElementById(sID).scrollIntoView("true")
	}

