
/*************************************************************************/
/* LibForm
/*
/* A XHTML, CSS and DOM enabled form validator.
/* In short the validator will, in the case of an error, add a CSS class
/* to the "required" or "optional" class identifier and inject the error 
/* description into the form element division.
/* 
/* version: 0.1.7
/* author: Felix Langfeldt
/* (c)Phixel.org
/*************************************************************************/

//*** Validation type constantes.
var VALIDFORM_STRING 		= "VALID_STRING";
var VALIDFORM_TEXT 			= "VALID_TEXT";
var VALIDFORM_TELFAX 		= "VALID_TELFAX";
var VALIDFORM_NUMERIC 		= "VALID_NUMERIC";
var VALIDFORM_INTEGER 		= "VALID_INTEGER";
var VALIDFORM_WORD 			= "VALID_WORD";
var VALIDFORM_EMAIL 		= "VALID_EMAIL";
var VALIDFORM_PASSWORD 		= "VALID_PASSWORD";
var VALIDFORM_ZIPCODE_NL 	= "VALID_ZIPCODE_NL";
var VALIDFORM_PHONE_NL 		= "VALID_PHONE_NL";
var VALIDFORM_SIMPLEURL 	= "VALID_SIMPLEURL";
var VALIDFORM_DATE 			= "VALID_DATE";
var VALIDFORM_PHONE			= "VALID_PHONE";
var VALIDFORM_FILE 			= "VALID_FILE";
var VALIDFORM_BOOLEAN		= "VALID_BOOLEAN";

function ValidForms() {
	/********************/
	/* ValidForms Class **************************************************/
	/* 
	/* Holds forms and there elements that need input validation.
	/*********************************************************************/
	
	this.forms 		= new Object();
}

function ValidForm(strFormId) {
	/*******************/
	/* ValidForm Class ***************************************************/
	/* 
	/* Holds a form and its elements.
	/*********************************************************************/
	
	this.id = strFormId;
	this.elements = new Object();
	this.alerts = new Object();
}

