/*************************************
 * Outils de validation de formulaires
 *************************************
 * V1.5
 *************************************
 * Fonctions publiques :
 * vldForm(frm)
 *   Valide les champs du formulaire frm. 
 * vldField(obj)
 *   Valide un champs de formulaire.
 * vldValue(test, value)
 *   Valide une valeur en fonction d'un test
 *************************************
 * Utilisation :
 *  Ajouter onsubmit='return vldForm(this)' à la déclaration du formulaire
 *  Ajouter aux elements de formulaire à valider une propriété "validation".
 *  Cette propriété doit être une chaine JSON, composée de :
 *   - required: booléen. (optionnel)
 *     Indique si le champs est obligatoire.
 *   - minlength : entier (optionnel) : indique le nombre minimum de caracteres de la valeur
 *   - maxlength : entier (optionnel) : indique le nombre maximum de caracteres de la valeur
 *   - test: chaine (optionnel)
 *     détermine le test à effectuer sur la valeur du champs
 *     Les valeurs possibles sont (cf: fonction vldValue) :
 *       - notempty: non vide
 *       - alphanumeric: lettre ou chiffre
 *       - domaine: un nom de domaine (on d'hote)
 *       - domaine-no-tld : une nom de domaine ou d'hote sans la partie tld (www.host)
 *       - domaine-element : un element de nom de domaine (entre 2 points)
 *       - tld : un tld (=> a améliorer)
 *       - email: une adresse email
 *       - numeric: un nombre entier
 *       - snumeric: un entier signé (signe + ou moins optionnel)
 *       - directory: une chaine pouvant être un nom de répertoire "sur" (pas d'espaces, de & etc.)
 *       - path: un chemin (format UNIX)
 *       - mailpassword : mot de passe email (compris entre 5 et 15 caractères)
 *   - callback: chaine (optionnel)
 *     Nom d'une fonction appelée après la validation du champs.
 *     Cette fonction reçoit 2 paramêtres :
 *      - obj: objet testé
 *      - result: booléen résultat de la validation
 *   - errmsg: chaine (optionnel)
 *     En cas de validation incorrecte, affiche une boite d'alerte contenant cette chaine
 * Il est possible de modifier le comportement global du validateur :
 *  Ajouter une propriété "validation" à la daclaration du formulaire.
 *  Cette propriété doit être une chaine JSON, composée de :
 *   - all : booleen. (optionnel, defaut true)
 *     Indique si le traitement doit continuer (true) ou stopper (false) si un champ invalide est trouvé
 *   - callback : chaine. (optionnel, default vide)
 *     Nom d'une fonction appelée après validation de chaque champ, si le champ n'indique pas de fonction de callback 
 *   - focus : booleen (defaut, true)
 *     Indique si le premier champ détecté comme faux doit recevoir le focus
 **************************************************
 * exemple d'utilisation :
 * function v(obj, r) {
 *  if( r ) alert('Le champs '+r.id+' est valide');
 *  else alert('Le champs '+r.id+' n'est pas valide');
 * }
 * <form onValidate="vldForm(this, true);">
 * <input type="text" name="votre_email" validation="{required: true; test: 'email'; callback: 'v'}">
 **************************************************
 * Historique :
 *   V1.0 : R��criture totale d'apr�s la fonction ValidForm
 *   V1.1 : Ajout d'une m�thode de callback par champ ou globale au formulaire
 *   V1.2 : Ajout de tests, utilisation de la valeur de retour de la fonction callback
 *   V1.3 : Optimisation, la fonction de callback est pass�e en tant que "pointeur de fonction"
 *   V1.4 : Ajout du test regexp
 *   V1.5 : Ajoute des tests tld, domaine-element, domaine-no-tld. Correction du test domaine.
 */

// Fonction vldForm
// Fonction publique
// Valide un formulaire en validant tous les champs un par un
// Si un seul champ est faux, le formulaire n'est pas valid�
// La validation peut �tre arr�t�e au premier champ faux trouv�
// frm : l'objet formulaire
// Renvoie : booleen
function vldForm(frm) {
	var v,errors=0, validator=frm.getAttribute('validation');
	try { v=vldGetFormValidation(frm); }
	catch(err) { alert(err); return false; }
	for (i=0; i<frm.elements.length; i++) {
		var obj = frm.elements[i];
		if( ! vldField(obj) ) {
			if(!errors++ && v.focus) obj.focus();
			if(!v.all) return false;
		}
	}
	return (errors==0);
}

