/**
 * Ensembles de fonctions et de classes prenant en charge la validation de formulaire,
 * le formatage ...
 * D?pendances : prototype (event , $H()).
 */

// ===================== A FAIRE / TODO =====================================================
// - ajouter la v?rification automatique des mails.
// voici lexp reg qui va bien :
// var emailfilter=/^\w+[\+\.\w-]*@([\w-]+\.)*\w+[\w-]*\.([a-z]{2,4}|\d+)$/i
// var returnval=emailfilter.test(e.value)
// if (returnval==false){
//	alert("Please enter a valid email address.")




// =================== CLASSES =============================================================


/**
 * Classe encapsulant une erreur de validation.
 * Les instances de ValidationError sont cr??es par le FormValidator.
 * une liste de messages pr? d?finie est cr??e ? la premiere instance d'un Validationerror.
 * Eventuellement, cette liste peut ?tre mise ? jour avec ValidationError.setMessages(map);
 */
var ValidationError = Class.create();

ValidationError.prototype = {
	initialize: function(eltSrc, errorCode, values, eltName,_msgMap) {
		this.msgMap=_msgMap;
		if (!this.msgMap)
		{
			this.msgMap = $H({
				'empty' : 'Le champ {elt} doit être renseigné.',
				'not_int' : 'Le champ {elt} doit avoir une valeur entière.',
				'not_double' : 'Le champ {elt} doit avoir une valeur numérique.',
				'not_date' : 'Le champ {elt} doit avoir le format jj/mm/aaaa.',
				'not_in_bounds' : 'Le champ {elt} doit être compris entre {0} et {1}.'
			});
		}
  		this.src = eltSrc;
  		this.code = errorCode;
  		this.values = values;
  		this.eltName = eltName;
		this.message = new MessageFormat(this.msgMap[this.code], this.values).getMessage();
		this.updateSpecialAttribute();
	},
	updateSpecialAttribute : function()
	{
		expr = eval('/\{elt\}/');
		this.message = this.message.replace(expr, this.eltName);
	},
	getMessage : function()
	{
		return this.message;
	},
	setMessages : function(errorsMsgMap)
	{
		this.msgMap = $H(errorsMsgMap);
	}
}

/**
 * Cette classe se charge de la validation d'un formulaire.
 *
 * formCtrler	 = new FormController($('formulaire'));
 * //formCtrler.initForm();
 *
 *  $Click($('btvalider'), function()
 *  {
 *			formCtrler.validate();
 *			if (formCtrler.hasErrors())
 *			{
 *				errList = formCtrler.getErrors();
 *				var resultString = '';
 *				for (var  i=0;i<errList.length;i++)
 *				{
 *					resultString +='\n'+errList[i].getMessage();
 *				}
 *                                displayErrors(resultString);
 *			}
 *                        else
 *                        {
 *				sendRequest(null, strFormId, options);
 *                        }
 *			                  }
 *                  );
 *  }
 */
var FormController = Class.create();

