/*
** CLASS CheckForm
** Verson 0.3
** http://www.kurimara.net
** 
** @données membres : ######################################################
**	Configuration : ---------------------------------------------------------------------------------------------------------------
**		- msgElement 	@String		balise créée pour l'affichage du message
**		- debug		@Boolean 		mode debug
**		- validateColor	@String		Code couleur si champs valide   (ex #0FF ou #139AFF ou transparent)
**		- errorColor		@String		Code couleur si champs invalide (ex #0FF ou #139AFF ou transparent)
**		- msgAttributes	@String		Contient tous les attributs pour la personnalisation du message d'erreur
**
**	Class : -------------------------------------------------------------------------------------------------------------------------
**		- formulaire		@Element 		élément qui pointe sur le formulaire par défaut document
**		- c			@Array		contient les identifiants des champs à controler( id ou name)
**		- value		@Array		contient la valeur du champs à controler
**		- t			@Array		contient les types des champs à controler (text, textArea, select, 
**                                                        			textBox, textRadio, button etc ..)
**		- r			@Array		contient les règles de validation des champs à contrôler ( required,                 
**									requiredLenFix,alpha,alphaLenFix, alphanum,alphanumLenfix,
**									number, email, phone, url, digit, nodigit, verif , expReg etc )
**		- m			@Array		contient les messages à afficher si les champs à contrôler ne 
**									respectent pas les règles définies
**		- p			@Array		contient éventuellement des paramètres exemple:min[min,max]
**		- l			@Array		contient éventuellement des champs liés entre eux
**		- messageError	@Array		contenant tous les erreurs trouvées dans le formulaire indice n étant le numéro de chaque règle
**		- reg			@Array		contient toutes les regex possibles 
**		- authorized		@Array		contient tous les caractères possibles
**
** @fonctions :  ############################################################
**
**
**
**
**
*/

//@fid 	string (id du formulaire cible)
function CheckForm(fid) {
	this.fid = fid;
	this.setFormulaire(fid);
}//__construct

