var FC_NO_ITEM_NOT_FOUND_ERROR = 1;
var FC_NO_RULE_ERROR = 2;
var g_boolFormAlreadySubmitted = false;
/**
 * Constructeur du vérificateur de formulaire
 *
 * @param strFormName	Nom du formulaire à vérifier dans la page HTML.
 */
function FormChecker( p_refForm )
{
	// On garde une trace du nom de formulaire.
	this.l_refForm = p_refForm;

	// Liste des éléments du formulaire à vérifier
	this.formItems = new Array();

	// Déclaration des fonctions.
	this.addRule = addRule;
	this.checkRules = checkRules;
	this.displayRules = displayRules;
}

/**
 * Constructeur d'objet décrivant une réglè associée à un élément de formulaire
 *
 * @param p_strName			Nom de l'élément du formulaire pour lequel on ajoute une
 *							régle de vérification.
 * @param p_strRule			Nom de la règle à appliquer.
 * @param p_strParameter	Paramètre de la règle.
 * @param p_strMessage		Message d'erreur à afficher si la règle n'est pas
 * 							vérifiée.
 */
function formItem( p_strName, p_strRule, p_strParameter, p_strMessage )
{
	this.name = p_strName;
	this.rule = p_strRule;
	this.parameter = p_strParameter;
	this.message = p_strMessage;
}

/**
 * Ajoute une règle de vérification à un élément de formulaire.
 *
 * @param p_strItemName	Nom de l'élément de formulaire auquel la règle doit être
 * 						appliquée.
 * @param p_strRuleName	Nom de la règle à vérifier.
 * @param p_strMessage Message à afficher si la règle n'est pas vérifiée.
 * @param arguments[3]	(optionnel) Paramètres de la règle appliquée  ou
 * 						Vérifications à désactiver lors de l'ajout de la règle.
 * @param arguments[4]	(optionnel) Vérifications à désactiver lors de l'ajout
 * 						de la règle.
 */
function addRule( p_strItemName, p_strRuleName, p_strMessage )
{
	var l_strParameter = "";
	var l_intChecks    = 0;

	// Y a-t-il des vérifications à désactiver ?
	if( typeof arguments[3] == "number" )
		l_intChecks = arguments[3];
	else if( typeof arguments[4] == "number" )
		l_intChecks = arguments[4];

	// Est-que l'élément du formulaire existe ?
	if( !this.l_refForm[p_strItemName] )
	{
		if( (l_intChecks & FC_NO_ITEM_NOT_FOUND_ERROR) == FC_NO_ITEM_NOT_FOUND_ERROR ) return;
		return alert( "Élément de formulaire inconnu " + p_strItemName + "." );
	}

	// Est-ce que la règle existe ?
	if( !eval( "window." + p_strRuleName ) )
	{
		if( (l_intChecks & FC_NO_RULE_ERROR) == FC_NO_RULE_ERROR ) return;
		return alert( "Règle inconnue " + p_strRuleName + "." );
	}

	// Y a-t-il des paramètres à passer à la règle ?
	if( typeof arguments[3] == "string" )
		l_strParameter = arguments[3];
	
	this.formItems[ this.formItems.length ] = new formItem( p_strItemName, p_strRuleName, l_strParameter, p_strMessage );
}

/**
 * Lance la vérification de toutes les règles déclarées dans le vérificateur de
 * formulaire.
 */
function checkRules()
{
	/*if ( g_boolFormAlreadySubmitted ) {
		return false;
	}*/
	var l_intCompteur;
	var l_boolRuleChecked;
	var l_refFormItem;

	for( l_intCompteur=0; l_intCompteur < this.formItems.length; l_intCompteur++ )
	{
		l_refFormItem = this.formItems[l_intCompteur];
		l_boolRuleChecked = eval( l_refFormItem.rule )( this.l_refForm[l_refFormItem.name], l_refFormItem.parameter );
		if( !l_boolRuleChecked )
		{
			alert( l_refFormItem.message );
			focusElement( this.l_refForm[l_refFormItem.name] );
			return false;
		}
	}
	//g_boolFormAlreadySubmitted = true;
	return true;
}
function focusElement ( p_objElement ) {
	if ( typeof( p_objElement.length ) == "undefined" ) {
		p_objElement.focus();
	} else {
		p_objElement[0].focus();
	}
}
/**
 * Affiche l'ensemble des régles déclarées dans le vérificateur de formulaire.
 */