FormController.prototype = {
	/**
	 * Le constructeur lie l'objet de validation au formulaire de validation.
	 */
	initialize : function(formId)
	{
		this.formId = formId;
		this.form = $(this.formId);
		this.initForm();
	},
	updateBlocksToSubmit : function(formId, form, pBlocksToSubmit)
	{
		this.blocksToSubmit = pBlocksToSubmit;
		this.updateForm(formId, form);
	},
	setMsgMap : function(_msgMap)
	{
		this.msgMap = _msgMap;
	},
	getFormId:function()
	{
		return this.formId;
	},
	isFormId:function()
	{
		return this.formId && this.formId!=null;
	},
	updateForm: function(strFormId, formElt)
	{
		this.formId = strFormId;
		this.form = formElt;
		this.initForm();
	},
	/**
	 * @return true si le formulaire a ï¿½tï¿½ modifiï¿½.
	 */
	isDirty:function()
	{
		return this.dirty;
	},
	/**
	 * permet de spï¿½cifier si le formulaire a ï¿½tï¿½ modifiï¿½ ou non
	 */
	setDirty : function(isFormDirty)
	{
		this.dirty = isFormDirty;
	},        
	/**
	 * retourne vrai si une erreur a ?t? d?tect?e.
	 * cette m?thode doit ?tre appel?e apr?s la m?thode validate.
	 */
	hasErrors : function()
	{
		return this.errors.length >0;
	},
	/**
	 * retourne un tableau d'objets ValidationError
	 */
	getErrors : function()
	{
		return this.errors;
	},
	
	getFormElements:function()
	{
		if(this.blocksToSubmit)
		{
			return new FormUtils().getFormElements(this.blocksToSubmit);
        }
		else
		{
			return this.form;
        }
    },
	
	/**
	 * met ? jour les champs du formulaire si besoin est.
	 * (par exemple, cette m?thode effectue un formatage automatique des donn?es.
	 * elle d?fini les ?venements 'blur', 'focus', 'keypress', 'keyup' sur tous les ?l?m?nts du formulaire)
	 */
	initForm:function()
	{
		this.errors = new Array();
		
		formElementList = this.getFormElements();
		
		//for(var i=0; i<this.form.length; i++)
		for(var i=0; i<formElementList.length; i++)
		{
			//var element = this.form[i];
			this.addElementToController(formElementList[i]);
		}
	},
	
	addElementToController:function(element)
	{
		if (element.type=='text')
		{
			Event.observe(element , 'blur',
			function(evt)
			{
				// ? la perte de focus, on applique le formatage
				// et la v?rification unitaire du champ.
				var element = Event.element(evt);
				var formatType = element.getAttribute('format');
				if (formatType)
				{
					var format = null;
					if (formatType == 'date')
					{
						format =new DateFormat();
						var formatDate = element.getAttribute('dateFormat');
						if (formatDate)
							format.setFormat(formatDate);
					}
					else
					{
						format = new NumberFormat();
					}
					resFormat = format.parse(element.value, formatType);

					if (resFormat)
					{
						var displayValue = format.format(resFormat, formatType);
						if (displayValue)
						{
							element.value = displayValue;
							if (Element.hasClassName(element, 'fielderror'))
							{
								Element.removeClassName(element, 'fielderror');
							}
						}
						else
						{	if (formatType == 'date')
							{
								element.value=format.getFormat();
							}
							Element.addClassName(element, 'fielderror');
						}
					}
					else
					{
						Element.addClassName(element, 'fielderror');
					}
				}

			}
		);
			Event.observe(element , 'focus',
			function(evt)
			{
				// (pour les champs nombre qui n'ont pas d'erreur)
				//  ? la prise de focus, on supprime le formatage
				var element = Event.element(evt);
				var formatType = element.getAttribute('format');
				var selectText = element.getAttribute('selectText');
				if (formatType && formatType != 'date' && Element.hasClassName(element, 'fielderror')==false)
				{
					var format = new NumberFormat();
					resFormat = format.parse(element.value, formatType);

					if (resFormat)
					{
						element.value = resFormat;
					}
				}
				if(selectText!=null)
				{
					element.select();
				}
			}
		);				

			Event.observe(element , 'keypress',
			function(evt)
			{
				// si l'ettribut 'validKey' de l'?l?ment existe,
				// on autorise seulement l'utilisation des caract?res
				// d?finis dans 'validKey' (plus les touches fl?ches, esc, tab, shift,...)
				var element = Event.element(evt);
				var validKey = element.getAttribute('validKey');
				if (validKey!=null)
				{
					var ret = keyRestrict(evt,validKey);
					if (ret==false && typeof(evt.stopPropagation)!="undefined")
					{
						evt.stopPropagation();
						evt.preventDefault();
					}
					return ret;
				}
				else
					return true;
			}
		);
			Event.observe(element , 'keyup',
			function(evt)
			{
				var element = Event.element(evt);
				// g?re le transfert de focus automatique de champ ? champ.
				// si la touche enter est press?e.
				var keyCode = evt.keyCode ? evt.keyCode : evt.which ? evt.which : evt.charCode;
				if (keyCode == 13 && element.form!=null) {
					var i;
					for (i = 0; i < element.form.elements.length; i++)
						if (element == element.form.elements[i])
							break;
					i = (i + 1) % element.form.elements.length;
					// FMN - si l'élément suivant de gère pas le focus on a une erreur
					// on évite donc l'erreur javascript avec le try...catch
					try
					{
						element.form.elements[i].focus();
					}
					catch(ex)
					{}
					return false;
				}

				// effectue une v?rification du format ? la frappe.
				// le formatage n'est pas appliqu?e.
				// par contre, s'il y a une erreur, elle est signal?e en rouge.
				if (!isNavKey(evt))
				{
					var formatType = element.getAttribute('format');
					if (formatType)
					{
						var format = null;
						if (formatType == 'date')
						{
							format =new DateFormat();
							var formatDate = element.getAttribute('dateFormat');
							if (formatDate)
								format.setFormat(formatDate);
						}
						else
						{
							format = new NumberFormat();
						}
						resFormat = format.parse(element.value, formatType);

						if (resFormat)
						{
							if (formatType == 'date')
							{
								if (element.value.replace(new RegExp("\/","g"), "").length==format.getFormat().replace(new RegExp("\/","g"),"").length)
								{
									var displayValue = format.format(resFormat, formatType);
									if (displayValue)
									{
										var posCurseur = doGetCaretPosition(element);
										var isCursorAtEnd = (posCurseur == element.value.length);
										element.value = displayValue;
										if (!isCursorAtEnd)
											doSetCaretPosition(element,posCurseur);
										launchFct('onDateValide',element);
									}
									else
										launchFct('onDateInvalide',element);
								}
								else
									launchFct('onDateInvalide',element);

							}
							if (Element.hasClassName(element, 'fielderror'))
							{
								Element.removeClassName(element, 'fielderror');
							}
						}
						else
						{
							if (formatType == 'date')
							{
								launchFct('onDateInvalide',element);
							}

							Element.addClassName(element, 'fielderror');
						}
					}
				}

			}
		);

		}
    },
	
	/**
	 * Lance la validation et le formatage du formulaire.
	 */
	validate:function()
	{
		this.errors = new Array();
		
		if (!this.form || this.form==null)
		{
			return;
		}

		formElementList = this.getFormElements();

		//for(var i=0; i<this.form.length; i++)
		for(var i=0; i<formElementList.length; i++)
		{
			//var element = this.form[i];
			var element = formElementList[i];
			
			if(Element.visible(element))
			{
				if(element.tagName=='SELECT')
				{
					if($F(element) == -1)
						this.errors.push(new ValidationError(element, 'empty', null, this.getLabel(element),this.msgMap));
				}
				else if(element.tagName=='INPUT')
				{
					if(element.type ==  'text')
					{
						this.affError(element);
					}
					else if(element.type ==  'radio')
					{
						if (isRequired(element))
						{
							var strName = element.name;
							var arrayOfNameElement = $A(this.form).findAll(
							function(otherElement)
							{
								return (otherElement.name == strName);
							}
						);
							var isChecked = false;
							for (var k=0; k < arrayOfNameElement.length; k++)
							{
								isChecked = isChecked || (arrayOfNameElement[k].checked == true);
							}
							if (!isChecked && element == arrayOfNameElement[0])
							{
								this.errors.push(new ValidationError(element, 'empty', null, this.getLabel(element),this.msgMap));
							}
						}
					}
					else if(element.type ==  'checkbox')
					{
						this.affError(element);
					}
					else if(element.type ==  'password')
					{
						this.affError(element);
					}
				}
			}
		}
	},
	
	affError:function(element)
	{
		var formatType = element.getAttribute('format');
		var formatDate = element.getAttribute('dateFormat');
		// si c'est un champ requis
		if (isRequired(element) && element.disabled!=true)
		{
			if (element.value == null || element.value.length==0)
			{
				this.errors.push(new ValidationError(element, 'empty', null, this.getLabel(element),this.msgMap));
			}
			else
			{
				this.evalFormat(element, formatType,formatDate);
			}
		}
		else
		// si le champ n'est pas obligatoire
		{
			this.evalFormat(element, formatType,formatDate);
		}
	},
	getLabel:function(element)
	{
		var label = findLabelTextFor(element);
		if(element.getAttribute('altLbl') != null)
			label = element.getAttribute('altLbl');
		return label;
	},
	evalFormat:function(element, formatType,formatDate)
	{
		// si aucun format n'est dÃ©fini, on ne fait aucun controle
		// la vÃ©rification n'est faite Ã©galement que si le champ a une valeur.
		if (formatType!=null && element.value != null && element.value.length>0)
		{
			var resFormat = null;
			var typeOfError = null;
			if (formatType == 'date')
			{
				var df = new DateFormat();
				if (formatDate!=null)
					df.setFormat(formatDate);
				resFormat = df.parse(element.value);
				
				if (resFormat == null)
				{
					typeOfError = 'not_date';
					this.errors.push(new ValidationError(element, typeOfError, null, this.getLabel(element),this.msgMap));
				}
			}
			else
			{
				var format = new NumberFormat();
				resFormat = format.parse(element.value);
				if (resFormat == null)
				{
					typeOfError = 'not_double';
					if (format.isInteger())
						typeOfError = 'not_int';
					this.errors.push(new ValidationError(element, typeOfError, null, this.getLabel(element),this.msgMap));
				}
			}
		}
	}

}

