// JavaScript Document
Validation = function(formName, required, absolutePositioning, verbose){
	var scope = this;
	/**
	* PUBLIC VARIABLES
	* Can be read/write from outside
	**/
	this.formName = formName;
	this.form = document.getElementsByName(this.formName)[0];
	this.required = required || "";
	this.required = this.required.split("|");
	this.absolutePositioning = (absolutePositioning == undefined)? true : absolutePositioning;
	this.verbose = (verbose == undefined)? true : verbose;
	this.disableSubmit = true;
	
	//default classes classes
	this.errorClass = "error_field_span";
	this.okFieldClass = "okField";
	this.errorFieldClass = "errorField";
	this.labelErrorClass = "labelError";
	this.labelOkClass = "labelOk";
	this.charCountClass = "charCount";
	
	//Empty messages
	this.emptyFieldMessage = "Ce champ ne peut être vide";
	this.emptyCheckboxMessage = "Veuillez accepter les conditions d'utilisation";
	this.emptySelectMessage = "Veuillez faire un choix";
	this.emptyFileMessage = "Veuillez choisir un fichier";
	
	//char limit
	this.useCharCount = true;
	this.forceCharCount = false;
	this.remainingCharsMessage = "Charactères restants : ";
	
	//browser  check +buffer
	this.isIE6 = /MSIE 6/i.test(navigator.userAgent);
	this.isIE7 = /MSIE 7/i.test(navigator.userAgent);
	this.isIE8 = /MSIE 8/i.test(navigator.userAgent);
	this.isOpera = /Opera/i.test(navigator.userAgent);
	this.bufferIE6 = {x:0, y:0};
	this.bufferIE7 = {x:-15, y:0};
	this.bufferIE8 = {x:0, y:0};
	this.bufferOpera = {x:0, y:0};
	this.buffer = {x:5, y:0}; //added to all other buffer
	
	this.validation = {};
	
	/**
	* PRIVATE VARIABLES & METHODS
	* Cannot be read/write from outside (directly)
	* Need a getter to be read, a setter to be set
	**/
	
	//field count for disabling/enabling submit button
	var _count = 0;
	var getCount = function getCount(){ //getter
		 return _count;
	}
	var setCount = function setCount(count){ //setter
		_count = count;
	}
	
	/**
	* Function called to setup the validation process
	**/
	var setupValidation = function setupValidation(s){
		var SCOPE = s; //get the form scope
		if(SCOPE.form == undefined){
			if(SCOPE.verbose)alert("Le formulaire " + SCOPE.formName + " n'est pas trouvé dans la page.");
			return false;
		}else{
			var fields = elements('input,select,textarea', SCOPE);
			for(var i=0; i < fields.length; i++){
				
				if(fields[i].type == "text" || fields[i].type == "password" || fields[i].rows != undefined){
					fields[i].onkeyup = function(e){
						e = e || window.event;
						formValidate(this, e, SCOPE);
					}
				}else if(fields[i].type == "checkbox"){
					fields[i].onclick = function(e){
						e = e || window.event;
						formValidate(this, e, SCOPE);
					}
					
				}else if(fields[i].type == "file" || fields[i].selectedIndex != undefined){
					fields[i].onchange = function(e){
						e = e || window.event;
						formValidate(this, e, SCOPE);
					}
					
				}else if(fields[i].type == "submit"){
					if(SCOPE.disableSubmit){
						fields[i].disabled = true;
					}
	
				}
			}
			
			SCOPE.form.onsubmit = function(){				
				return checkSubmit(this, SCOPE.required, SCOPE); 			   	   
			}
			return true;
		}
	}

	/**
	* Function called to add a field to the validation process
	* @param fieldName : The name of the filed in the form
	* @param ereg : The RegEx use to validate format
	* @param message : The message in case of failed validation for the field
	* @param comparaison : The comparaison fieldName (optional)
	**/
	var formValidate = function formValidate(field, e, scope,  fullcheck, noColors){
		var SCOPE = scope;
		fullcheck = (fullcheck == undefined) ? true : fullcheck;
		var noColors = noColors || false;
		
		var reponse = "OK";
		
		if(e != null && field.rows == undefined){
			if(e.which != undefined && e.which == 13)return checkSubmit(SCOPE.form, SCOPE.required, SCOPE)
			if(e.keyCode == 13)return checkSubmit(SCOPE.form, SCOPE.required, SCOPE)
		}
		
		var submitBtns = elements("input:submit",SCOPE);
		if(fullcheck && SCOPE.disableSubmit){
			for(submitBtn in submitBtns){
				submitBtns[submitBtn].disabled = !checkSubmit(SCOPE.form, SCOPE.required, SCOPE, true)
			}
		}
		
		if(fullcheck && SCOPE.useCharCount && SCOPE.validation[field.name] != undefined && SCOPE.validation[field.name].maxChar != undefined)showCharCount(field,SCOPE);
		
		for (fieldToValidate in SCOPE.validation){
			if(fieldToValidate ==  field.name){
				if(field.value != ''){
					if(field.value.match(SCOPE.validation[fieldToValidate].ereg)){
						//ok
						//alert(fieldToValidate + 'VALID : ' + field.value);
						if(SCOPE.validation[fieldToValidate].comparaison != undefined && SCOPE.validation[fieldToValidate].comparaison != ""){
							var comparaisonField = document.getElementsByName(SCOPE.validation[fieldToValidate].comparaison)[0];
							if(field.value != comparaisonField.value){
								reponse = SCOPE.validation[fieldToValidate].message;
							}else reponse = 'OK';
						}else if(SCOPE.validation[fieldToValidate].maxChar != undefined){
							
							if(field.value.length <= SCOPE.validation[fieldToValidate].maxChar){
								reponse = 'OK';
							}else{
								reponse = SCOPE.validation[fieldToValidate].message;
								
							}
						}else reponse = 'OK';
					}else{
						//probleme de format
						reponse = SCOPE.validation[fieldToValidate].message;
						
					}
				}else{
					
					reponse = "OK";
				}
				return checkFieldValidity(field, reponse, SCOPE, noColors);
			}
		}
		
		//if select element
		if(field.selectedIndex != undefined && $.inArray(field.name,SCOPE.required) != -1){
			if(field.selectedIndex  == 0){
				reponse = SCOPE.emptySelectMessage;
			}
			return checkFieldValidity(field, reponse,SCOPE, noColors);
		}
		
		//if checkbox element
		
		if(field.type == "checkbox" && $.inArray(field.name, SCOPE.required)!= -1){
			if(!field.checked){
				reponse = SCOPE.emptyCheckboxMessage;
			}
			return checkFieldValidity(field, reponse,SCOPE,  noColors);
		}
		
		//if fileinput element
		if(field.type == "file" && $.inArray(field.name,SCOPE.required)!= -1){
			if(field.value == ""){
				reponse = SCOPE.emptyFileMessage;
			}
			return checkFieldValidity(field, reponse,SCOPE,  noColors);
		}
		//if gets here, its not validated -> true
		return true;
	}
	
	/**
	* Function called to add a field to the validation process
	* @param fieldName : The name of the filed in the form
	* @param ereg : The RegEx use to validate format
	* @param message : The message in case of failed validation for the field
	* @param comparaison : The comparaison fieldName (optional)
	**/
	var checkSubmit = function checkSubmit(form, required, scope, checkOnly, targetField){
		var SCOPE = scope;
		checkOnly = (checkOnly == undefined) ? false:checkOnly;
		//au départ on prend en compte que tous les champs sont ok
		var valid = true;
		//récupère chaque champ texte d'un formulaire et boucle sur chacun
		var fields = elements("input:text,input:password,input:file,input:checkbox,select,textarea", SCOPE);
		var invalidFields = Array();
		for(var i=0; i < fields.length; i++){
			if(targetField == undefined || targetField == fields[i]){
				if(valid == false){
					valid = valid;
					var tmpValid = formValidate(fields[i], null, SCOPE, false, checkOnly);
					if(!tmpValid)invalidFields.push(fields[i].name);
				}else{
					valid = formValidate(fields[i], null, SCOPE, false, checkOnly);
					if(!valid)invalidFields.push(fields[i].name);
				}	
			}
			
		}
		//vérification des champs requis, sont-ils vides ?
		for(var i=0; i < SCOPE.required.length; i++){
			var field = document.getElementsByName(SCOPE.required[i])[0];
			var message = "OK";
			if(targetField == undefined || targetField == field){
				if(field != undefined && $.inArray(field.name,invalidFields) == -1){
					if(field.type == 'checkbox' && !field.checked){
						message = (SCOPE.validation[field.name] != undefined && SCOPE.validation[field.name].emptyMessage != undefined) ? SCOPE.validation[field.name].emptyMessage : SCOPE.emptyCheckboxMessage;
						valid = false;
					}else if(field.type == 'file' && field.value == ""){
						message = (SCOPE.validation[field.name] != undefined && SCOPE.validation[field.name].emptyMessage != undefined) ? SCOPE.validation[field.name].emptyMessage : SCOPE.emptyFileMessage;
						valid = false;
					}else if(field.selectedIndex != undefined && field.selectedIndex == 0){
						message = (SCOPE.validation[field.name] != undefined && SCOPE.validation[field.name].emptyMessage != undefined) ? SCOPE.validation[field.name].emptyMessage : SCOPE.emptySelectMessage;
						valid = false;
					}else if(field.value == ""){
						
						message = (SCOPE.validation[field.name] != undefined && SCOPE.validation[field.name].emptyMessage != undefined) ? SCOPE.validation[field.name].emptyMessage : SCOPE.emptyFieldMessage;
						valid = false;
					}
					if(!checkOnly)checkFieldValidity(field, message, SCOPE, checkOnly);
				}
			}
			
		}
		return valid;
	}

	/**
	* Source http://www.quirksmode.org/js/findpos.html
	* Function called to find the position of an htmlobject adding parent offset
	* along the way to get real position in pixels from screen
	* @param fieldName : The name of the filed in the form
	* @param ereg : The RegEx use to validate format
	* @param message : The message in case of failed validation for the field
	* @param comparaison : The comparaison fieldName (optional)
	**/
	var checkFieldValidity = function checkFieldValidity(field, reponse, scope, checkOnly) {
		checkOnly = (checkOnly == undefined) ? false:checkOnly;
		var SCOPE = scope;
		//Est-ce que le champ est ok
		//alert(reponse + ' champ ' + field.name);
		if(reponse == 'OK'){
			if(!checkOnly){
				if($('#err_'+field.id).length != 0){
					$('#err_'+field.id).remove();
				}
				if(field.value != ""){
					//changer la couleur de fond pour vert
					$('#' + field.id).removeClass(SCOPE.errorFieldClass);
					$('#' + field.id).addClass(SCOPE.okFieldClass);
					$('label[for="'+field.name+'"]',SCOPE.form).removeClass(SCOPE.labelErrorClass);
					$('label[for="'+field.name+'"]',SCOPE.form).addClass(SCOPE.labelOkClass);
				}else{
					
					$('#' + field.id).removeClass(SCOPE.errorFieldClass);
					$('#' + field.id).removeClass(SCOPE.okFieldClass);
					$('label[for="'+ field.name+'"]',SCOPE.form).removeClass(SCOPE.labelErrorClass);
					$('label[for="'+ field.name+'"]',SCOPE.form).removeClass(SCOPE.labelOkClass);
				}
			}
			return true;
		}else{
			if(!checkOnly){
				$('#' + field.id).addClass(SCOPE.errorFieldClass);
				$('#' + field.id).removeClass(SCOPE.okFieldClass);
				$('label[for="'+field.name+'"]',SCOPE.form).addClass(SCOPE.labelErrorClass);
				$('label[for="'+field.name+'"]',SCOPE.form).removeClass(SCOPE.labelOkClass);
				//est-ce qu'un champ erreur existe deja
				if($('#err_'+field.id).length == 0){
					//probleme avec le champ
					if(SCOPE.absolutePositioning){
						$(field).after("<span id='err_"+field.id+"' class='"+SCOPE.errorClass+"'>"+reponse+"</span>");
						$("#err_"+field.id).css("top",findPos(field, SCOPE)[1]);
						$("#err_"+field.id).css("left",findPos(field, SCOPE)[0] + field.offsetWidth);
					}else{
						$(field).after("<span id='err_"+field.id+"' class='"+SCOPE.errorClass+"'>"+reponse+"</span>");
					}
				}else{
					$('#err_' + field.id).html(reponse);
				}
			}
			return false;
		}
	}
	
	/**
	* Source http://www.quirksmode.org/js/findpos.html
	* Function called to find the position of an htmlobject adding parent offset
	* along the way to get real position in pixels from screen
	* @param fieldName : The name of the filed in the form
	* @param ereg : The RegEx use to validate format
	* @param message : The message in case of failed validation for the field
	* @param comparaison : The comparaison fieldName (optional)
	**/
	var findPos = function findPos(obj, scope) {
		var SCOPE = scope;
		var curleft = curtop = 0;
		//if (obj.offsetParent) {
			//do {
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
				//damn ie engine!!!
				if(SCOPE.isIE6){
					curleft += SCOPE.bufferIE6.x;	
					curtop += this.bufferIE6.y;	
				}else if(SCOPE.isIE7){
					curleft += SCOPE.bufferIE7.x;	
					curtop += SCOPE.bufferIE7.y;	
				}else if(SCOPE.isIE8){
					curleft += SCOPE.bufferIE8.x;	
					curtop += SCOPE.bufferIE8.y;	
				}else if(SCOPE.isOpera){
					curleft += SCOPE.bufferOpera.x;	
					curtop += SCOPE.bufferOpera.y;	
				}
			//} while (obj = obj.offsetParent);
			//alert(curtop);
		//}
		return [curleft + SCOPE.buffer.x,curtop + SCOPE.buffer.y];
	}
	
	
	/**
	* Function called to mimic jquery Selector (input:submit)
	* @param selectors : The selection (input:submit, select)
	* @return Array : The elements found
	**/
	var elements = function elements(selectors, scope){
		var SCOPE = scope;
		var allSelectors = selectors.split(",");
		var found = Array();
		for(selector in allSelectors){
			var tag = allSelectors[selector].split(":")[0];
			var type = allSelectors[selector].split(":")[1];
			var elements = SCOPE.form.getElementsByTagName(tag);
			for(element in elements){
				if(type != undefined){
					if(elements[element].type == type){
						if(typeof elements[element] != 'number' && typeof elements[element] != 'function')found.push(elements[element]);
					}
				}else{
					if(typeof elements[element] != 'number' && typeof elements[element] != 'function')found.push(elements[element]);
				}
			}
		}
		return found;
	}
	
	/**
	* Function called to calculate remaining chars for a particular field
	* @param field : The the field to check
	* @param maxChar : The maximum allowed
	**/
	var showCharCount = function showCharCount(field, scope){
		var SCOPE = scope;
		var maxChar = SCOPE.validation[field.name].maxChar;
		var remainingChars = (maxChar - field.value.length > -1) ? maxChar - field.value.length : 0;
		if(SCOPE.forceCharCount)field.value = field.value.substr(0,maxChar);
		//if(field.value != ""){
			if($('#count_'+field.id).length == 0){
				$(field).after("<span id='count_"+field.id+"' class='"+SCOPE.charCountClass+"'>"+ SCOPE.remainingCharsMessage + remainingChars +"</span>");
			}else $('#count_' + field.id).html(SCOPE.remainingCharsMessage + remainingChars);
		//}
	}

	/** PRIVILEGED VARIABLES & METHODS
	* Cannot be read/write from outside (directly)
	* Need a getter to be read, a setter to be set
	**/
	
	
	/**
	* Function called to add a field to the validation process
	* @param fieldName : The name of the filed in the form
	* @param ereg : The RegEx use to validate format
	* @param message : The message in case of failed validation for the field
	* @param comparaison : The comparaison fieldName (optional)
	* @param maxChar : The maximum char count for this element (optional)
	**/
	Validation.prototype.addField = function(fieldName, ereg, message, comparaison, maxChar){
		field = document.getElementsByName(fieldName)[0];
		if(field == undefined){
			if(this.verbose)alert("L'élément " + fieldName + " n'existe pas dans le formulaire " + this.formName);
		}else{
			this.validation[fieldName] = {
				message :message,
				ereg:ereg,
				comparaison:comparaison,
				maxChar:maxChar
			}
			setCount(0);
			for (k in this.validation) if (this.validation.hasOwnProperty(k)) setCount(getCount()+1);
		}
	}
	
	/**
	* Function called to change the default empty message for a particular field
	* @param fieldName : The name of the filed in the form
	* @param message : The message in case of failed validation for the field
	**/
	Validation.prototype.setEmptyFieldMessage = function (fieldName, message){
		field = document.getElementsByName(fieldName)[0];
		if(field == undefined){
			if(this.verbose)alert("L'élément " + fieldName + " n'existe pas dans le formulaire " + this.formName);
		}else{
			if(this.validation[fieldName] == undefined)this.validation[fieldName] = {};
			this.validation[fieldName].emptyMessage = message;
		}
	}
	
	/**
	* Function called to validate the form (or single field) and enable or disable submit btns (if any)
	* @param aTargetFieldId : The id of the field to validate (if empty, the full form is validated)
	* @param aCheckOnly : If true, no css is applied to valid or invalid elements
	* Return true if the form is valid else returns false
	**/
	Validation.prototype.validate = function(aTargetFieldId, aCheckOnly){
		var full = (aTargetFieldId == undefined) ? true : false;
		var checkOnly = (aCheckOnly == undefined) ? false:aCheckOnly;
		var targetField =  document.getElementById(aTargetFieldId);
		var submitBtns = elements("input:submit",this);
		var valid = (full) ? checkSubmit(this.form, this.required, this, checkOnly, targetField) : checkSubmit(this.form, this.required, this, checkOnly, targetField);
		if(this.disableSubmit){
			for(submitBtn in submitBtns){
				submitBtns[submitBtn].disabled = !checkSubmit(this.form, this.required, this, true);
			}
		}
		return valid;
	}
	
	
	//ACTIONS - INIT
	this.validSetup = setupValidation(this);
}