function displayRules()
{
	var l_intCompteur;
	var l_strRules = "";
	for( l_intCompteur=0; l_intCompteur < this.formItems.length; l_intCompteur++ )
		l_strRules += "Nom : " + this.formItems[l_intCompteur].name +
			"\nRègle : " + this.formItems[l_intCompteur].rule +
			"\nParamètre : " + this.formItems[l_intCompteur].parameter +
			"\nMessage : " + this.formItems[l_intCompteur].message + "\n\n";
		alert( "Rules :\n" + l_strRules );
}

/********************/
/*      REGLES      */
/********************/

/**
 * Vérifie si la propriété "value" de l'élément de formulaire est vide.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_empty( p_refFormItem )
{
	return p_refFormItem.value == "" ? false : true;
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire est vide après
 * avoir supprimé les espaces .
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_emptytrim( p_refFormItem )
{
	var l_strTrimed = p_refFormItem.value.replace( /^\s*/, "" );
	l_strTrimed = l_strTrimed.replace( /\s*$/, "" );
	return l_strTrimed == "" ? false : true;
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire comprend
 * exactement p_intSize caractères.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_intSize	Nombre de caractères que doit contenir la valeur
 * de cet élément.
 */
function chkform_size( p_refFormItem, p_intSize )
{
	return (p_refFormItem.value.length == p_intSize);
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire comprend au moins
 * p_intMinSize caractères.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_intMinSize	Nombre minimum de caractères que doit contenir la valeur
 * de cet élément.
 */
function chkform_minsize( p_refFormItem, p_intMinSize )
{
	return (p_refFormItem.value.length >= p_intMinSize);
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire comprend au plus
 * p_intMaxSize caractères.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_intMaxSize	Nombre maximum de caractères que doit contenir la valeur
 * de cet élément.
 */
function chkform_maxsize( p_refFormItem, p_intMaxSize )
{
	return p_refFormItem.value == null ? true : p_refFormItem.value.length <= p_intMaxSize;
}


/**
 * Vérifie si la propriété "value" de l'élément de formulaire est conforme au
 * modèle donné.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_strModele	Modèle auquel doit se conformer la valeur de l'élément.
 */
function chkform_match( p_refFormItem, p_strModele )
{
	return new RegExp( p_strModele ).test( p_refFormItem.value ); }

/**
 * Vérifie si la valeur de l'élément de formulaire est une adresse email valide.
 * Cette fonction provient du site http://javascript.internet.com
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_email( p_refFormItem )
{
	/* Le modèle suivant est utilisé pour vérifier si l'adresse email entrée
	 * vérifie le format utilisateur@domaine. Il est également utilisé pour
	 * séparer le nom utilisateur du domaine. */
	var emailPat = /^(.+)@(.+)$/;

	/* La chaîne de caractères suivantes représente le modèle pour trouver tous
	 * les caractères spéciaux. Nous ne voulons pas autoriser les caractères
	 * spéciaux dans l'adresse. Ces caractères comprennent
	   ( ) < > @ , ; : \ " . [ ]    */
	var specialChars = "\\(\\)<>@,;:\\\\\\\"\\.\\[\\]";

	/* La chaîne de caractères suivantes représentes l'ensemble des caractères
	 * autorisés dans le nom de l'utilisateur et le nom de domaine. Elle
	 * correspond en réalité aux caractères qui ne sont pas admis. */
	var validChars = "\[^\\s" + specialChars + "\]";

	/* Le modèle suivant s'applique si l'utilisateur est une chaîne de
	 * caractères entre guillemets (auquel cas, il n'y a aucune règle sur les
	 * caractères admis et ceux qui ne le sont pas : tous correspondent). Par
	 * exemple "jiminy cricket"@disney.com est une adresse email légale. */
	var quotedUser = "(\"[^\"]*\")";

	/* Le modèle suivant s'applique pour les domaines qui sont des adresses IP,
	 * au lieu de noms symboliques. Par exemple, joe@[123.124.233.4] est une
	 * adresse email légale. NOTE : Les crochets sont obligatoires. */
	var ipDomainPat = /^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;

	/* La chaîne de caractères suivante représente un atome ( basiquement une
	 * série de caractères non spéciaux.) */
	var atom = validChars + '+';

	/* La chaîne de caractères suivante représente un mot dans un nom
	 * d'utilisateur caractéristique. Par exemple, dans
	 * john.doe@quelquepart.com, john et doe sont des mots. Typiquement, un
	 * mot est soit un atome ou une chaîne de caractères entre guillemets. */
	var word = "(" + atom + "|" + quotedUser + ")";
	
	// Le modèle suivant décrit la structure de l'utilisateur
	var userPat = new RegExp("^" + word + "(\\." + word + ")*$");

	/* Le modèle suivant décrit la structure d'un domaine sumbolique normal, par
	 * opposition à ipDomainPat, montré précédemment. */
	var domainPat = new RegExp("^" + atom + "(\\." + atom +")*$");


	/* Pour finir, essayons de voir si l'adresse email fournie est valide. */

	/* Commençons avec le gros modèle pour simplement casser utilisateur@domaine
	 * en différents morceaux qui sont plus faciles à analyser. */
	var matchArray = p_refFormItem.value.match( emailPat );
	/* Trop ou pas assez de caractères @ ou autre; Concrétement, cette adresse
	 * ne correspond même pas au moule général d'une adresse email valide. */
	if( matchArray == null )
		return false

	var user=matchArray[1];
	var domain=matchArray[2];

	// Regardons si l'utilisateur est valide
	if( user.match(userPat) == null )
		return false;

	/* si l'adresse email est à une adresse IP (par opposition à un nom d'hôte
	 * symbolique), soyons sûr que l'adresse IP soit valide. */
	var IPArray=domain.match(ipDomainPat);
	if( IPArray != null )
	{
		// Il s'agit d'une adresse IP
		for( var i=1; i<=4; i++ )
			if( IPArray[i] > 255 )
				return false;
		return true
	}

	// Le domaine est un nom symbolique
	var domainArray = domain.match(domainPat);
	if( domainArray == null )
		return false;

	/* le nom de domaine semble valide, mais maintenant soyons sûr qu'il se
	 * termine par un mot de 3 lettres (comme com, edu, gov) ou un mot de 2
	 * lettres représentant un pays (fr, nl), et qu'il y a un nom d'hôte
	 * précédant le domaine ou le pays. */

	/* Maintenant, nous avons besoin de découper le domaine pour obtenir le
	 * nombre d'atomes qui le composent. */
	var atomPat=new RegExp(atom,"g");
	var domArr=domain.match(atomPat);
	var len=domArr.length;
	// l'adresse doit se terminer avec un mot de 2, 3 ou 4 lettres.
	if( domArr[domArr.length-1].length < 2 || 
		domArr[domArr.length-1].length > 4 )
		return false;

	// Soyons sûr qu'il y ait un nom d'hôte avant le domaine.
	if( len < 2 )
		return false;

	// Si nous arrivons aussi loin, c'est que tout est valide !
	return true;
}

/**
 * Vérifie si la valeur de l'élément de formulaire est une adresse IP valide.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_ip( p_refFormItem )
{
	var l_strIPValue = p_refFormItem.value;
	var l_regIP = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
	var l_objIPArray = l_strIPValue.match( l_regIP ); 

	if( l_strIPValue == "0.0.0.0" || l_strIPValue == "255.255.255.255" || l_objIPArray == null )
		return false;
	else
	{
		var l_strSegment;
		for( i = 0; i < 4; i++ )
		{
			l_strSegment = l_objIPArray[i];
			if( l_strSegment > 255 )
				return false;
		}
	}
	return true;
}

/**
 * Vérifie si la valeur de l'élément de formulaire est une date correspondant au
 * format fourni en paramètre.
 *
 * @param p_refFormItem		Référence de l'élément de formulaire à vérifier.
 * @param p_strDateFormat	Chaîne de caractères contenant le format de la date
 * à saisir. Les champs doivent être de la forme %xx, où xx représente l'un des
 * éléments ci-dessous.
 * Voici la liste des éléments supportés :
 * m (ou M) : numéro du mois, avec 1 ou 2 chiffres.
 * mm (ou MM) : numéro du mois, avec 2 chiffres strictement (i.e. Avril
 * correspond à 04).
 * d (ou D) : numéro du jour, avec 1 ou 2 chiffres.
 * dd (ou DD) : numéro du jour, avec 2 chiffres strictement.
 * y (ou Y) : année, avec 2 ou 4 chiffres.
 * yy (ou YY) : année, avec 2 chiffres strictement.
 * yyyy (ou YYYY) : année, avec 4 chiffres strictement.
 * mon : Nom du mois abrégé (Avril correspond à avr, Avr, AVR, etc.)
 * Mon : Nom du mois abrégé, mixed-case (i.e. Avril correspond à Avr
 * seulement).
 * MON : Nom du mois abrégé, en majuscule (i.e. Avril correspond à AVR seulement).
 * mon_strict : Nom du mois abrégé, en miniscule (i.e. AVril correspond à avr 
 * seulement).
 * month : Nom du mois complet (Avril correspond à avril, Avril, AVRIL, etc.)
 * Month : Nom du mois complet, mixed-case (i.e. Avril seulemnet).
 * MONTH : Nom du mois complet, en majuscule (i.e. AVRIL seulement).
 * month_strict : Nom du mois complet, en minuscule (i.e. avril seulement).
 * h (ou H) : heure, avec 1 ou 2 chiffres.
 * hh (ou HH) : heure, avec 2 chiffres strictement.
 * min (ou MIN): minutes, avec 1 ou 2 chiffres.
 * mins (ou MINS) : minutes, avec 2 chiffres strictement.
 * s (ou S) : secondes, avec 1 ou 2 chiffres.
 * ss (ou SS) : secondes, avec 2 chiffres strictement.
 * ampm (ou AMPM) : attribut am/pm. Les valeurs valides pour cet élément sont
 * am, pm, AM, PM, a.m., p.m., A.M., P.M.
 */
function chkform_date( p_refFormItem, p_strDateFormat )
{
	// Attention avec ce modèle. Les éléments longs doivent être placés avant
	// les éléments courts pour les distinguer les uns des autres. Par exemple,
	// analyser "mon_strict" doit correspondre à un éléments "mon_strict" et non
	// aux deux éléments "mon" et un littéral "_strict".

	var tokPat=new RegExp("^month_strict|month|Month|MONTH|yyyy|YYYY|mins|MINS|mon_strict|ampm|AMPM|mon|Mon|MON|min|MIN|dd|DD|mm|MM|yy|YY|hh|HH|ss|SS|m|M|d|D|y|Y|h|H|s|S");

	// lowerMonArr est utilisé pour associer les mois à leur valeur numérique.
	var lowerMonArr={jan:1, feb:2, mar:3, apr:4, may:5, jun:6, jul:7, aug:8, sep:9, oct:10, nov:11, dec:12}

	// monPatArr contient les expressions régulières pour trouver les mois
	// abrégés dans une date.
	var monPatArr=new Array();
	monPatArr['mon_strict']=new RegExp(/jan|fév|mar|avr|mai|jun|jui|aoû|sep|oct|nov|déc/);
	monPatArr['Mon']=new RegExp(/Jan|Fév|Mar|Avr|Mai|Jun|Jui|Aoû|Sep|Oct|Nov|Déc/);
	monPatArr['MON']=new RegExp(/JAN|FEV|MAR|AVR|MAI|JUN|JUI|AOU|SEP|OCT|NOV|DEC/);
	monPatArr['mon']=new RegExp("jan|fév|mar|avr|mai|jun|jui|aoû|sep|oct|nov|déc",'i');

	// monthPatArr contient les expressions régulières pour trouver les mois
	// complets dans une date.
	var monthPatArr=new Array();
	monthPatArr['month']=new RegExp(/^janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre/i);
	monthPatArr['Month']=new RegExp(/^Janvier|Février|Mars|Avril|Mai|Juin|Juillet|Août|Septembre|Octobre|Novembre|Décembre/);
	monthPatArr['MONTH']=new RegExp(/^JANVIER|FEVRIER|MARS|AVRIL|MAI|JUIN|JUILLET|AOUT|SEPTEMBRE|OCTOBRE|NOVEMBRE|DECEMBRE/);
	monthPatArr['month_strict']=new RegExp(/^janvier|février|mars|avril|mai|juin|juillet|août|septembre|octobre|novembre|décembre/);

	// cutoffYear est la charnière pour assigner "19" ou "20" au siècle. Toute
	// année à 2 chiffres >= à cette charnière aura pour siècle "19", et les
	// autres auront pour siècle "20".
	var cutoffYear=50;

	// FormatToken est un type de données pour stocker les éléments extraits
	// d'une date.
	function FormatToken( token, type )
	{
		this.token=token;
		this.type=type;
	}

	function parseFormatString( formatStr )
	{
		var tokArr=new Array;
		var tokInd=0;
		var strInd=0;
		var foundTok=0;

		while( strInd < formatStr.length )
		{
			if( formatStr.charAt( strInd ) == "%" &&
				( matchArray=formatStr.substr( strInd+1 ).match( tokPat ) ) != null)
			{
				strInd += matchArray[0].length + 1;
				tokArr[ tokInd++ ] = new FormatToken( matchArray[0],"symbolic" );
			}
			else
			{
				// Aucun élément ne correspond pour la position courante, donc
				// le caractère courant doit être considéré comme un littéral
				// requis.
				if( tokInd > 0 && tokArr[tokInd-1].type == "literal" )
				{
					// Les éléments littéraux peuvent être combinés. Il faut
					// juste ajouter le dernier élément.
					tokArr[ tokInd-1 ].token += formatStr.charAt( strInd++ );
				}
				else
				{
					tokArr[ tokInd++ ] = new FormatToken( formatStr.charAt( strInd++ ), "literal" );
				}
			}
		}
		return tokArr;
	}

	/**
	 * buildDate fait tout le travail. Elle prend une chaîne de caractères
	 * contenant une date et une autre spécifiant un format de date, essaye de
	 * faire correspondre les deux, et retourne un objet Date (avec la valeur de
	 * la chaîne de caractères contenant la date fournie). Si la chaîne de
	 * caractères contenant la date ne contient pas tous les champs qu'un objet
	 * Date requiert (par exemple, une date avec juste le mois), tous les champs
	 * non fournis sont définis par défaut avec les caractéristiques de la date
	 * courante. Les champs de temps sont mis à zéro par défaut. Ainsi, une date
	 * telle que "3/30/2000" dans le format "%mm/%dd/%yyyy" conduit à un objet
	 * Date correspondant à cette date à minuit.
	 * formatStr est une chaîne de caractères de forme libre qui indique les
	 * éléments spéciaux via le caractère %. Voici quelques exemples qui
	 * retourne un objet Date :
	 * 
	 * buildDate('3/30/2000','%mm/%dd/%y') // 30 mars 2000
	 * buildDate('March 30, 2000','%Mon %d, %y') // pareil.
	 * buildDate('Voici la date : 30-3-00','Voici la date : %dd-%m-%yy')
	 * 
	 * Si la chaîne de caractère de formattage ne correspond pas à la chaîne
	 * fournie, un message d'erreur est retournée. Ainsi, pour tester si
	 * buildDate a réussi la correspondance, l'appelant pour utiliser la
	 * commande "typeof" sur la valeur retournée. Par exemple, voici la fonction
	 * dateCheck, qui retourne true si la date donnée est valide, et false dans
	 * les autres cas (et reporte une erreur dans le cas où c'est false) :
	 * 
	 * function dateCheck( dateStr, formatStr )
	 * {
	 * 		var myObj = buildDate( dateStr, formatStr );
	 * 		if( typeof myObj == "object" )
	 * 		{
	 * 			// Nous obtenons un objet Date, donc c'est bon
	 * 			return true;
	 * 		}
	 * 		else
	 * 		{
	 * 			// Nous obtenons un message d'erreur
	 * 			alert(myObj);
	 * 			return false;
	 * 		}
	 * }
	 */
	function buildDate( dateStr, formatStr )
	{
		// Analyse de la chaîne de format d'abord.
		var tokArr = parseFormatString( formatStr );
		var strInd=0;
		var tokInd=0;
		var intMonth;
		var intDay;
		var intYear;
		var intHour;
		var intMin;
		var intSec;
		var ampm="";
		var strOffset;

		// Création d'un objet Date avec la date courante de telle manière que
		// si l'utilisateur fournit seulement un mois ou un jour, nous pouvons
		// quand même retourner une date valide.
		var curdate = new Date();
		intMonth = curdate.getMonth() + 1;
		intDay = curdate.getDate();
		intYear = curdate.getFullYear();

		// Heure par défaut à minuit, de telle manière que s'il n'y a que des
		// infos sur la date, on retourne un objet Date pour cette date à
		// minuit.
		intHour=0;
		intMin=0;
		intSec=0;

		// Analyse de dateStr, mise en correspondance de formatStr jusqu'à ce
		// qu'on trouve une disparité ou une correspondance totale.
		while( strInd < dateStr.length && tokInd < tokArr.length )
		{
			// Commençons avec le cas facile d'un littéral.
			if( tokArr[tokInd].type == "literal" )
			{
				if( dateStr.indexOf( tokArr[tokInd].token,strInd ) == strInd )
				{
					// La position courante dans la chaîne doit correspondre au
					// format.
					strInd += tokArr[ tokInd++ ].token.length;
					continue;
				}
				else
				{
					// Il y a une disparité : on retourne une erreur.
					return "\"" + dateStr + "\" does not conform to the expected format: " + formatStr;
				}
			}

			// Si on arrive ici, c'est que nous avons une correspondance avec un
			// élément symbolique.
			switch( tokArr[tokInd].token )
			{
				case 'm':
				case 'M':
				case 'd':
				case 'D':
				case 'h':
				case 'H':
				case 'min':
				case 'MIN':
				case 's':
				case 'S':

					// Extraction d'un ou deux caractères de la date et si c'est
					// un nombre, on le sauve comme mois, jour, heure ou minute
					// selon le cas.
					curChar = dateStr.charAt( strInd );
					nextChar = dateStr.charAt( strInd + 1 );
					matchArr = dateStr.substr( strInd ).match( /^\d{1,2}/ );
					if( matchArr == null )
					{
						// Le premier caractère n'est pas un nombre : il y a une
						// disparité entre le format et la date, donc on
						// retourne une erreur.
						switch( tokArr[tokInd].token.toLowerCase() )
						{
							case 'd': var unit="day"; break;
							case 'm': var unit="month"; break;
							case 'h': var unit="hour"; break;
							case 'min': var unit="minute"; break;
							case 's': var unit="second"; break;
						}
						return "Bad " + unit + " \"" + curChar + "\" or \"" + curChar +
							nextChar + "\".";
					}
					strOffset =matchArr[0].length;
					switch( tokArr[tokInd].token.toLowerCase() )
					{
						case 'd': intDay = parseInt( matchArr[0], 10 ); break;
						case 'm': intMonth = parseInt( matchArr[0], 10 ); break;
						case 'h': intHour = parseInt( matchArr[0], 10 ); break;
						case 'min': intMin = parseInt( matchArr[0], 10 ); break;
						case 's': intSec = parseInt( matchArr[0], 10 ); break;
					}
					break;
				case 'mm':
				case 'MM':
				case 'dd':
				case 'DD':
				case 'hh':
				case 'HH':
				case 'mins':
				case 'MINS':
				case 'ss':
				case 'SS':

					// Extraction de deux caractères de la date et si c'est un
					// nombre, on le sauve comme mois, jour, ou heure selon le
					// cas.
					strOffset = 2;
					matchArr = dateStr.substr( strInd ).match( /^\d{2}/ );
					if( matchArr == null )
					{
						// Le premier caractère n'est pas un nombre : il y a une
						// disparité entre le format et la date, donc on
						// retourne une erreur.
						switch( tokArr[tokInd].token.toLowerCase() )
						{
							case 'dd': var unit="day"; break;
							case 'mm': var unit="month"; break;
							case 'hh': var unit="hour"; break;
							case 'mins': var unit="minute"; break;
							case 'ss': var unit="second"; break;
						}
						return "Bad " + unit + " \"" + dateStr.substr(strInd,2) + 
							"\".";
					}
					switch (tokArr[tokInd].token.toLowerCase()) {
						case 'dd': intDay=parseInt(matchArr[0],10); break;
						case 'mm': intMonth=parseInt(matchArr[0],10); break;
						case 'hh': intHour=parseInt(matchArr[0],10); break;
						case 'mins': intMin=parseInt(matchArr[0],10); break;
						case 'ss': intSec=parseInt(matchArr[0],10); break;
					}
					break;
				case 'y':
				case 'Y':
					// Extraction de deux ou quatre caractères de la date et si
					// c'est un nombre, on le sauve comme année. Conversion des
					// années à deux chiffres en années à quatre chiffres en
					// assignant le siècle "19" si l'année est >= à la
					// charnière, et "20" sinon.
					if( dateStr.substr( strInd, 4 ).search( /\d{4}/ ) != -1 )
					{
						// Année à quatre chiffres.
						intYear = parseInt( dateStr.substr( strInd, 4 ), 10 );
						strOffset=4;
					}
					else
					{
						if( dateStr.substr( strInd, 2 ).search( /\d{2}/ ) != -1 )
						{
							// Année à deux chiffres.
							intYear =parseInt( dateStr.substr( strInd, 2 ), 10 );
							if( intYear >= cutoffYear )
							{
								intYear+=1900;
							}
							else
							{
								intYear+=2000;
							}
							strOffset=2;
						}
						else
						{
							// Mauvaise année ; retourne une erreur.
							return "Bad year \"" + dateStr.substr( strInd, 2 ) + 
								"\". Must be two or four digits.";
						}
					}
					break;
				case 'yy':
				case 'YY':
					// Extraction de quatre caractères de la date et si
					// c'est un nombre, on le sauve comme année. Conversion des
					// années à deux chiffres en années à quatre chiffres en
					// assignant le siècle "19" si l'année est >= à la
					// charnière, et "20" sinon.
					if( dateStr.substr( strInd, 2 ).search( /\d{2}/ ) != -1 )
					{
						// Année à deux chiffres.
						intYear = parseInt( dateStr.substr( strInd, 2 ), 10 );
						if( intYear >= cutoffYear )
						{
							intYear+=1900;
						}
						else
						{
							intYear+=2000;
						}
						strOffset=2;
					}
					else 
					{
						// Mauvaise année ; retourne une erreur.
						return "Bad year \"" + dateStr.substr(strInd,2) + 
							"\". Must be two digits.";
					}
					break;
				case 'yyyy':
				case 'YYYY':
					// Extraction de quatre caractères de la date et si c'est un
					// nombre, les sauve comme année.
					if( dateStr.substr( strInd, 4 ).search( /\d{4}/ ) != -1 )
					{
						// Année à quatre chiffres.
						intYear=parseInt(dateStr.substr(strInd,4),10);
						strOffset=4;
					}
					else
					{
						// Mauvaise année ; retourne une erreur.
						return "Bad year \"" + dateStr.substr(strInd,4) + 
							"\". Must be four digits.";
					}
					break;
				case 'mon':
				case 'Mon':
				case 'MON':
				case 'mon_strict':
					// Extraction de trois caractères de dateStr et analyse de
					// la casse selon le cas.
					monPat = monPatArr[ tokArr[ tokInd ].token ];
					if( dateStr.substr( strInd, 3 ).search( monPat ) != -1 )
					{
						intMonth = lowerMonArr[ dateStr.substr( strInd, 3 ).toLowerCase() ];
					}
					else
					{
						// Mauvais mois, Retourne une erreur.
						switch( tokArr[tokInd].token )
						{
							case 'mon_strict': caseStat = "lower-case"; break;
							case 'Mon': caseStat = "mixed-case"; break;
							case 'MON': caseStat = "upper-case"; break;
							case 'mon': caseStat = "entre Jan et Déc"; break;
						}
						return "Bad month \"" + dateStr.substr(strInd,3) + 
							"\". Must be " + caseStat + ".";
					}
					strOffset=3;
					break;
				case 'month':
				case 'Month':
				case 'MONTH':
				case 'month_strict':
					// Extraction d'un nom de mois complet à la position strInd
					// de dateStr si possible.
					monPat = monthPatArr[ tokArr[ tokInd ].token ];
					matchArray = dateStr.substr( strInd ).match( monPat );
					if( matchArray == null )
					{
						// Mauvais mois, Retourne une erreur.
						return "Can't find a month beginning at \"" +
							dateStr.substr(strInd) + "\".";
					}

					// C'est un bon mois.
					intMonth = lowerMonArr[ matchArray[0].substr( 0, 3 ).toLowerCase() ];
					strOffset = matchArray[0].length;
					break;
				case 'ampm':
				case 'AMPM':
					matchArr = dateStr.substr( strInd ).match( /^(am|pm|AM|PM|a\.m\.|p\.m\.|A\.M\.|P\.M\.)/ );
					if( matchArr == null )
					{
						// Il n'y a pas d'élément am/pm dans la chaîne de
						// caractères. Retourne un message d'erreur.
						return "Missing am/pm designation.";
					}

					// Stocke la valeur de am/pm pour plus tard ( juste sous la
					// forme am ou pm, pour rendre les choses plus faciles plus
					// tard).
					if( matchArr[0].substr( 0, 1 ).toLowerCase() == "a" )
					{
						// Il s'agit de am.
						ampm = "am";
					}
					else
					{
						ampm = "pm";
					}
					strOffset = matchArr[0].length;
					break;
			}
			strInd += strOffset;
			tokInd++;
		}
		if( tokInd != tokArr.length || strInd != dateStr.length )
		{
			/* Nous avons parcouru la totalité de la chaîne de caractères
			 * contenant la date ou le format, mais il reste des caractères dans
			 * l'autre chaîne, donc il y a une disparité. */
			return "\"" + dateStr + "\" is either missing desired information or has more information than the expected format: " + formatStr;
		}

		// Assurons-nous que tous les composants sont dans les bons intervalles.
		if( intMonth < 1 || intMonth > 12 )
		{
			return "Month must be between 1 and 12.";
		}
		if( intDay < 1 || intDay > 31 )
		{
			return "Day must be between 1 and 31.";
		}

		// Assurons-nous que l'utilisateur n'a pas mis 31 pour un mois qui n'a
		// que 30 jours.
		if ((intMonth == 4 || intMonth == 6 || intMonth == 9 || intMonth == 11) && intDay == 31) {
			return "Month "+intMonth+" doesn't have 31 days!";
		}

		// Vérification de la validité des dates pour le mois de février (y
		// compris les années bissextiles)
		if( intMonth == 2 )
		{
			// Est-ce que l'année de la date est bissextile ?
			// figure out if "year" is a leap year; don't forget that
			// century years are only leap years if divisible by 400
			var isleap=(intYear%4==0 && (intYear%100!=0 || intYear%400==0));
			if( intDay > 29 || (intDay == 29 && !isleap) )
			{
				return "February " + intYear + " doesn't have " + intDay + 
					" days!";
			}
		}

		// Vérifions que les heures sont entre 0 et 23 si am/pm n'est pas
		// fourni.
		if( ampm == "" )
		{
			if( intHour < 0 || intHour > 23 )
			{
				return "Hour must be between 0 and 23 for military time.";
			}
		}
		else
		{
			// Heure non militaire, donc elle doit être entre 1 et 12.
			if( intHour < 1|| intHour > 12 )
			{
				return "Hour must be between 1 and 12 for standard time.";
			}
		}

		// Si l'utilisateur a spécifié am ou pm, conversion de intHour au format
		// militaire.
		if( ampm == "am" && intHour == 12 )
		{
			intHour=0;
		}
		if( ampm == "pm" && intHour < 12 )
		{
			intHour += 12;
		}
		if( intMin < 0 || intMin > 59 )
		{
			return "Minute must be between 0 and 59.";
		}
		if( intSec < 0 || intSec > 59 )
		{
			return "Second must be between 0 and 59.";
		}
		return new Date( intYear, intMonth-1, intDay, intHour, intMin, intSec );
	}

	function dateCheck( dateStr, formatStr )
	{
		var myObj = buildDate( dateStr, formatStr );
		if( typeof myObj == "object" )
		{
			// Nous obtenons un objet Date, donc c'est bon
			return true;
		}
		else
		{
			// Nous obtenons un message d'erreur
			return false;
		}
	}

	return dateCheck( p_refFormItem.value, p_strDateFormat );
}

	/**
	 * Retourne le nombre de mots contenus dans une chaîne de caractères.
	 *
	 * @param p_strString Chaîne de caractères dont il faut déterminer le nombre de
	 * mots.
	 */
	function countWords( p_strString )
	{
		var char_count = p_strString.length;
		var fullStr = p_strString + " ";
		var initial_whitespace_rExp = /^[^A-Za-z0-9]+/gi;
		var left_trimmedStr = fullStr.replace(initial_whitespace_rExp, "");
		var non_alphanumerics_rExp = rExp = /[^A-Za-z0-9]+/gi;
		var cleanedStr = left_trimmedStr.replace(non_alphanumerics_rExp, " ");
		var splitString = cleanedStr.split(" ");
		var word_count = splitString.length -1;
		if (fullStr.length <2) {
			word_count = 0;
		}
		return word_count;
	}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire comprend
 * exactement p_intWords mots.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_intWords	Nombre de mots que doit contenir la valeur
 * de cet élément.
 */
function chkform_words( p_refFormItem, p_intWords )
{
	return countWords(p_refFormItem.value) == p_intWords;
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire comprend
 * au moins p_intMinWords mots.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_intWords	Nombre de mots que doit contenir la valeur
 * de cet élément.
 */
function chkform_minwords( p_refFormItem, p_intMinWords )
{
	return countWords(p_refFormItem.value) >= p_intMinWords;
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire comprend
 * au plus p_intWords mots.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 * @param p_intMaxWords	Nombre de mots que doit contenir la valeur
 * de cet élément.
 */
function chkform_maxwords( p_refFormItem, p_intMaxWords )
{
	return countWords(p_refFormItem.value) <= p_intMaxWords;
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire est un nombre
 * (entier ou flottant).
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_number( p_refFormItem )
{
	var number = Number( p_refFormItem.value );
	return !isNaN( number );
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire est un nombre
 * entier.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_int( p_refFormItem )
{
	var number = Number( p_refFormItem.value );
	if( !isNaN( number ) )
		return parseInt(""+number) == number;
	else
		return false;
}

/**
 * Vérifie si la propriété "value" de l'élément de formulaire est un nombre
 * flottant.
 *
 * @param p_refFormItem	Référence de l'élément de formulaire à vérifier.
 */
function chkform_float( p_refFormItem )
{
	var number = Number( p_refFormItem.value );
	if( !isNaN( number ) )
		return parseFloat(""+number) == number;
	else
		return false;
}

/**
 * Vérifie si un des boutons radios d'un même ensemble est coché.
 *
 * @param p_refFormItem	Référence d'un des boutons radio.
 */
function chkform_checked( p_refFormItem )
{
	var l_intCounter = p_refFormItem.length;
	while( l_intCounter )
		if( p_refFormItem[ --l_intCounter ].checked ) return true;
	return false;
}