// Fonction vdlField
// Fonction publique
// Valide un champ de formulaire en fonction des contraintes définies pour celui-ci
// Lance le traitement en fonction du résultat (vldFieldIs)
function vldField(obj) {
	// Creation de l'objet de validation du champ
	try { var v=vldGetFieldValidation(obj); }
	catch(err) { alert(err); return false; }
	var f=obj.form;
	// Si un callback est défini au niveau du formulaire et qu'il n'y en a pas au niveau du champ
	// On utilise celui du formulaire
	if( f!=undefined || f!=null ) {
		var vf;
		try { vf=vldGetFormValidation(f); }
		catch(err) { alert(err); return false; }
		if( vf!=null ) {
			if( v.callback==undefined || v.callback==null )
				v.callback=vf.callback;
		}
	}

	var value=obj.value;
	// Si le champ est vide, pas de test à effectuer :
	// le seul cas ou il est faux, c'est qu'il est obligatoire
	if( value.length==0 )
		return vldFieldIs(obj, v, !v.required);

	// Qu'il soit obligatoire ou pas n'importe plus: 
	// Il n'est pas vide, il faut le controler
	
	// Pas de test à effectuer : terminé
	if( v.test==undefined || v.test==null || v.test=='' ) return true;
	
	if( v.minlength!=undefined && v.minlength!=null && v.minlength!=-1 )
		if( value.length<v.minlength )
			return vldFieldIs(obj, v, false);
	if( v.maxlength!=undefined && v.maxlength!=null && v.maxlength!=-1 )
		if( value.length>v.maxlength )
			return vldFieldIs(obj, v, false);
	if( v.test=='regexp' ) {
		if( v.regexp==undefined || v.regexp==null || typeof(v.regexp)!='object' || !(v.regexp instanceof RegExp) ) {
			alert('vldField: regexp incorrecte "'+obj.id+'"');
			return false;
		}
		return vldFieldIs(obj, v, v.regexp.test(value));
	}

	return vldFieldIs(obj, v, vldValue(v.test, value));
	
	/*if( !vldValue(v.test, value) )
		return vldFieldIs(obj, v, false);
	return vldFieldIs(obj, v, true);*/
}

// Fonction vldGetFieldValidation
// Fonction interne
// Renvoie un objet de validation pour un champ de formulaire
function vldGetFieldValidation(obj) {
	function _vdlFieldValidation() {
		this.required=false;
		this.test='notest';
		this.minlength=-1;
		this.maxlength=-1;
		this.errmsg='';
		this.callback=null;
	}
	var v, validator=obj.getAttribute('validation');
	if( validator==undefined || validator==null || validator=='' ) return new _vdlFieldValidation();
	try { v=eval('('+validator+')'); }
	catch(err) {
		throw 'vldField: validateur incorrect pour "'+obj.id+'"\n'+err.message;
		//alert('vldField: validateur incorrect pour "'+obj.id+'"\n'+err.message);
	//	return false;
	}
	if( v==undefined || v==null ) {
		throw 'vldField: validateur incorrect pour "'+obj.id+'"';
		//alert('vldField: validateur incorrect pour "'+obj.id+'"');
		//return false;
	}
	if( v.required==undefined||v.required==null) v.required=false;
	if( v.test==undefined||v.test==null) v.test='notest';
	if( v.minlength==undefined||v.minlength==null) v.minlength=-1;
	if( v.maxlength==undefined||v.maxlength==null) v.maxlength=-1;
	if( v.errmsg==undefined||v.errmsg==null) v.errmsg='';
	if( v.callback==undefined||v.callback==null) v.callback=null;
	return v;
}

