/*/////////////////////////////////////////////////////////////////////
Validation avancée des formulaires
http://openweb.eu.org/articles/validation_avancee
/////////////////////////////////////////////////////////////////////*/

function isNotEmpty(s) {
	return s.replace(/^\s+|\s+$/g,"")!="";
}

function isInt(s) {
	return isNotEmpty(s)?parseInt(s, 10)==s:true;
}

function hasClassName(oNode,className) {
	return ((" "+oNode.className+" ").indexOf(" "+className+" ")!=-1);
}

function controle_formulaire(nForm) {
	var bIsValide = true;
	/* booléen précisant l'état courant de la validation qui sera utilisé comme valeur de retour de la fonction.
	La validation devant s?arrêter à la première erreur de saisie, l?ensemble des contrôles ne sera exécuté
	que si cette variable à la valeur true.*/
	var aFormCtrlSchemes = [["isNotEmpty","Le champ \"%s\" doit être renseigné."], ["isInt","Le champ \"%s\" ne correspond pas à un entier valide."]];
	/*variable tableau contenant l?ensemble des classes devant déclencher un contrôle ainsi que les messages
	d?erreurs associés. Pour cet exercice nous allons limiter nos contrôles aux valeurs de champs :
	* non vide ; * représentant un entier.*/
	var cLabels = nForm.getElementsByTagName("label");
	/*Pour tester les différentes classes, nous allons avoir besoin de récupérer l?ensemble des labels grâce
	à la méthode getElementsByTagName. Cette méthode définie par le DOM permet de récupérer au sein d?un objet
	tous les éléments dont le nom correspond à la chaîne passée en paramètre.
	Cette méthode retourne la liste des éléments concernés sous forme de tableau javascript.*/
	var nField;
	/*L?objectif étant de contrôler les données saisies, nous allons avoir besoin d?une variable faisant
	référence au champ de formulaire en cours de validation pour récupérer sa valeur. Pour lever toutes ambiguïtés
	quant à l?origine de cette variable, nous allons la déclarer au niveau local en début de fonction.*/
	for (var i=0; bIsValide && i<cLabels.length; i++) {
	/*boucle "for" sur les tout les éléments label pour vérifier qu?ils soient correctement associés à un champ,
	c?est à dire qu?il est possible de récupérer un objet en utilisant la méthode getElementById*/
		if ((cLabels[i].htmlFor=="") || !(nField=document.getElementById(cLabels[i].htmlFor))) continue;
		/*verification de la valeur de l'attribut for de l'élément label;
		Si cette valeur n?existe pas ou que la méthode getElementById retourne la valeur null
		(c?est à dire qu?il n?existe pas d?objet avec l?id spécifié) nous passerons à l?élément suivant
		de la boucle à l?aide du mot clef "continue". "for" correspondant à un mot réservé dans de nombreux
		langages dont javascript, la récupération de la valeur de cet attribut est réalisée en accédant à la propriété "htmlFor".*/
		for (var j=0; bIsValide && aFormCtrlSchemes[j]; j++) {
		//pour chaque label associé à une valeur, et pour chaque classe declenchant un controle (tableau créé plus haut)
			if (hasClassName(cLabels[i],aFormCtrlSchemes[j][0])) {
			//test utilisé pour déterminer si une classe particulière est implémentée via la fonction hasClassName créee plus haut
				if (!eval(aFormCtrlSchemes[j][0]+"(nField.value)")) {
				/*Dans le cas où une classe prédéfinie est spécifiée au sein de l?élément label, il reste
				à exécuter la fonction de même nom en lui passant en paramètre la valeur du champ à contrôler.
				La méthode utilisée consiste à faire évaluer par le moteur javascript du navigateur - et
				grâce à la méthode eval - une chaîne dont la valeur sera interprétée comme un élément de script.*/
					bIsValide = false;
					//si controle non vérifié, envoie de retour avec false 
					var textContent = getTextContent(cLabels[i]).replace(/\s{2,}/g," ");
					/*puis récupération du contenu textuel du label en erreur via la fonction getTextContent()
					il est possible que la valeur de la variable textContent inclue les sauts de ligne et
					les espaces multiples présents dans la source HTML. Nous allons remplacer ces éléments
					disgracieux par un simple espace en utilisant une expression régulière.*/
					textContent = textContent.replace(/^[\s:*]+|[\s:*]+$/g,"");
					/*nettoyage des ":", "espaces" et "*" */
					alert(aFormCtrlSchemes[j][1].replace("%s",textContent));
					/*generation de la boite de dialogue en remplaçant la séquence "%s" du message générique
					par la valeur de la variable textContent.*/
				}
			}
		}
	}
	if (!bIsValide) {
		nField.focus();
	}
	return bIsValide;
}

function getTextContent(oNode) {
/*fonction récursive permettant de recupérer le contenu textuel d'un élément*/
	if (typeof(oNode.textContent)!="undefined") {
		return oNode.textContent;
	}
	/*Pour commencer, la fonction vérifie si le client implémente la propriété "textContent" en vérifiant que le type de
	la valeur retournée est différent de "undefined". Si tel est le cas, la fonction retourne directement la valeur de
	cette propriété*/
	switch (oNode.nodeType) {
		case 3: // TEXT_NODE
		case 4: // CDATA_SECTION_NODE
			return oNode.nodeValue;
			break;
		case 8: // COMMENT_NODE
		case 7: // PROCESSING_INSTRUCTION_NODE
			if (getTextContent.caller!=getTextContent) {
				return oNode.nodeValue;
			}
			break;
		case 9: // DOCUMENT_NODE
		case 10: // DOCUMENT_TYPE_NODE
		case 12: // NOTATION_NODE
			return null;
			break;
	}
	var _textContent = "";
	oNode = oNode.firstChild;
	while (oNode) {
		_textContent += getTextContent(oNode);
		oNode = oNode.nextSibling;
	}
	return _textContent;
	/*Dans le cas contraire et pour rester en accord avec la propriété textContent, la fonction va avoir un comportement
	spécifique au type du noeud (oNode.nodeType) reçus en paramètre. Pour un noeud de type texte (TEXT_NODE) ou d?une
	section CDATA (CDATA_SECTION_NODE) la fonction va retourner la valeur du noeud. Pour un noeud de type commentaire
	(COMMENT_NODE) ou instruction de traitement (PROCESSING_INSTRUCTION_NODE) la fonction retourne la valeur du noeud
	si et seulement si la fonction qui a appelé getTextContent (getTextContent.caller) ne correspond pas à getTextContent
	(c?est à dire que nous ne somme pas dans le cas d?une boucle récursive). En effet, selon la spécification DOM la
	propriété textContent ne doit pas prendre en compte ces noeuds lorsque la récupération du contenu textuel se fait
	initialement sur un autre type de noeud - par exemple un élément. Dans le dernier cas d?un noeud de type document
	(DOCUMENT_NODE), doctype (DOCUMENT_TYPE_NODE) et notation (NOTATION_NODE), la fonction retourne la valeur null.
	Après les traitements spécifiques à certains types de noeud, la fonction récupère le premier enfant du noeud
	(oNode.firstChild) passé en paramètre. Si cet enfant existe, la fonction concatène le retour de getTextContent
	appliquée récursivement à cet enfant et aux noeuds qui le suivent (oNode.nextSibling).*/
}