/**
 * Cette classe se charge de simuler le comportement de
 * fa?on minimaliste de la classe MessageFormat de Java.
 * <br />
 * De fa?on basique, elle remplace les ?l?ments {n} o? n est
 * un nombre entier ? l'int?rieur d'une cha?ne 'srcString'.
 * Pour cela, elle s'appuie sur le tableau d'?l?ments 'args'
 */

var MessageFormat = Class.create();
MessageFormat.prototype = {
	initialize: function(srcString, args) {
		this.message = srcString;
		if (args)
		{
			for (var i = 0; i<args.length; i++)
			{
				expr = eval('/\{['+i+']\}/');
				this.message = this.message.replace(expr, args[i]);
			}
		}
	},
	getMessage: function()
	{
		return this.message;
	}
}

/**
 * Cette classe permet de manipuler Date et chaine.
 * il n'y a qu'un seul format applicable / appliqu? : dd/MM/yyyy
 */
var DateFormat = Class.create();
DateFormat.prototype = {
	initialize : function (_pattern)
	{
		
		this.setFormat(_pattern!=null?_pattern:"jj/mm/aaaa");
	},
	format : function(dateArg)
	{
		if (dateArg)
		{
			var strDate = "";
			for (var i=0; i<this.tabFormat.length; i++)
			{
				var val = eval('this.get'+this.tabFormat[i].toUpperCase())(dateArg);
				if (isNaN(val))
					return null;
				if (val<10)
					val = '0'+val;
				strDate += val;
				if (i<this.tabFormat.length-1)
					strDate+='/';
			}
			return strDate;
		}
		else
		{
			return null;
		}
	},
	getJ : function(dateArg)
	{
		return dateArg.getDate();
	},
	getM : function(dateArg)
	{
		return dateArg.getMonth()+1;
	},
	getA : function(dateArg)
	{
		return dateArg.getFullYear();
	},
	getExpJ : function(dateArg)
	{
		return '(\\d{2})';
	},
	getExpM : function(dateArg)
	{
		return '(\\d{2})';
	},
	getExpA : function(dateArg)
	{
		return '(\\d{2,4})';
	},
	parse : function(strArg)
	{
		if (!strArg || strArg.length==0)
			return null;

		strArg = strArg.replace(/(-|\.)/g, "/") ;
		var expReg = "^";
		var strReg = "";
		for (var i=0; i<this.tabFormat.length; i++)
		{
			expReg += eval("this.getExp"+this.tabFormat[i].toUpperCase()+"()");
			strReg+="$"+eval(i+1);
			if (i<this.tabFormat.length-1)
				strReg+='/';
		}
		expReg += "$";
		//strArg = strArg.replace(/^(\d{2})(\d{2})(\d{2,4})$/g, "$1/$2/$3") ; /* cas o? il n'y a aucun s?parateur.*/
		strArg = strArg.replace(new RegExp(expReg,"g"),strReg);
		var elements = strArg.split(/\//g);
		if (elements == null || elements.length !=this.tabFormat.length)
			return null;
		var val = new Array();
		val['j']=1;
		val['m']=1;
		val['a']=2000;
		for (var i=0; i<this.tabFormat.length; i++)
		{
			val[this.tabFormat[i]] = elements[i];
			
		}
		if (isNaN(val['a']) || isNaN(val['j']) || isNaN(val['m']))
		{
			return null;
		}
		val['m'] -=1;
		// prise en compte de la saisie sur 2 chiffres pour les ann?es.
		var currentYear = new Date().getFullYear() - 2000;
		if (val['a'] <= currentYear)
			val['a'] = eval(val['a'])+2000;

		// v?rification du domaine de valeur pour chaque champ de la date.
		daysInMonths = [31,28,31,30,31,30,31,31,30,31,30,31];
		var maxDays = daysInMonths[val['m']];
		if (isLeapYear(val['a']) && val['m']==1)
			maxDays = 29;

		if (val['a'] <1600)
			return null;
		if (val['m'] <0 || val['m'] >11)
			return null;
		if (val['j'] < 0 || val['j'] > maxDays)
			return null;

		return new Date(val['a'],val['m'], val['j'] );
	},
	setFormat : function(_pattern)
	{
		this.pattern=_pattern;
		this.tabFormat = new Array();
		var arrayFormat = this.pattern.split(''), i = 0;
		var cpt=0;
		for (var i=0; i<arrayFormat.length; i++)
		{
			if ((i==0 || arrayFormat[i]!=arrayFormat[i-1]) && arrayFormat[i]!='/')
			{
				this.tabFormat[cpt++] = arrayFormat[i];
			}
		}
		
	},
	getFormat : function()
	{
		return this.pattern;
	}
}

/**
 * Retourne vrai (true) si l'ann?e pass?e en argument est bissextile.
 */
function isLeapYear(aYear)
{
	/* A year will be a leap year if it is divisible by 4 but not by 100.
	If a year is divisible by 4 and by 100, it is not a leap year
	unless it is also divisible by 400.*/

	if (aYear % 4 == 0)
	{
		if (aYear % 100 !=0)
		{
			return true;
		}
		else
		{
			if (aYear % 400 == 0)
			{
				return true;
			}
		}
	}
	return false;

	// autre solution :
	// return new Date(yr,2-1,29).getDate()==29;
}

/**
 * Simule le comportement de la classe NumberFormat issue de Java.
 * version minimaliste :
 * ne supporte que des formats variant autour de #,###.##.
 * attention: pour le format :
 *	- ',' = s?parateur de milliers
 *	- '.' = s?parateur d?cimal
 *	- '#' = n'importe quel chiffre
 *
 * ? partir de la chaine qui est pass?e, 2 choses sont conserv?es et utilis?es :
 * 	- le nombre de digits de la partie d?cimale
 * 	- si le s?parateur de groupe existe, le nombre de caract?res ? sa droite.
 *
 * ? la r?cup?ration de la chaine, on regarde s'il y a des
 *
 * au formatage du nombre
 * A utiliser pour le formatage de nombres entiers et r?els.
 */
var NumberFormat = Class.create();
NumberFormat.prototype = {
	initialize : function (strPattern)
	{
		this.pattern = strPattern;
	},
	/**
	 * lance le formatage d'une valeur num?rique
	 * en fonction d'un pattern d?fini.
	 */
	format : function(value, strPattern)
	{
		this.setFormat(strPattern);

		var result = null;

		// application de l'arrondi
		value = roundUp(value, this.digits);

		// ajustement des groupes
		var valueAsStr = ''+value;
		var iSep = valueAsStr.indexOf('.');
		result = '';
		var digitsStr = '';
		if (iSep ==-1)
		{
			iSep = valueAsStr.length;
		}
		else
		{
			digitsStr = '.'+valueAsStr.substr(iSep+1, valueAsStr.length);
		}
		var iCpt = 0;
		for (var iDec = iSep-1; iDec >=0; iDec--)
		{
			if (this.grouping > 0 && iCpt % this.grouping == 0 && iCpt!=0)
			{
				result =' ' + result;
				iCpt = 0;
			}
			result = valueAsStr.charAt(iDec) + result;
			iCpt++;
		}

		// ajustement du s?parateur d?cimal
		result += digitsStr;

		return result;
	},
	isInteger : function()
	{
		this.setFormat(null);
		return this.digits==0;
	},
	/**
	 * Cette m?thode parse une chaine et retourne la valeur num?rique
	 * associ?e en fonction du pattern (d?fini en argument ou sp?cifi? par d?faut)
	 * @return la valeur r?elle (int ou double), null en cas d'erreur lors du parsing
	 */
	parse : function(strArg, strPattern)
	{

		this.setFormat(strPattern);
		if (strArg)
		{
			strArg = strArg.replace(',','.');
			strArg = strArg.replace(new RegExp(/\s/g),"");
			if (isNaN(strArg))
			{
				return null;
			}
			if (this.digits==0)
				return parseInt(strArg,10);
			else
			{
				var realValue = parseFloat(strArg);
				realValue = roundUp(realValue, this.digits);
				return realValue;
			}
		}
		return null;
	},
	/**
	 * Cette m?thode d?termine automatiquement les grandeurs caract?ristiques
	 * li?es au format.
	 * Elle cr?e un pattern par d?faut si aucun n'est d?fini.
	 */
	setFormat:function (strPattern)
	{
		if (strPattern==null)
		{
			strPattern = '#,###.##';
		}
		this.pattern = strPattern;
		this.grouping = 0;
		this.digits = 0;
		var strGroup = this.pattern.match(/,#+[\.]{0,1}/);
		if (strGroup)
		{
			var str = strGroup[0].substr(1, strGroup[0].length-1);
			var endPoint = str.indexOf('.');
			if (endPoint>-1)
			{
				str = str.substr(0, str.length-1);
			}
			if (str != null)
			{
				this.grouping = str.length;
			}
		}
		var strDigits = this.pattern.match(/\.#+/);
		if (strDigits)
		{
			var str = strDigits[0].substr(1, strDigits[0].length-1);
			if (str != null)
			{
				this.digits = str.length;
			}
		}
	}
}

/**
 * fonction dont le r?le est d'arrondir une valeur pass?e en argument en fonction du nombre de digits
 * pass? en argument.
 * si le nombre de digits est de 0, on applique le traitement simple Math.round.
 */
function roundUp(value, nbDigits)
{
	if (value!="" && !isNaN(value))
	{
		if (nbDigits!=-1 )
		{
			value = Math.round(value * Math.pow(10.0,nbDigits)) / Math.pow(10.0,nbDigits);
		}
		else
		{
			value = Math.round(value);
		}
	}

	return value;
}

var timerID;
function launchFct(_fct,element)
{
	if (timerID!=null)
		clearTimeout(timerID);
	var fct = element.getAttribute(_fct);
	if (fct)
		timerID = setTimeout("eval('"+fct+"');",500);
}

//====================== M?thodes globales ===============================

/**
 * retourne le libell? associ? ? champ (? partir du tag label)
 */
function findLabelTextFor(element)
{
	// 2 solutions :
	// soit le champ input est sous un label
	// soit le chp label a un attribut for d?fini pour ce champ input.

	// m?thode 1
	parentNode = element.parentNode;
	if (parentNode && parentNode.tagName=='LABEL')
	{
		return getInnerText(parentNode);
	}

	// m?thode 2
	var allLbls = (document.body).getElementsByTagName('LABEL');
	if (allLbls)
	{
		for (var itLbls = 0; itLbls < allLbls.length;itLbls++)
		{
			curLbl = allLbls[itLbls];
			var forAttribute = curLbl.htmlFor;

			if (forAttribute == element.id)
			{
				return getInnerText(curLbl);
			}
		}
	}

	// sinon
	return null;
}

/**
 * retourne le text directement sous un ?l?ment.
 * g?re les diff?rences entre IE et Mozilla.
 */
function getInnerText(element)
{
	if (element.innerText)
	{
		return element.innerText;
	}
	return element.textContent;
}
/**
 * retourne vrai si un champ est obligatoire (attribut required sp?cifi? sur le champ)
 */
function isRequired(element)
{
	return element.getAttribute('required')!=null;
}
/**
 * annule la saisie de la touche entr?e ou controle, et n'accepte que les caracteres
 * contenus dans validchars (plus les touches fl?ches, esc, tab, shift,...)
 * info : utiliser cette fonction dans la page jsp comme cel? pour une saisie d'un montant :
 * <input ... onKeyPress="return keyRestrict(event,'1234567890')"/>
 */
function keyRestrict(e, validchars) 
{
	if(e.ctrlKey && e.ctrlKey==true) // si touche controle
		return false;
	var key='', keychar='', charCode='';
	key = getKeyCode(e);     
	charCode=e.charCode;    
	if (key == null) return true;
	keychar = String.fromCharCode(key);
	keychar = keychar.toLowerCase();
	validchars = validchars.toLowerCase();
	if (validchars.indexOf(keychar) != -1)
	{
		if ((keychar=="," || keychar==".") && (Event.element(e).value.indexOf(",")>=0 || Event.element(e).value.indexOf(".")>=0))
		{
			return false;
		}
		return true;
	}
	// touches accept?es dans tous les cas (fl?ches, suppr, del, tab, etc...)
	if ( charCode==0 && (key==null || key==Event.KEY_BACKSPACE || key==Event.KEY_TAB 
		|| key==Event.KEY_ESC || key==Event.KEY_LEFT || key==Event.KEY_UP || key==Event.KEY_RIGHT
		|| key==Event.KEY_DOWN || key==Event.KEY_DELETE || key==Event.KEY_HOME
		|| key==Event.KEY_END || key==Event.KEY_PAGEUP || key==Event.KEY_PAGEDOWN ) )
		return true;
	return false;
}

function isNavKey(e)
{
    if(e.ctrlKey && e.ctrlKey==true) // si touche controle
        return true;
    var key='';
    key = getKeyCode(e);
    var charCode=typeof(e.charCode)!='undefined'?e.charCode:0;
    if (key == null) return true;
    return ( charCode==0 && (key==null || key==Event.KEY_TAB 
		|| key==Event.KEY_ESC || key==Event.KEY_LEFT || key==Event.KEY_UP || key==Event.KEY_RIGHT
		|| key==Event.KEY_DOWN || key==Event.KEY_HOME
		|| key==Event.KEY_END || key==Event.KEY_PAGEUP || key==Event.KEY_PAGEDOWN ) );
}

function doGetCaretPosition (oField) {

	// Initialize
	var iCaretPos = 0;

	// IE Support
	if (document.selection) { 

		// Set focus on the element
		oField.focus ();
  
		// To get cursor position, get empty selection range
		var oSel = document.selection.createRange ();
  
		// Move selection start to 0 position
		oSel.moveStart ('character', -oField.value.length);
  
		// The caret position is selection length
		iCaretPos = oSel.text.length;
	}

	// Firefox support
	else if (oField.selectionStart || oField.selectionStart == '0')
		iCaretPos = oField.selectionStart;

	// Return results
	return (iCaretPos);
}


/*
 **  Sets the caret (cursor) position of the specified text field.
 **  Valid positions are 0-oField.length.
 */
function doSetCaretPosition (oField, iCaretPos) {

	// IE Support
	if (document.selection) { 

		// Set focus on the element
		oField.focus ();
  
		// Create empty selection range
		var oSel = document.selection.createRange ();
  
		// Move selection start and end to 0 position
		oSel.move ('character', -oField.value.length);
         
		// Move selection start and end to desired position
		oSel.move ('character', iCaretPos);
		//oSel.moveEnd ('character', iCaretPos);
		oSel.select ();
	}

	// Firefox support
	else if (oField.selectionStart || oField.selectionStart == '0') {
		oField.selectionStart = iCaretPos;
		oField.selectionEnd = iCaretPos;
		oField.focus ();
	}
}