// Function vldGetFormValidation
// Fonction interne
// Renvoie un objet de validation du formulaire
function vldGetFormValidation(frm) {
	function _vldFormValidator() {
		this.all=true;
		this.focus=true;
		this.callback=null;
	}

	var v, validator=frm.getAttribute('validation');
	if( validator==undefined || validator==null || validator=='' ) return new _vldFormValidator();
	try { v=eval('('+validator+')'); }
	catch(err) {
		throw 'Format de validateur de formulaire incorrect\n'+err.message;
		//alert('Format de validateur de formulaire incorrect\n'+err.message);
		//return null;
	}
	if( v==undefined || v==null ) {
		throw 'Format de validateur de formulaire incorrect';
		//alert('Format de validateur de formulaire incorrect\n'+err.message);
		//return null;
	}
	if(v.all==undefined || v.all==null) v.all=true;
	if( v.callback==undefined||v.callback==null) v.callback=null;
	if( v.focus==undefined||v.focus==null) v.focus=true;
	return v;
}

// Fonction vldFieldIs
// Fonction interne
// Effectue les actions demand�es apr�s validation du champs :
//   - affiche une boite de dialogue si la validation est incorrecte, et si demand�
//   - apelle la fonction de callback si d�finie
// obj : l'objet champs de formualire test�
// v   : l'objet validateur
// isValid : booleen indiquant le r�sultat de la validation d�j� effectu�e
// Retourne : booleen : le r�sultat final de la validation
function vldFieldIs(obj, v, isValid) {
	var str, retval=isValid;
	if( !isValid && v.errmsg!=undefined && v.errmsg!=null && v.errmsg!='') alert(v.errmsg);
	if( (v.callback!=undefined) && (v.callback!=null) ) {
		try { retval=v.callback(obj, isValid); }
		catch(err) {
			alert('vldFieldIs: callback incorrect pour  "'+obj.id+'"\n'+err.description);
			return false;
		}
	}
	return retval;
}

// Fonction vldValue
// Indique si une valeur r�pond � un crit�re de test
// test : chaine indiquant le test � r�aliser
// value : valuer � tester
// Retourne : booleen :
//   true  : La valeur r�ponds au crit�re
//   false : devine...
function vldValue(test, value) {
	switch(test) {
		case 'notest':
			return true;
		case 'notempty':
			return (value.length!=0);
		case 'alphanumeric_':
		case 'alphanumeric':
			return /^[a-z0-9]+$/i.test(value);
		case 'domain':
		case 'domaine':
			var elements=value.split(/\./);
			if( elements.length<2 )
				return false;
			if( ! vldValue('tld', elements[elements.length-1]) )
				return false;
			for( var i=0; i<elements.length-1 ; i++ ){
				if( !vldValue('domain-element', elements[i] ) )
					return false;
			}
			return true;
		case 'tld':
			return /^[a-z]{2,}$/i.test(value);
		case 'domain-no-tld':
		case 'domaine-no-tld':
			var elements=value.split(/\./);
			if( elements.length<1 )
				return false;
			for( var i=0; i<elements.length ; i++ ){
				if( !vldValue('domain-element', elements[i] ) )
					return false;
			}
			return true;
		case 'domain-element':
		case 'domaine-element':
			// un element de nom de domaine (entre deux .) :
			//  - ne contient pas deux - qui se suivent
			//  - ne commence pas par -
			//  - ne se termine pas par - 
			if( /--/.test(value) || /^-/.test(value) || /-$/.test(value) )
				return false;
			//  - ne contient que des lettres, chiffres, et -
			return /^[0-9a-z-]+$/.test(value);
		case 'email':
		case 'e-mail':
		case 'mail':
			var tmail=value.split(/@/);
			if( tmail==null || tmail.length!=2 ) return false;
			if( ! vldValue('domain', tmail[1]) ) return false;
			return /^[a-z0-9\.\-\+_]+$/i.test(tmail[0]);
		case 'numeric':
			return /^[0-9]+$/.test(value);
		case 'snumeric':
			return /^[\-\+]?[0-9]+$/.test(value);
		case 'money':
			return /^[0-9]+[\.,]?[0-9]*$/.test(value);
		case 'directory':
			return /^[a-z0-9\-\._]$/i.test(value);
		case 'path':
			var t=value.split(/\//);
			if( t==null || t.length==0 ) return false;
			for(var i=0; i<t.length; i++) {
				if( ! vldValue('directory', t[i]) )
					return false;
			}
			return true;
		case 'mailpassword':
			if(value.length<5 || value.length>15)
				return false;
			return true;
		default:
			alert('vldValue : test inconnu "'+test+'"');
	}
	return false;
}

