/**
 * class formValidation
 * author : lx barjon <lx.barjon@gmail.com>
 * version : 0.7.0 (21-05-2007)
 * licence : GNU GPL
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************
 * 15-02-07
 **********
 * actualisation du code pour prototype.js v1.5
 **********
 * 21-05-07
 **********
 * Ajout méthode reset -> permet d'annuler les events accrochés et de les réaccrocher 
 *	Utile dans le cas d'un formulaire avec block duplicable. Il est possible maintenant selon les besoins de lancer un reset avec set_afterDuplicationCallback via isiForm
 */

/**
	var validationSet = {
	'isRequired': {
	  'errorMsg': 'Ce champ est requis. Veuillez fournir l\'information demandée.'
	},
	'isEmail': {
		'regexp': /^(.+?@.+?\..+)*$/,
		'errorMsg' : 'Cette adresse e-mail n\'est pas valide. Elle doit être de la forme quelquechose@exemple.com.'
	},
	'isAlpha': {
		'regexp': /^[a-zA-Z]$/,
		'errorMsg': 'Seul les caractères alphabétiques sont autorisés.'
	},
	'isNumber': {
		'regexp': /^[-0-9]*$/,
		'errorMsg': 'Veuillez saisir un nombre valide.'
	}
}
 */

var formValidation = Class.create();
formValidation.prototype = {
	initialize: function( formID, options ) {
		this.version = '0.7.0';
		
		if ( !document.getElementById ) return;
		
		this.form = $(formID);
		if ( !this.form ) alert('formValidation.js :: PROBLEME !! Formulaire - '+formID+' - introuvable !');
		
		if ( typeof validationSet == 'undefined' ) {
			alert( 'formValidation.js :: PROBLEME !! validationSet introuvable !' );
			return;
		}
		
		Object.extend( this.options = {
			pathToImgs: '',
			ecartX: 5,
			ecartY: 10,
			imgErrEcartX: 2,
			imgErrEcartY: -5,
			remoteProcess: true, // execute le formulaire via AJAX xmlHTTPRequest
			/** SI remoteProcess == true
			 * showPRAnim: Effect.Appear, // optionnel - animation pour l'affichage du div de resultat (nom d'une fonction)
			 * prX: "20%",  // optionnel - pour fixer le div de resultat sur X
			 * prY: "40px", // optionnel - pour fixer le div de resultat sur Y
			 */
			prMode: 'modal', // modal OU notModal OU draggable
			prClassName: 'processResult',	// nom de la class du div de resultat
			ZIndex: 100000 // z-index pour les div générés
		}, options || {} );
		
		// création d'un div pour l'affichage des messages d'erreurs
		var errorBox = Builder.node( 'div', {id: this.form.id + '_ErrorBox', style: 'position: absolute; z-index: '+this.options.ZIndex+'; display: none;', className: 'formErrorBox'} );
		document.getElementsByTagName('body')[0].appendChild(errorBox);
		this.errorBox = $(errorBox.id);
		Event.observe( document, 'mousemove', this.positionErrorBox.bindAsEventListener(this) );
		
		// accroche les évènements aux champs de formulaire
		_checkValidity = this.checkValidity.bind(this);
		el_checkValidity = this.checkValidity.bindAsEventListener(this);
		for ( var checkOption in validationSet ) {
			var formFields = document.getElementsByClassName( checkOption, this.form );
			if ( formFields.length > 0 ) {
				for ( var fieldCpt = 0; fieldCpt < formFields.length; fieldCpt++ ) {
					// field id est du type : check_id-champ -> on enlève check_ pour avoir l'id réel du champ
					var realFormField = $(formFields[fieldCpt].id.substring(6, formFields[fieldCpt].id.length));
					if ( realFormField) {
						if ( !realFormField.checkOptions ) realFormField.checkOptions = [];
						var reg = new RegExp("" + checkOption + "_[0-9]+","g");
						var result = formFields[fieldCpt].className.match(reg);
						if ( result ) realFormField.checkOptions.push(result);
						else realFormField.checkOptions.push(checkOption);
						Event.observe( realFormField, 'blur', el_checkValidity );
					}
				}
			}
			// évènement onsubmit pour le formulaire
			if ( !this.form.eventHooked ) {
				Event.observe( this.form, 'submit', this.checkValidityOnSubmit.bindAsEventListener(this), false );
				this.form.eventHooked = true;
			}
		}
	},
	
	reset: function() {
		// enlève les events
		for ( var checkOption in validationSet ) {
			var formFields = document.getElementsByClassName( checkOption, this.form );
			if ( formFields.length > 0 ) {
				for ( var fieldCpt = 0; fieldCpt < formFields.length; fieldCpt++ ) {
					// field id est du type : check_id-champ -> on enlève check_ pour avoir l'id réel du champ
					var realFormField = $(formFields[fieldCpt].id.substring(6, formFields[fieldCpt].id.length));
					if ( realFormField) {
						realFormField.checkOptions = [];
						var reg = new RegExp("" + checkOption + "_[0-9]+","g");
						var result = formFields[fieldCpt].className.match(reg);
						/*if ( result ) realFormField.checkOptions.pull(result);
						else realFormField.checkOptions.pull(checkOption);*/
						Event.stopObserving( realFormField, 'blur', el_checkValidity );
					}
				}
			}
		}
		// re-récup. les champs et ré-accroche les events
		for ( var checkOption in validationSet ) {
			var formFields = document.getElementsByClassName( checkOption, this.form );
			if ( formFields.length > 0 ) {
				for ( var fieldCpt = 0; fieldCpt < formFields.length; fieldCpt++ ) {
					// field id est du type : check_id-champ -> on enlève check_ pour avoir l'id réel du champ
					var realFormField = $(formFields[fieldCpt].id.substring(6, formFields[fieldCpt].id.length));
					if ( realFormField) {
						var reg = new RegExp("" + checkOption + "_[0-9]+","g");
						var result = formFields[fieldCpt].className.match(reg);
						if ( result ) realFormField.checkOptions.push(result);
						else realFormField.checkOptions.push(checkOption);
						Event.observe( realFormField, 'blur', el_checkValidity );
					}
				}
			}
		}
	},
	
	checkValidity: function( e ) {
		var field = Event.element(e);
		if ( !field ) return;
		
		var fieldIsValid = this.isValid(field);
		if ( !fieldIsValid ) {
			this.setError(field);
			field.onError = true;
			Element.addClassName( field, 'errorField' );
		}
		if ( fieldIsValid ) {
			field.errorMsg = null;
			field.onError = false;
			field.className = field.className.replace( /\b ?errorField\b/, '' );
			if ( $(field.id + '_errorImg') ) field.parentNode.removeChild($(field.id + '_errorImg'));
		}
	},
	
	checkValidityOnSubmit: function( e ) {
		var error = false;
		for ( var fieldCpt = 0; fieldCpt < this.form.elements.length; fieldCpt++ ) {
			var field = this.form.elements[fieldCpt];
			if ( field.checkOptions ) {
   			var fieldIsValid = this.isValid(field);
				if ( !fieldIsValid ) {
					this.setError(field);
					field.onError = true;
					Element.addClassName( field, 'errorField' );
					error = true;
				}
				if ( fieldIsValid ) {
					field.errorMsg = null;
					field.onError = false;
					field.className = field.className.replace( /\b ?errorField\b/, '' );
					if ( $(field.id + '_errorImg') ) field.parentNode.removeChild($(field.id + '_errorImg'));
				}
			}
		}
		// error ?
		if ( error ) {
			var errorMsg = "Certains champs du formulaire n'ont pas été saisis correctement." +
				"Veuillez corriger les erreurs et renvoyer le formulaire.";
			this.errorBox.innerHTML = errorMsg;
			setTimeout( this.hideError.bind(this), 5000);
			Element.show( this.errorBox );
		// process mode : xmlHTTPRequest
		} else if ( this.options.remoteProcess ) {
			// serialize form content and call form action via Ajax.Updater
			var formData = '';
			var formData = Form.serialize( this.form );
			// création div pour rendre la fenêtre, contenant les infos résultat, modal.
			// MARCHE PAS ENCORE SOUS IE 6.x
			if ( this.options.prMode == 'modal' & !this.overlay ) {
				this.overlay = Builder.node( 'div', { id: 'modalOverlay', style: 'position: fixed; z-index: '+this.options.ZIndex+';' } );
				document.getElementsByTagName('body')[0].appendChild(this.overlay);
			} else if ( this.overlay ) Element.show('modalOverlay');
			// création div pour affichage info résultat
			if ( !this.processResult ) {
				this.processResult = Builder.node( 'div', { id: 'processResult_' + this.form.id, style: 'position: fixed; display: none; z-index: '+this.options.ZIndex+';', className: this.options.prClassName } );
				document.getElementsByTagName('body')[0].appendChild(this.processResult);
			}
			// positionnement du div
			if ( window.innerHeight ) {
				var userWinHeight = window.innerHeight;
			} else var userWinHeight = (screen.availHeight-window.screenTop);
			var userWinWidth = document.getElementsByTagName('body')[0].offsetWidth;
			var oldDisplay = this.processResult.style.display;
			this.processResult.style.display = '';
			// draggable
			if( this.options.prMode == 'draggable' ) new Draggable(this.processResult);
			if ( this.options.prX ) this.processResult.style.left = this.options.prX;
			else {
				if ( (userWinWidth - this.processResult.offsetWidth) > 0 ) {
					var leftPos = Math.ceil( (userWinWidth - this.processResult.offsetWidth) / 2) + (Position.cumulativeOffset(this.form.parentNode.parentNode)[0]);
					this.processResult.style.left =  leftPos + 'px';
					// le container du formulaire est positionné de manière absolue -> on corrige 
					if ( Position.cumulativeOffset(this.processResult)[0] > leftPos ) {
						leftPos -= Position.cumulativeOffset(this.form.parentNode.parentNode)[0];
						this.processResult.style.left =  leftPos + 'px';
					}
				} else this.processResult.style.left = '0px';
			}
			if ( this.options.prY ) this.processResult.style.top = this.options.prY;
			else {
				if ( (userWinHeight - this.processResult.offsetHeight) > 0 ) this.processResult.style.top = Math.ceil( (userWinHeight - this.processResult.offsetHeight) / 2) + 'px';
				else this.processResult.style.top = '0px';
			}
			this.processResult.style.display = oldDisplay;
			this.processResult.innerHTML = 'Traitement en cours...';
			var processResult = this.processResult;
			// traitement du formulaire via xmlHTTPRequest
			var myAjax = new Ajax.Updater( {
					success: 'processResult_' + this.form.id
				}, this.form.action, {
					method: 'post',
					postBody: formData,
					onFailure: function(request) {
						processResult.innerHTML = '<p style="text-align: center;"><br />Erreur lors du transfert des informations distantes.<br /></p>';
					},
					evalScripts: true
				}
			);
			window.scrollTo( 0, 0 );
			// montre le div de résultat
			if (this.options.showPRAnim ) new this.options.showPRAnim( this.processResult );
			else Element.show( this.processResult );
			//if ( this.processResult.style.display == 'none' ) new this.options.showPRAnim( this.processResult, { afterFinish: this.options.afterSubmit } );
			//if ( this.processResult.style.display == 'none' )new Effect.BlindDown( this.processResult, { afterFinish: this.options.afterSubmit } );
			//if ( this.options.afterSubmit ) this.options.afterSubmit();
			//if ( $('CUSTOM_FORM') && this.options.prHideCF ) new Effect.BlindUp('CUSTOM_FORM');
		}

		// error or process mode : xmlHTTPRequest ? -> prevent default action
		if ( error || this.options.remoteProcess ) {
			if ( e && e.stopPropagation && e.preventDefault ) {
				e.stopPropagation();
				e.preventDefault();
			}
			if ( window.event ) {
				window.event.cancelBubble = true;
				window.event.returnValue = false;
				return false;
			}
		}
	},

	isValid: function( field ) {
		field.errorMsg = '';
		var isValid = true;
		var option = '';
		var testValue = '';
  	for ( var cpt = 0; cpt < field.checkOptions.length; cpt++ ) {
			if ( field.checkOptions[cpt].toString().indexOf('_') ) {
				 var optionInfo = field.checkOptions[cpt].toString().split('_');
				 option = optionInfo[0];
				 testValue = optionInfo[1];
			} else option = field.checkOptions[cpt];
			switch( option ) {
				case 'isRequired':
				  if ( !field.value ) {
						field.errorMsg += validationSet[option]['errorMsg'];
						isValid = false;
					}
				break;
				case 'isGreaterThan':
					if ( field.value ) {
						if ( isNaN(field.value) ) {
							field.errorMsg += "Veuillez saisir un nombre valide.";
							isValid = false;
						} else {
							if ( field.value <= testValue ) {
								field.errorMsg += validationSet[option]['errorMsg'].replace(/{x}/, testValue);
								isValid = false;
							}
						}
					}
				break;
				case 'isLessThan':
					if ( field.value ) {
						if ( isNaN(field.value) ) {
							field.errorMsg += "Veuillez saisir un nombre valide.";
							isValid = false;
						} else {
							if ( field.value >= testValue ) {
								field.errorMsg += validationSet[option]['errorMsg'].replace(/{x}/, testValue);
								isValid = false;
							}
						}
					}
				break;
				case 'maxLength':
					if ( field.value ) {
						if ( field.value.length > testValue ) {
							field.errorMsg += validationSet[option]['errorMsg'].replace(/{x}/, testValue);
							isValid = false;
						}
					}
				break;
				case 'minLength':
					if ( field.value ) {
						if ( field.value.length < testValue ) {
							field.errorMsg += validationSet[option]['errorMsg'].replace(/{x}/, testValue);
							isValid = false;
						}
					}
				break;
				default:
				  if ( !field.value.match(validationSet[option]['regexp']) ) {
						field.errorMsg += validationSet[option]['errorMsg'];
						isValid = false;
					}
				break;
			}
		}
		return isValid;
	},

	setError: function( refElement ) {
		if ( !refElement.onError ) {
			var errorImg = Builder.node( 'img', {id: refElement.id + '_errorImg', src: this.options.pathToImgs + 'error.png', className: 'onErrorImg', style: 'position: relative;'} );
			refElement.parentNode.appendChild(errorImg);

			var posX = this.options.imgErrEcartX;
			var posY = this.options.imgErrEcartY;
			errorImg.style.left = posX + 'px';
			errorImg.style.top = posY + 'px';

			Event.observe( errorImg, 'mouseover', this.showError.bindAsEventListener(this), false);
			Event.observe( errorImg, 'mouseout', this.hideError.bindAsEventListener(this), false);
		} else var errorImg = $(refElement.id + '_errorImg');
		errorImg.errorMsg = refElement.errorMsg;
	},

	showError: function(e) {
    var element = window.event ? window.event.srcElement : e ? e.target : null;
		if ( !element ) return;

		this.errorBox.innerHTML = element.errorMsg;
		Element.show( this.errorBox );
	},

	hideError: function() {
		Element.hide( this.errorBox );
	},

	positionErrorBox: function( e ) {
		var posX = Event.pointerX(e) + this.options.ecartX;
		var posY = Event.pointerY(e) + this.options.ecartY;

		var farX = posX + this.errorBox.offsetWidth;
		var limitX = (document.documentElement.scrollLeft || document.body.scrollLeft || 0) + (window.innerWidth || screen.availWidth) - 25;
		var xOverlap = limitX - this.errorBox.offsetWidth;
		if (farX > limitX ) this.errorBox.style.left = xOverlap + 'px';
		else this.errorBox.style.left = posX + 'px';
		
		var farY = posY + this.errorBox.offsetHeight;
		var limitY = (document.documentElement.scrollTop || document.body.scrollTop || 0) + (window.innerHeight || (screen.availHeight-window.screenTop)) -10;
		var yOverlap = limitY - this.errorBox.offsetHeight;
		if (farY > limitY ) this.errorBox.style.top = yOverlap + 'px';
		else this.errorBox.style.top = posY + 'px';
	}

}