function ValidFormElement(strFormId, strElementName, strElementId, intValidType) {
	/**************************/
	/* ValidFormElement Class ********************************************/
	/* 
	/* Holds an element that can be validated.
	/*********************************************************************/
	
	this.formId				= strFormId;
	this.id 				= strElementId;
	this.name 				= strElementName;
	this.validType 			= intValidType;
	this.required 			= false;
	this.minLength 			= 0;
	this.maxLength 			= 0;
	this.matchWith 			= "";
		
	this.VALID_STRING 		= /^[-a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüý€0-9\.\,\'"()_ |&\\]*$/i;
	this.VALID_TEXT 		= /^[-a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüý€0-9\.\,\'"@_?#^*!&() \n\r\\]*$/i;
	this.VALID_NUMERIC 		= /^[0-9,\.]*$/i;
	this.VALID_INTEGER 		= /^[0-9]*$/i;
	this.VALID_TELFAX 		= /^[0-9 \/]*$/i;
	this.VALID_DATE 		= /^(\d{2}\/\d{2}\/\d{4})$/i;
	this.VALID_WORD 		= /^[-a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüý€0-9_]*$/i;
	this.VALID_EMAIL 		= /^[^@\s]+@([-a-z0-9]+\.)+[a-z]{2,}$/i;
	this.VALID_PASSWORD		= /^[-A-Z0-9\.-_!@#$^]*$/i;
	this.VALID_ZIPCODE_NL	= /^[1-9]\d{3}\s[A-Z]{2}$/i;
	this.VALID_PHONE_NL		= /^0(\d{2}-\d{7})|(\d{3}-\d{4})|(6-\d{8})$/i;
	this.VALID_SIMPLEURL	= /^[-A-Z0-9]+\.[-A-Z0-9]+/i;
	this.VALID_PHONE 		= /^[0-9+() ]*$/i;
	this.VALID_FILE			= /^[-a-zàáâãäåæçèéêëìíîïðñòóôõöøùúûüý0-9\.\'":\\_\/ ]*$/i;
	this.VALID_BOOLEAN		= /^[true]?[false]*$/i;
	
	if (ValidFormElement.arguments.length > 4) {
		this.required = ValidFormElement.arguments[4];
	}
	
	if (ValidFormElement.arguments.length > 5) {
		this.maxLength = ValidFormElement.arguments[5];
	}
	
	if (ValidFormElement.arguments.length > 6) {
		this.minLength = ValidFormElement.arguments[6];
	}
	
	if (ValidFormElement.arguments.length > 7) {
		this.matchWith = ValidFormElement.arguments[7];
	}
}

ValidForms.prototype.addForm = function(objValidForm) {
	this.forms[objValidForm.id] = objValidForm;
	$(document).ready( function() {
		$("#" + objValidForm.id).submit( function() {
			return objValidForms.validate(this.id);
		});
	});
};

ValidForms.prototype.form = function(strFormId) {
	return this.forms[strFormId];
};	

ValidForms.prototype.validate = function(strFormId) {
	if (this.forms[strFormId]) {
		return this.forms[strFormId].validate();
	} else {
		return true;
	}
};

ValidForm.prototype.addElement = function() {
	if (arguments.length > 0 && typeof(arguments[0]) == "object") {
		this.elements[arguments[0].name] = arguments[0];
		
		return true;
	} else {
		var blnRequired 	= false;
		var intMinLength	= 0;
		var intMaxLength 	= 0;
		var strMatchWith 	= "";

		if (arguments.length > 0) {
			var strElementId = arguments[0];
		} else {
			return false;
		}

		if (arguments.length > 1) {
			var strElementName = arguments[1];
		} else {
			return false;
		}

		if (arguments.length > 2) {
			var intValidType = arguments[2];
		} else {
			return false;
		}
		
		if (arguments.length > 3) {
			blnRequired = arguments[3];
		}
		
		if (arguments.length > 4) {
			intMaxLength = arguments[4];
		}
		
		if (arguments.length > 5) {
			intMinLength = arguments[5];
		}
		
		if (arguments.length > 6) {
			strMatchWith = arguments[6];
		}
		
		this.elements[strElementName] = new ValidFormElement(this.id, strElementName, strElementId, intValidType, blnRequired, intMaxLength, intMinLength, strMatchWith);
	}
};

ValidForm.prototype.elementByName = function(strElementName) {
	return this.elements[strElementName];
};

ValidForm.prototype.elementById = function(strElementId) {
	for (var strName in this.elements) {
		if (this.elements[strName].id == strElementId) {
			return this.elements[strName];
			break;
		}
	}
};

ValidForm.prototype.validate = function() {
	/*************************/
	/* validate function     *********************************************/
	/* 
	/* Uses the ValidForms, ValidForm, ValidElement and ValidFormAlerter 
	/* objects to validate form elements.
	/*********************************************************************/
	
	var blnReturn = true;
	var arrMultiElements = new Array();
	var objAlerter = new ValidFormAlerter(this.id);

	//*** Set the form object.
	try {
		var objForm = document.getElementById(this.id);
	} catch(e) {
		alert("Er ging iets mis bij het aanroepen van het formulier.\nFoutmelding: " + e.message);
	}
	
	if (objForm) {
		/*** Loop through the elements of the form and look for elements 
			  that need validation. */
		var formElements = objForm.elements;
		var intIndex;
		
		//*** Get the ValidForm object
		var objValidForm = objValidForms.form(this.id);
		
		//*** Reset main error notifications.
		objAlerter.mainPop(objValidForm.alerts.mainErrorId);
			
		if (objValidForm) {
			objAlerter.mainAlert = objValidForm.alerts.mainError;
	
			//*** Element loop.
			for (intIndex = 0; intIndex < formElements.length; intIndex++) {
				var objElement = formElements[intIndex];

				//*** Check for elements with the same name
				if (!arrMultiElements.inArray(objElement.name) 
						&& objElement.name != "" 
						&& objElement.id != "") {
				
					//*** Check for radio and checkboxes
					if (objElement.type == "radio" || objElement.type == "checkbox") {
						arrMultiElements.push(objElement.name);
					}

					//*** Reset error notifications.
					objAlerter.pop(objElement.id);

					var objValidElement = objValidForm.elementByName(objElement.name);

					//*** Finally let's validate the input.	
					if (objValidElement) {
						if (!objValidElement.validate()) {
							blnReturn = false;
							objAlerter.push(objElement.id, objValidForm.alerts.getAlert(objElement.name), objValidForm.alerts.mainErrorId);
						}	
					}
				}
			}
		}
				
	} else {
		alert("Het formulier werd niet gevonden.");
	}
	
	return blnReturn;
}

ValidFormElement.prototype.validate = function() {
	//*** Validate the element using the validType and required

	var objValidForm = objValidForms.form(this.formId);
	var objElement = document.getElementById(this.name);
	var blnReturn = true;

	try {
		var value = objElement.value;

		/*** Redirect to error handler if a checkbox or radio is found.
				This is done for cross-browser functionality. */
		switch (objElement.type) {
			case 'radio':
			case 'checkbox':
				throw "Checkbox or radio button detected.";
				break;
		}

		//*** Required, but empty is not good.
		if (this.required && value == "") {
			objValidForm.alerts.setActiveError(objElement.name, "required");
			return false;
		} else if (!this.required && value == "") {
			return true;
		}
		
		//*** Check if the length of the value is within the range.
		if (value.length < this.minLength) {
			objValidForm.alerts.setActiveError(objElement.name, "minLength");
			return false;
		}
		if (this.maxLength > 0 && value.length > this.maxLength) {
			objValidForm.alerts.setActiveError(objElement.name, "maxLength");
			return false;
		}
		
		//*** Check if the value matches with the match value.
		if (this.matchWith != "") {
			var objMatch = document.getElementById(this.matchWith);
			if (objMatch.value !== value) {
				objValidForm.alerts.setActiveError(objElement.name, "matchWith");
				return false;
			}
		}

		//*** Check specific types using regular expression.
		blnReturn = this[this.validType].test(value);
		if (blnReturn == false) objValidForm.alerts.setActiveError(objElement.name);
		return blnReturn;
		
	} catch(e) {
		//*** Checkbox or radio button.
		var objForm = document.getElementById(this.formId);
		var arrChecked = [];

		for (var intIndex = 0; intIndex < objForm.elements.length; intIndex++) {
			var objElement = objForm.elements[intIndex];
			if (objElement.type) {
				var strName = objElement.name.trim();

				if (strName == this.name) {
					if (objElement.checked) {
						arrChecked.push(objElement.value);
					}
				}
			}
		}
				
		//*** Required, but empty is not good.
		if (this.required && arrChecked.length == 0) {
			objValidForm.alerts.setActiveError(this.name, "required");
			return false;
		} else if (!this.required && arrChecked.length == 0) {
			return true;
		}

		//*** Check if the length of the value is within the range.
		if (this.minLength > 0 && arrChecked.length < this.minLength) {
			objValidForm.alerts.setActiveError(this.name, "minLength");
			return false;
		}
		if (this.maxLength > 0 && arrChecked.length > this.maxLength) {
			objValidForm.alerts.setActiveError(this.name, "maxLength");
			return false;
		}
		
		//*** Check specific types using the type array.
		if (typeof this.validType == "array") {
			for (var intCount = 0; intCount < arrChecked.length; intCount++) {
				if (!this.validType.inArray(arrChecked[intCount])) {
					objValidForm.alerts.setActiveError(this.name)
					return false;
				}
			}
		}
		
		return true;
	}
};

function ValidFormAlert(strId, strRequired) {
	/********************/
	/* ValidFormAlert Class ***********************************************/
	/* 
	/* Holds alerts for a ValidFormElement object.
	/*********************************************************************/
	
	this.id 					= strId;
	this.alerts					= Array();
	this.alerts['required']		= strRequired;
	this.alerts['type']			= "";
	this.alerts['maxLength']	= "";
	this.alerts['minLength']	= "";
	this.alerts['matchWith']	= "";
	this.activeError			= "";
	
	if (arguments.length > 2) {
		this.alerts['type'] = arguments[2];
	}
	
	if (arguments.length > 3) {
		this.alerts['maxLength'] = arguments[3];
	}
	
	if (arguments.length > 4) {
		this.alerts['minLength'] = arguments[4];
	}
	
	if (arguments.length > 5) {
		this.alerts['matchWith'] = arguments[5];
	}
}

ValidFormAlert.prototype.getAlert = function() {
	if (arguments.length > 0) {
		return this.alerts[arguments[0]];
	} else {
		return this.alerts['type'];
	}
}

function ValidFormAlerts(strMainErrorId) {
	/********************/
	/* ValidFormAlerts Class *********************************************/
	/* 
	/* Holds alerts for a ValidForm object.
	/*********************************************************************/
	
	this.mainErrorId	= strMainErrorId;
	this.alerts 		= new Object();
	this.mainError		= "";
}

ValidFormAlerts.prototype.addAlert = function() {
	if (arguments.length > 0 && typeof(arguments[0]) == "object") {
		this.alerts[arguments[0].id] = arguments[0];
		
		return true;
	} else {
		var strRequired		= "";
		var strMaxLength	= "";
		var strMinLength	= "";
		var strMatchWith	= "";

		if (arguments.length > 0) {
			var strId = arguments[0];
		} else {
			return false;
		}

		if (arguments.length > 1) {
			var strType = arguments[1];
		} else {
			return false;
		}
		
		if (arguments.length > 2) {
			strRequired = arguments[2];
		} else {
			strRequired = strType;
		}
		
		if (arguments.length > 3) {
			strMaxLength = arguments[3];
		} else {
			strMaxLength = strType;
		}
		
		if (arguments.length > 4) {
			strMinLength = arguments[4];
		} else {
			strMinLength = strType;
		}
		
		if (arguments.length > 5) {
			strMatchWith = arguments[5];
		} else {
			strMatchWith = strType;
		}
		
		this.alerts[strId] = new ValidFormAlert(strId, strType, strRequired, strMaxLength, strMinLength, strMatchWith);
	}
}

ValidFormAlerts.prototype.getAlert = function(strElementName) {
	if (arguments.length > 1) {
		return this.alerts[strElementName].getAlert(arguments[1]);
	} else {
		return this.alerts[strElementName].activeError;
	}
}

ValidFormAlerts.prototype.setActiveError = function(strElementName) {
	if (arguments.length > 1) {
		this.alerts[strElementName].activeError = this.alerts[strElementName].alerts[arguments[1]];
	} else {
		this.alerts[strElementName].activeError = this.alerts[strElementName].alerts['type'];
	}
}

function ValidFormAlerter(strFormId) {
	/*********************/
	/* ValidFormAlerter Class ********************************************/
	/* 
	/* Display class used to push alerts regarding form validation 
	/* to the browser.
	/*********************************************************************/
	
	this.id 				= strFormId;
	this.mainAlert			= "";
}

/*************************************************************************/
/* Library functions
/*************************************************************************/

function getParentByClass(objChild, varClass) {
	if (typeof varClass == "string") {
		varClass = varClass.split(",");
	}
	
	try {
		var arrClassName = objChild.className.split(" ");
		for (var intCount = 0; intCount < arrClassName.length; intCount++) {
			if (varClass.inArray(arrClassName[intCount].trim())) {
				return objChild;
			}
		}
		
		var objParent = objChild.parentNode;
		if (objParent) {
			return getParentByClass(objParent, varClass);
		} else {
			return null;
		}
	} catch(e) {
		return null;
	}
}

function getElementsByClass(searchClass, node, tag) {
	var classElements = new Array();
	if (node == null) node = document;
	if (tag == null) tag = '*';
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
	for (i = 0, j = 0; i < elsLen; i++) {
		if ( pattern.test(els[i].className) ) {
			classElements[j] = els[i];
			j++;
		}
	}
	return classElements;
}

Array.prototype.inArray = function (value) {
	var i;
	for (i=0; i < this.length; i++) {
		if (this[i] === value) {
			return true;
		}
	}
	return false;
};

String.prototype.trim = function () {
	var s = this.replace(/^\s*/, "");
	return s.replace(/\s*$/, "");    
};