CheckForm.prototype = {
	
	/* ***Paramétrage de la classe*** */
	
	debug			: true,
	
	validateColor	: '#e5ff80',
	errorColor		: '#ff5555',
	msgElement		: 'li',
	
	//on peut ajouter autant d'attribut de balise qu'on le souhaite (title,src,title,...)
	msgAttributes	: {
				 'style' : 'color=#000;padding:2px;',
				 'class' : 'separator'
				//example pour afficher une image
				 //src:'./image.png'
				//il faut aussi mettre le msgElement à img plus haut dans le code
				 //msgElement		: 'img',
	},

	//données membres
	formulaire 		: document,    
	messageError 	: [],
	value			: [],
	c				: [],
	t				: [],
	r				: [],
	m				: [],
	p				: [],
	l				: [],
	e				: [],
	
	/* FEATURED
	authorized: {
		alpha : /^[a-z ._\-]*$/i,
		alphanum : /^[a-z0-9 ._\-]*$/i,
		digitSign : /^[\-+0-9]*$/,
		digit : /^[0-9]*$/,
		nodigit : /^[^0-9]*$/,
		number : /^[\d\.]*$/,
		email : /^[a-z0-9._%@\-]*$/i,
		url : /^[a-z0-9\-\.\/_]*$/i
	},
	*/
	
	reg:{
		required : /[^.*]/,
		alpha : /^[a-z ._\-]+$/i,
		alphanum : /^[a-z0-9 ._\-]+$/i,
		digitSign : /^[\-+]?[0-9]+$/,
		digit : /^[0-9]+$/,
		nodigit : /^[^0-9]+$/,
		number : /^[\-+]?\d*\.?\d+$/,
		email : /^[a-z0-9._%\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$/i,
		phone : /^[\d\s ().\-]+$/,
		url : /^(http|https):\/\/[a-z0-9\-\.\/_]+\.[a-z]{2,2}$/i,
		date : /^[0-9]{1,2}.[0-9]{1,2}.[0-9]{2,4}$/
		//le cas custom est créé dans addReg ou non en fonction des désir de l'utilisateur
	},
	
	
	/* ***** SET FUNCTIONS ****** */
	
	
	//@elem	element 	(élément dont il faut récupérer la valeur)
	//@n		number	(numéro de la règle sur lequel doit porter cet ajout)
	//desc 	initialise dans le tableau value la valeur du champs
	setValue : function (elem, n) {
		//prend la valeur du champs
		if (elem.value !== '') {
			this.value[n] = elem.value;
		}
	},
	
	//@fid	string (id of the form)
	//desc	initialise l'élément formulaire ave le fid
	setFormulaire: function (fid) { 
		this.formulaire = document.getElementById(fid);
		this.addEventListener(this.formulaire,"submit",this.bind(this,this.traiteSubmit),false);
	},
	
	//@bool	boolean (true = debug activated)
	//desc	initialise the debug mode
	setDebug: function (bool) { 
		this.debug = bool;
	},
	
	//@color	string (hexadecimal color (ex : #fff or #03A93A)
	//desc	initialise the validateColor
	setValidateColor: function (color) { 
		this.validateColor = color;
	},
	
	//@color	string (hexadecimal color (ex : #fff or #03A93A)
	//desc	initialise the errorColor
	setErrorColor: function (color) { 
		this.errorColor = color;
	},
	
	//@value	string (valeur du message)
	//desc 	initialise la donnée membre msgElement
	setMsgElement: function (value) {
		this.msgElement = value;
	},
	
	
	/* ***** GET FUNCTIONS ****** */

	//desc 	retourne le message d'erreur courant
	getMessageError: function() { 
		return this.messageError;
	},
	
	
	/* ***** EVENT FUNCTIONS ****** */
	
	traiteSubmit: function (e) {
		this.validateReg();
		if (this.messageError !== '' && (this.messageError.join('')).length !== 0) {
			var loc_e = e || window.event;
			if (loc_e.preventDefault) {
			   loc_e.preventDefault();  // FF
			}
			loc_e.returnValue = false;  // IE
			
			return false;
		}
	},
	
	/* ***** EXECUTION FUNCTIONS ****** */
	
	
	//@chp		identifiant du champ à contrôler (id ou name)
	//@typ		type du champ à contrôler
	//@reg		règle à appliquer sur le champ
	//@event		event qui déclenche la validation
	//@mes		message à afficher si la règle n'est pas vérifiée
	//@par		paramètre facultatif pouvant préciser la règle supplémentaire. (taille de la value)
	//@custom_reg	paramètre facultatif pouvant préciser la règex à utiliser.
	//@liens		paramètre facultatif pouvant préciser les champs liés
	//desc		remplit les 5 tableaux de description d'un champ
	//			permet d'enregistrer une nouvelle règle
	addReg: function (chp, typ, reg, event, mes, par, custom_reg, liens) {
		if ( this.$(chp) !== null )
		{
			//on passe à initialise le tableau d'argument de la fonction
			this.initialise(arguments);
			var att = this.c.length-1;
			this.addEventListener(this.$(chp),event,this.bind(this,this.validateReg,[att]),false);
			/* FEATURED
			if ( typeof(this.authorized[this.r[att]]) != 'undefined')
			{
				this.addEventListener(this.$(chp),'onpress',this.bind(this,this.traitePress,[att]),false);
			}
			*/
		}
		else { this.showError('Développeur : Attention l\'élément id = "' + chp + '" n\'existe pas.\n'
							+'l\'élément name = "' + chp + '" n\'existe pas non plus.');
		}
							
	},

	//desc 	cette fonction récupère un tableau de paramètre donné par addReg
	initialise: function() {
		this.c.push(arguments['0']['0']);
		this.t.push(arguments['0']['1']);
		this.r.push(arguments['0']['2']);
		this.e.push(arguments['0']['3']);
		this.m.push(arguments['0']['4']);
		this.p.push(arguments['0']['5']);
		//on ajoute à reg une regex custom
		var custom = arguments['0']['6'];
		if ( custom !== '' && custom != 'undefined' && typeof(custom) != 'undefined'){
			this.reg.custom = custom;
		}
		//on ajoute à un l les liens
		this.l.push((arguments['0']['7'] != 'undefined') ? arguments['0']['7'] : '') ;
		
	},

	//@param		le numéro des règles à valider
	//desc		Valide une ou plusieurs règles
	validateReg: function() {
		if (arguments.length !== 0)
		{
			for (var i = 0 , sz = arguments.length; i < sz ; ++i) {
				this.execute(arguments[i]);
			}
		}
		else
		{
			for (var n in this.t) {
				if(n != 'undefined') {
					this.execute(n);
				}
			}
		}
	},

	//@n 		number
	//desc 	execute les opération sur un champs
	execute: function (n) {
		
		if(isNaN(n)) return;
		
		this.setValue(this.$(this.c[n]),n);
		/* FEATURED
		if ( typeof(this.authorized[this.r[n]]) != 'undefined')
		{
			if (! this.authorized[this.r[n]].test(this.value[n][this.value.length-1])) {
				event.stop();
			}
		}*/
		var loc_reg = this.reg[this.r[n]];
		
		var isError = false;
		
		if (typeof(loc_reg) != 'undefined')
		{	
			if ( ! loc_reg.test(this.value[n])){ isError = true; }
		}
		if (typeof(this.p[n]) != 'undefined')
		{
			var length = (typeof(this.value[n]) == 'undefined') ? 0 : this.value[n].length;
			if ( length < this.p[n]['0'] || length > this.p[n]['1'] ) {
				isError = true;
			}
		}
		/* FEATURED
		if (typeof(this.t[n]) != 'undefined')
		{	
			if (this.t[n] == 'radio' && this.value[n].length > 1 ){
					isError = true;
			}
			else if (this.t[n] == 'checkbox')
			{
				if (typeof(this.p[n]) != 'undefined')
				{
					alert(this.value[n]);
					var length = (typeof(this.value[n]) == 'undefined') ? 0 : this.value[n].length;
					if ( length < this.p[n]['0'] || length > this.p[n]['1'] )
						isError = true;
				}
			}
		}
		*/
		if (typeof(this.l[n]) != 'undefined')
		{
			for ( var i in this.l[n] )
			{
				if (this.$(this.l[n][i]).value != this.value[n]) {
					isError = true;
				}
			}
		}
			
		if (isError)
		{
			this.messageError[n] = this.m[n];
			this.afficheMsg(n);
		}
		else { this.hiddeMsg(n); delete this.messageError[n];}
		
	},
	
	
	/* ***** DISPLAY FUNCTIONS ****** */
	
	
	//desc 	supprime le node relatif au message
	hiddeMsg: function (n) {
		//destruction du message
		if (document.getElementById('msg-error-'+n) === null) { return false; }
		
		var node = document.getElementById('msg-error-'+n);
		node.parentNode.removeChild(node);
		
		//colorisation du champ
		this.colorize(n,false);
	},
	
	//desc 	créée le node relatif au message
	afficheMsg: function (n) {
		//construction du message
		this.hiddeMsg(n);
		var new_node = document.createElement(this.msgElement);
		new_node.id = 'msg-error-'+n;
		var txt = this.m[n];
		var new_txt = document.createTextNode(txt);
		new_node.appendChild(new_txt);
		
		for (var i in this.msgAttributes) {
			if(i != 'undefined') {
				new_node.setAttribute(i,this.msgAttributes[i]);
			}
		}
		
		var cur_node = this.$(this.c[n]);
		this.insertAfter(new_node,cur_node);
		
		//colorisation du champ
		this.colorize(n,true);
	},
	
	//desc 	colorise le champ en fonction de l'état
	colorize: function (n, state) {
		if (state) {
			this.$(this.c[n]).style.backgroundColor = this.errorColor;
		}
		else {
			this.$(this.c[n]).style.backgroundColor = this.validateColor;
		}
	},
	
	
	//desc 	montre des erreur de code en alert si le debuggage est actif
	showError: function (msg) {
		if (this.debug) { alert(msg); }
	},
	
	
	/* ***** SYSTEM FUNCTIONS ****** */
		
	//@elem	string (id ou name d'un élément)
	//desc	cette fonction n'est pas l'équivalent de la classe de celle de prototype mais permet de récupérer un élement 
	//		par son id si il existe sinon par son name le premier name = elem du document
	$ 	: function (elem) {
		
		if (document.getElementById(elem) !== null) {
			return document.getElementById(elem);
		}
		else if (typeof(document.getElementsByName(elem)) != 'undefined'){
			return document.getElementsByName(elem)['0'];
		}
		else {
			return null;
		}
	},
	
	//@new_node	xml node (noeud à insérer)
	//@node		xml node (noeud cible)
	//desc		cette fonction "simule" une fonction insertAfter
	insertAfter : function (new_node, node) {
		//on determine le noeud parent
		var parent_node = node.parentNode.parentNode;
		//on determine le noeud suivant
		var next_node = node.parentNode.nextSibling;
		//si il y a des lignes blanches entre les deux (3 = text)
		/*while(next_node.nodeType == 3 && next_node.nextSibling !== null) { 
			try
			{
				//next_node = next_node.nextSibling;
			}
			catch(e)
			{
				showError(e);
			}
			
		}*/
		try
		{
			//si le noeud suivant n'existe pas cela signifie que node est le dernier est donc il suffit de faire un appendChild de parent_node
			if (next_node === null) {
				parent_node.appendChild(new_node,parent_node);
			}
			else {
				parent_node.insertBefore(new_node,next_node);
			}
		}
		catch(e)
		{
			showError(e);
		}
	},
	
	//http://www.truerwords.net/articles/web-tech/custom_events.html
	addEventListener : function ( element, event_name, observer, capturing ) {
        if ( element.addEventListener ) {  // the DOM2, W3C way
            element.addEventListener( event_name, observer, capturing );
		}
        else if ( element.attachEvent ) {  // the IE way
            element.attachEvent( "on" + event_name, observer );
		}
    },
	
	//http://fn-js.info/snippets/bind
	//desc	cette fonction permet de propager un objet dans un environnement où il ne devrait normalement plus exister !!!!
	bind : function (obj, fun, args) {
		return function() {
		if (obj === true) {
			obj = this;
		}
		var f = typeof fun === "string" ? obj[fun] : fun;

		return f.apply(obj, Array.prototype.slice.call(args || [])
			.concat(Array.prototype.slice.call(arguments)));
		};
	}
};
