
/*************************************************************************/
/* 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.1
/* author: Felix Langfeldt
/* (c)Phixel.org
/*************************************************************************/

//*** Validation type constantes.
var LIBFRM_STRING 		= "VALID_STRING";
var LIBFRM_TEXT 		= "VALID_TEXT";
var LIBFRM_TELFAX 		= "VALID_TELFAX";
var LIBFRM_NUMERIC 		= "VALID_NUMERIC";
var LIBFRM_INTEGER 		= "VALID_INTEGER";
var LIBFRM_WORD 		= "VALID_WORD";
var LIBFRM_EMAIL 		= "VALID_EMAIL";
var LIBFRM_PASSWORD 	= "VALID_PASSWORD";
var LIBFRM_ZIPCODE_NL 	= "VALID_ZIPCODE_NL";
var LIBFRM_PHONE_NL 	= "VALID_PHONE_NL";
var LIBFRM_PHONE_AN 	= "VALID_PHONE_AN";
var LIBFRM_SIMPLEURL 	= "VALID_SIMPLEURL";
var LIBFRM_DATE 		= "VALID_DATE";

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.errors = 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-Z0-9\.\,\'"_ \\]*$/i;
	this.VALID_TEXT 		= /^[-A-Z0-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-Z0-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_PHONE_AN		= /^0(\d{2}-\d{7})|(\d{3}-\d{4})|(6-\d{8})$/i;
	this.VALID_SIMPLEURL	= /^[-A-Z0-9]+\.[-A-Z0-9]+/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 strElementName = arguments[0];
		} else {
			return false;
		}

		if (arguments.length > 1) {
			var strElementId = 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 FormAlerter objects
	/* to validate form elements.
	/*********************************************************************/
	
	var blnReturn = true;
	var arrMultiElements = new Array();
	var objAlerter = new FormAlerter(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;
		
		//*** Reset main error notifications.
		objAlerter.mainPop();
		
		//*** Get the ValidForm object
		var objValidForm = objValidForms.form(this.id);
			
		if (objValidForm) {
			objAlerter.mainAlert = objValidForm.errors.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)) {
				
					//*** 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.errors.getError(objElement.name));
						}	
					}
				}
			}
		}
				
	} 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.errors.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.errors.setActiveError(objElement.name, "tooShort");
			return false;
		}
		if (this.maxLength > 0 && value.length > this.maxLength) {
			objValidForm.errors.setActiveError(objElement.name, "tooLong");
			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.errors.setActiveError(objElement.name, "noMatch");
				return false;
			}
		}

		//*** Check specific types using regular expression.
		blnReturn = this[this.validType].test(value);
		if (blnReturn == false) objValidForm.errors.setActiveError(objElement.name);
		return blnReturn;
		
	} catch(e) {
		//*** Checkbox or radio button.
		var objForm = document.getElementById(this.formId);
		var blnChecked = false;

		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) {
						blnChecked = true;
						break;
					} else if (!this.required) {
						blnChecked = true;
						break;
					}
				}
			}
		}
		
		if (blnChecked == false) objValidForm.errors.setActiveError(this.name, "required");
		return blnChecked;
	}
};

function FormError(strId, strMainError) {
	/********************/
	/* FormError Class **************************************************/
	/* 
	/* Holds error for a ValidFormElement object.
	/*********************************************************************/
	
	this.id 				= strId;
	this.errors				= Array();
	this.errors['main']		= strMainError;
	this.errors['required']	= this.errors['main'];
	this.errors['tooShort']	= this.errors['main'];
	this.errors['tooLong']	= this.errors['main'];
	this.errors['noMatch']	= this.errors['main'];
	this.activeError		= this.errors['main'];
	
	if (arguments.length > 2) {
		this.errors['required'] = arguments[2];
	}
	
	if (arguments.length > 3) {
		this.errors['tooLong'] = arguments[3];
	}
	
	if (arguments.length > 4) {
		this.errors['tooShort'] = arguments[4];
	}
	
	if (arguments.length > 5) {
		this.errors['noMatch'] = arguments[5];
	}
}

FormError.prototype.getError = function() {
	if (arguments.length > 0) {
		return this.errors[arguments[0]];
	} else {
		return this.errors['main'];
	}
}

function FormErrors() {
	/********************/
	/* FormErrors Class **************************************************/
	/* 
	/* Holds errors for a ValidForm object.
	/*********************************************************************/
	
	this.errors 		= new Object();
	this.mainError		= "";
}

FormErrors.prototype.addError = function() {
	if (arguments.length > 0 && typeof(arguments[0]) == "object") {
		this.errors[arguments[0].id] = arguments[0];
		
		return true;
	} else {
		var strRequiredError	= "";
		var strTooShortError	= "";
		var strTooLongError		= "";
		var strNoMatchError		= "";

		if (arguments.length > 0) {
			var strId = arguments[0];
		} else {
			return false;
		}

		if (arguments.length > 1) {
			var strMainError = arguments[1];
		} else {
			return false;
		}
		
		if (arguments.length > 2) {
			strRequiredError = arguments[2];
		} else {
			strRequiredError = strMainError;
		}
		
		if (arguments.length > 3) {
			strTooLongError = arguments[3];
		} else {
			strTooLongError = strMainError;
		}
		
		if (arguments.length > 4) {
			strTooShortError = arguments[4];
		} else {
			strTooShortError = strMainError;
		}
		
		if (arguments.length > 5) {
			strNoMatchError = arguments[5];
		} else {
			strNoMatchError = strMainError;
		}
		
		this.errors[strId] = new FormError(strId, strMainError, strRequiredError, strTooLongError, strTooShortError, strNoMatchError);
	}
}

FormErrors.prototype.getError = function(strElementName) {
	if (arguments.length > 1) {
		return this.errors[strElementName].getError(arguments[1]);
	} else {
		return this.errors[strElementName].activeError;
	}
}

FormErrors.prototype.setActiveError = function(strElementName) {
	if (arguments.length > 1) {
		this.errors[strElementName].activeError = this.errors[strElementName].errors[arguments[1]];
	} else {
		this.errors[strElementName].activeError = this.errors[strElementName].errors['main'];
	}
}

function FormAlerter(strFormId) {
	/*********************/
	/* FormAlerter Class *************************************************/
	/* 
	/* Display class used to push alerts/errors regarding form validation 
	/* to the browser.
	/*********************************************************************/
	
	this.id 				= strFormId;
	this.mainAlert			= "";
}

/*************************************************************************/
/* Library functions
/*************************************************************************/

function getParentByClass(objChild, strClassName) {
	try {
		if (objChild.className.trim() == strClassName.trim()) {
			return objChild;
		} else {
			var objParent = objChild.parentNode;
			if (objParent) {
				return getParentByClass(objParent, strClassName);
			} 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*$/, "");    
};
