	//window.onload = function() { initValidator(); };

	var validators = new Array();

	var defaultErrorMsgs = new Array();
	defaultErrorMsgs['default']             = '"%%label%%" is invalid.';
	defaultErrorMsgs['required']            = '"%%label%%" is a required field.';
	defaultErrorMsgs['radiorequired']       = '"%%grouplabel%%" requires a selection.';
	defaultErrorMsgs['parentrequirement']   = '"%%label%%" is required based on your selection of "%%parentlabel%%".';
	defaultErrorMsgs['datatype']            = '"%%label%%" is not properly formatted.';
	defaultErrorMsgs['minlength']           = '"%%label%%" needs to be a minimum of %%minlength%% characters.';
	defaultErrorMsgs['maxlength']           = '"%%label%%" needs to be a maximum of %%maxlength%% characters.';
	defaultErrorMsgs['minimum']             = '"%%grouplabel%%" requires at least %%minimum%% selection(s).';
	defaultErrorMsgs['maximum']             = '"%%grouplabel%%" requires at most %%maximum%% selection(s).';
	defaultErrorMsgs['fieldsminimum']       = '"%%grouplabel%%" requires at least %%minimum%% field(s).';
	defaultErrorMsgs['fieldsmaximum']       = '"%%grouplabel%%" requires at most %%maximum%% field(s).';
	
	var errorHandlers                = new Array();
	errorHandlers['alert']           = 'alertErrors';
	errorHandlers['display']         = 'displayErrors';
	errorHandlers['displayandalert'] = 'displayAndAlertErrors';
	
	
	var dataTypes                             = new Array();
	
	dataTypes['int']                          = new Array();
	dataTypes['int']['regex']                 = /^(\+|-)?\d+$/;
	dataTypes['int']['errorMsg']              = '"%%label%%" requires positive or negative numbers only.';
	
	dataTypes['decimal']                      = new Array();
	dataTypes['decimal']['regex']             = /^[-+]?\d+(\.\d+)?$/;
	dataTypes['decimal']['errorMsg']          = '"%%label%%" requires positive or negative decimal numbers only.';
	
	dataTypes['smalldate']                    = new Array();
	dataTypes['smalldate']['regex']           = /^(0?[1-9]|1[012])\/(0?[1-9]|[12][0-9]|3[01])\/(19|20)[0-9]{2}$/;
	dataTypes['smalldate']['errorMsg']        = '"%%label%%" requires a date using the format MM/DD/YYYY.';
	
	dataTypes['email']                        = new Array();
	dataTypes['email']['regex']               = /^\w+([-.]\w+)*@((([a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*\.)?[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*\.[a-zA-Z]{2,4})|(\[([12]?[012345]?\d\.){3}[12]?[012345]?\d\])|(([12]?[012345]?\d\.){3}[12]?[012345]?\d))$/;
	dataTypes['email']['errorMsg']            = '"%%label%%" requires a valid email address.';
	
	dataTypes['url']                          = new Array();
	dataTypes['url']['regex']                 = /^(http\:\/\/)?(([a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*\.)?[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*\.[a-zA-Z]{2,4}|([012]?[012345]?\d\.){3}[012]?[012345]?\d)(\/[\w.]+)*\/?$/;
	dataTypes['url']['errorMsg']              = '"%%label%%" requires a valid URL.';
	
	dataTypes['uspostalcode']                 = new Array();
	dataTypes['uspostalcode']['regex']        = /^\d{5}([\s-]{1}\d{4})?$/;
	dataTypes['uspostalcode']['errorMsg']     = '"%%label%%" requires a valid US postal code.';
	
	dataTypes['canadapostalcode']             = new Array();
	dataTypes['canadapostalcode']['regex']    = /^[a-zA-Z]\d[a-zA-Z][\s-]?\d[a-zA-Z]\d$/;
	dataTypes['canadapostalcode']['errorMsg'] = '"%%label%%" requires a valid Canada postal code.';
	
	dataTypes['usphonenumber']                = new Array();
	dataTypes['usphonenumber']['regex']       = /^\(?[\d]{3}\)?[\s-]?[\d]{3}[\s-.]?[\d]{4}$/;
	dataTypes['usphonenumber']['errorMsg']    = '"%%label%%" requires a valid US 16 digit phone number.';


	function initValidator() 
	{
		if(document.getElementById && document.createElement)
		{
			forms = document.getElementsByTagName('form');					
		
			for(var i = 0; i < forms.length; i++) 
			{
				if(forms[i].getAttribute('validate') == "true")
				{
					validators[forms[i].getAttribute('name')] = new Validator(forms[i]);
            	}
        	}
		}
    }
	
/****
 * Validator class
 */
	function Validator(form)
	{
		this.form         = form;
		this.inputs       = new Array();
		this.errors       = new Array();
		this.name         = this.getName();
		this.errorHandler = this.getErrorHandler();
		this.errorDiv     = this.getErrorDiv();
		
		this.extraValidator = form.getAttribute('extravalidator');
		
		this.setFormEvent();
	}
	
	Validator.prototype.validate = function()
	{
		var validated = false;
		this.inputs = this.getInputs();
		this.errors = new Array();

		for(var i = 0; i < this.inputs.length; i++)
		{
			if(this.inputs[i].validated)
			{
				continue;
			}
			else if(this.inputs[i].customValidator != false)
			{
				var cv = eval(this.inputs[i].customValidator+'(validators[this.name].inputs,'+i+')');
				if(cv != true)
				{
					this.addError(cv, this.inputs[i]);
				}
			}
			else if(this.inputs[i].groupLabel != false && !this.inputs[i].groupValidated)
			{
				var tally = this.getGroupTally(this.inputs[i], i);
				
				if(this.inputs[i].minimum != false && tally < this.inputs[i].minimum)
				{
					switch(this.inputs[i].type)
					{
						case 'radio':
							this.addError('radiorequired', this.inputs[i]);
							break;
						case 'checkbox':
						case 'multiSelect':
							this.addError('minimum', this.inputs[i]);
							break;
						case 'text':
						case 'password':
						case 'textarea':
						case 'select':
							this.addError('fieldsminimum', this.inputs[i]);
							break;
					}
				}
				else if(this.inputs[i].maximum != false && tally > this.inputs[i].maximum)
				{
					switch(this.inputs[i].type)
					{
						case 'checkbox':
						case 'multiSelect':
							this.addError('maximum', this.inputs[i]);
							break;
						case 'text':
						case 'password':
						case 'textarea':
						case 'select':
							this.addError('fieldsmaximum', this.inputs[i]);
							break;
					}
				}
			}
			else if(this.isRequired(this.inputs[i]))
			{
				this.addError('required', this.inputs[i]);
			}
			else if(this.isBelowMinLength(this.inputs[i]))
			{
				this.addError('minlength', this.inputs[i]);
			}
			else if(this.isAboveMaxLength(this.inputs[i]))
			{
				this.addError('maxlength', this.inputs[i]);
			}
			else if(this.isDataType(this.inputs[i]) != true)
			{
				this.addError('datatype', this.inputs[i]);
			}
			else if(this.isRequiredByParent(this.inputs[i]))
			{
				this.addError('parentrequirement', this.inputs[i]);
			}
			
			this.inputs[i].setValidated(true);
		}
		
		if(this.errors.length > 0)
		{
			if(errorHandlers[this.errorHandler])
			{
				eval('this.'+errorHandlers[this.errorHandler]+'()');
			}
			else
			{
				eval(this.errorHandler+'(validators[this.name].errors,this.name)');
			}
		}
		else
		{
			validated = true;
		}
		
		return validated;
	};
	
	Validator.prototype.addError = function(errorType, input)
	{
		var errorMsg = false;
		var index    = this.errors.length;
		
		if(input.errorMsgs['all'])
		{
			errorMsg = input.errorMsgs['all'];
		}
		else if(errorType == 'datatype')
		{
			if(input.errorMsgs['datatype'])
			{
				errorMsg = input.errorMsgs['datatype'];
			}
			else if(input.errorMsgs[input.dataType])
			{
				errorMsg = input.errorMsgs[input.dataType];
			}
			else if(dataTypes[input.dataType]['errorMsg'])
			{
				errorMsg = dataTypes[input.dataType]['errorMsg'];
			}
			else
			{
				errorMsg = defaultErrorMsgs['datatype'];
			}
		}
		else if(input.errorMsgs[errorType])
		{
			errorMsg = input.errorMsgs[errorType];
		}
		else if(xmlErrorMsgs[errorType] && xmlErrorMsgs[errorType] != 'false')
		{
			errorMsg = xmlErrorMsgs[errorType];
		}
		else if(xmlErrorMsgs['default'] && xmlErrorMsgs['default'] != 'false')
		{
			errorMsg = xmlErrorMsgs['default'];
		}
		else if(defaultErrorMsgs[errorType])
		{
			errorMsg = defaultErrorMsgs[errorType];
		}
		else
		{
			errorMsg = defaultErrorMsgs['default'];
		}
		
		errorMsg = errorMsg.replace(/%%label%%/g,input.label);
		errorMsg = errorMsg.replace(/%%minlength%%/g,input.minLength);
		errorMsg = errorMsg.replace(/%%maxlength%%/g,input.maxLength);
		errorMsg = errorMsg.replace(/%%minimum%%/g,input.minimum);
		errorMsg = errorMsg.replace(/%%maximum%%/g,input.maximum);
		errorMsg = errorMsg.replace(/%%grouplabel%%/g,input.groupLabel);
		errorMsg = errorMsg.replace(/%%parentlabel%%/g,input.parent.label);
		
		this.errors[index]         = new Array();
		this.errors[index]['type'] = errorType;
		this.errors[index]['id']   = input.id;
		this.errors[index]['msg']  = errorMsg;
	};
	
	Validator.prototype.alertErrors = function()
	{
		var errs = '';
		
		for(var i = 0; i < this.errors.length; i++) 
		{ 
			errs += this.errors[i]['msg']+'\n'; 
		}
		
		if(errs != '') 
		{ 
			alert(errs);
		} 
	};
	
	Validator.prototype.displayErrors = function()
	{
		var errs, ul, li, p;
		
		if(this.errorDiv == false || this.errorDiv.nodeName != 'DIV')
		{
			this.errorDiv = document.createElement('div');
			this.errorDiv.setAttribute('id', 'errors'+this.name);
			this.errorDiv.setAttribute('class', 'errorsdiv');
			document.getElementsByTagName("body")[0].insertBefore(this.errorDiv, this.form);
		}
		
		if(this.errorDiv.getElementsByTagName('ul')[0])
		{
			this.errorDiv.removeChild(this.errorDiv.getElementsByTagName('ul')[0]);
		}
		
		ul = document.createElement('ul');
		this.errorDiv.appendChild(ul);
		
		for(var i = 0; i < this.errors.length; i++)
		{
			li = document.createElement('li');
			t = document.createTextNode(this.errors[i]['msg']);
			li.appendChild(t);
			ul.appendChild(li);
		}
		
		this.errorDiv.style.display = 'block';
	};
	
	Validator.prototype.displayAndAlertErrors = function()
	{
		this.alertErrors();
		this.displayErrors();
	};
	
	Validator.prototype.getErrorHandler = function()
	{
		var errorHandler = 'alert';
		
		if(this.form.getAttribute('errorhandler'))
		{
			errorHandler = this.form.getAttribute('errorhandler');
		}
		
		return errorHandler;
	};
	
	Validator.prototype.getErrorDiv = function()
	{
		var errorDiv = false;
		
		if(this.form.getAttribute('errordiv'))
		{
			errorDiv = document.getElementById(this.form.getAttribute('errordiv'));
		}
		
		return errorDiv;
	};
	
	Validator.prototype.getGroupTally = function(input, pos)
	{
		var tally = 0;
				
		for(var j = pos; j < this.inputs.length; j++)
		{
			if(this.inputs[j].groupLabel == this.inputs[pos].groupLabel)
			{
				switch(this.inputs[j].type)
				{
					case 'checkbox':
					case 'radio':
						if(this.inputs[j].checked)
						{
							tally++;
						}
						break;
					case 'multiSelect':
						for(var k = 0; k < this.inputs[j].element.options.length; k++)
						{
							if(this.inputs[j].element.options[k].selected && this.inputs[j].element.options[k].value != '')
							{
								tally++;
							}
						}
						break;
					case 'text':
					case 'password':
					case 'textarea':
					case 'select':
						if(this.inputs[j].length > 0)
						{
							tally++;
						}
						break;
				}
				
				this.inputs[j].setGroupValidated(true);
			}
		}
		
		return tally;
	};
	
	Validator.prototype.isRequired = function(input)
	{
		var required = false;
		
		switch(input.type)
		{
			case 'text':
			case 'textarea':
			case 'select':
			case 'password':
				if(input.required && input.length <= 0)
				{
					required = true;
				}
				break;
			case 'checkbox':
			case 'radio':
			case 'multiSelect':
				break;
		}
		
		return required;
	};
	
	Validator.prototype.isBelowMinLength = function(input)
	{
		var bln = false;
		
		if(input.minLength != false && input.length > 0)
		{
			switch(input.type)
			{
				case 'text':
				case 'textarea':
				case 'password':
					bln = (input.length < input.minLength) ? true : false;
					break;
			}
		}

		return bln;
	};
	
	Validator.prototype.isAboveMaxLength = function(input)
	{
		var bln = false;
		
		if(input.maxLength != false && input.length > 0)
		{
			switch(input.type)
			{
				case 'text':
				case 'textarea':
				case 'password':
					bln = (input.length > input.maxLength) ? true : false;
					break;
			}
		}
		
		return bln;
	};
	
	Validator.prototype.isDataType = function(input)
	{
		var bln = false;
		var re = new RegExp();
		if(input.dataType != false && input.value != false)
		{
			if(dataTypes[input.dataType])
			{
				re = dataTypes[input.dataType]['regex'];
				bln = (input.value.match(re) == null) ? false : true;
			}
			else
			{
				bln = true;
			}
		}
		else
		{
			bln = true;
		}
		
		return bln;
	};
	
	Validator.prototype.isRequiredByParent = function(input)
	{
		var bln = false;
		
		if(input.parents != false && input.length <= 0)
		{
			var temp = new Array();
			var parent;
			
			for(var i = 0; i < input.parents.length; i++)
			{
				if(input.parents[i].indexOf(':') > 0)
				{
					temp = input.parents[i].split(':');
					temp[0] = temp[0].trim();
					temp[1] = temp[1].trim();
				}
				else
				{
					temp[0] = input.parents[i].trim();
				}
				
				for(var j = 0; j < this.inputs.length; j++)
				{
					if(this.inputs[j].id == temp[0] || this.inputs[j].name == temp[0])
					{
						parent = this.inputs[j];
						break;
					}
				}
				
				switch(parent.type)
				{
					case 'checkbox':
					case 'radio':
						for(var k = 0; k < this.inputs.length; k++)
						{
							if(this.inputs[k].name == parent.name)
							{
								if((temp[1] && this.inputs[k].value == temp[1] && this.inputs[k].checked != false) || (!temp[1] && this.inputs[k].checked != false))
								{
									bln = true;
									break;
								}
							}
						}
						break;
					case 'multiSelect':
						for(var k = 0; k < parent.element.options.length; k++)
						{
							if(parent.element.options[k].selected && ((temp[1] && parent.element.options[k].value == temp[1]) || (!temp[1])))
							{
								bln = true;
								break;
							}
						}
						break;
					case 'text':
					case 'password':
					case 'textarea':
					case 'select':
						if((temp[1] && parent.value == temp[1]) || (!temp[1] && parent.length > 0))
						{
							bln = true;
						}
						break;
				}
				
				if(bln == true)
				{
					input.parent = parent;
					break;
				}
			}
		}
		
		return bln;
	};
	
	Validator.prototype.setFormEvent = function()
	{
		this.form.onsubmit = function()
		{
			if(validators[this.name].extraValidator)
			{
				eval(validators[this.name].extraValidator+'()');
			}
			return validators[this.name].validate();
		};
	};
	
	Validator.prototype.getName = function()
	{
		return this.form.getAttribute('name') ? this.form.getAttribute('name') : false;
	};
	
	Validator.prototype.getInputs = function()
	{
		var inps = new Array();
		for(var i = 0; i < this.form.elements.length; i++)
		{
			switch(this.form.elements[i].nodeName)
			{
				case 'INPUT':
				case 'SELECT':
				case 'TEXTAREA':
					var inp = new Input(this.form.elements[i]);
					inps[inps.length] = inp;
					break;
			}
		}
		return (inps.length > 0 ? inps : false);
	};
	
/****
 * Input class
 */
	function Input(element)
	{
		this.element            = element;
		this.type               = this.getType();
		this.name               = this.getName();
		this.id                 = this.getId();
		this.label              = this.getLabel();
		this.required           = this.getRequired();
		this.parents            = this.getParents();
		this.parent             = false;
		this.groupLabel         = this.getGroupLabel();
		this.minimum            = this.getMinimum();
		this.maximum            = this.getMaximum();
		this.checked            = this.isChecked();
		this.value              = this.getValue();
		this.length             = this.getLength();
		this.minLength          = this.getMinLength();
		this.maxLength          = this.getMaxLength();
		this.dataType           = this.getDataType();
		this.errorMsgs          = this.getErrorMsgs();
		this.customValidator    = this.getCustomValidator();
		this.groupValidated     = false;
		this.validated          = false;
	}
	
	Input.prototype.getType = function()
	{
		var type = false;
		switch(this.element.nodeName)
		{
			case 'INPUT':
				type = this.element.getAttribute('type');
				break;
			case 'SELECT':
				type = this.element.getAttribute('multiple') ? (this.element.getAttribute('multiple') != 'false' ? 'multiSelect' : 'select') : 'select';
				break;
			case 'TEXTAREA':
				type = 'textarea';
				break;
		}
		
		if(type == 'file') { type = 'text'; }
		
		return type;
	};
	
	Input.prototype.getName = function()
	{
		return this.element.getAttribute('name') ? this.element.getAttribute('name') : false;
	};
	
	Input.prototype.getId = function()
	{
		return this.element.getAttribute('id') ? this.element.getAttribute('id') : false;
	};
	
	Input.prototype.getLabel = function()
	{
		return this.element.getAttribute('label') ? this.element.getAttribute('label') : false;
	};
	
	Input.prototype.getRequired = function()
	{
		return this.element.getAttribute('required') ? this.element.getAttribute('required') : false;
	};
	
	Input.prototype.getParents = function()
	{
		var parents = false;
		var temp;
		
		if(this.element.getAttribute('parent'))
		{
			temp = this.element.getAttribute('parent');
			temp = temp.replace(/(;)$/g,"");
			parents = new Array();
			if(temp.indexOf(';') > 0)
			{
				parents = temp.split(';');
			}
			else
			{
				parents[0] = temp;
			}
		}

		return parents;
	};
	
	Input.prototype.getGroupLabel = function()
	{
		var gLabel = false;	
	
		if(this.element.getAttribute('grouplabel'))
		{
			gLabel = this.element.getAttribute('grouplabel');
		}
		else if(this.type == 'multiSelect')
		{
			gLabel = this.label;
		}
		
		if(this.label == false)
		{
			this.label = gLabel;
		}
		
		return gLabel;
	};
	
	Input.prototype.getMinimum = function()
	{
		var minimum = false;
		
		if(this.element.getAttribute('minimum'))
		{
			minimum = this.element.getAttribute('minimum');
		}
		else if(this.required && this.groupLabel != false)
		{
			minimum = 1;
		}
		
		return minimum;
	};
	
	Input.prototype.getMaximum = function()
	{
		var maximum = false;
		
		if(this.element.getAttribute('maximum'))
		{
			maximum = this.element.getAttribute('maximum');
		}
		
		return maximum;
	};
	
	Input.prototype.isChecked = function()
	{
		var checked = false;
		
		if(this.type == 'radio' || this.type == 'checkbox')
		{
			checked = this.element.checked;
		}
		
		return checked;
	};
	
	Input.prototype.getValue = function()
	{
		return this.element.value.trim();
	};
	
	Input.prototype.getLength = function()
	{
		return this.value.length;
	};
	
	Input.prototype.getMinLength = function()
	{
		var minLength = false;
		
		if(this.element.getAttribute('minlength'))
		{
			minLength = this.element.getAttribute('minlength');
		}
		
		return minLength;
	};
	
	Input.prototype.getMaxLength = function()
	{
		var maxLength = false;
		
		if(this.element.getAttribute('maxlength'))
		{
			maxLength = this.element.getAttribute('maxlength');
		}
		
		return maxLength;
	};
	
	Input.prototype.getDataType = function()
	{
		return this.element.getAttribute('datatype') ? this.element.getAttribute('datatype') : false;
	};
	
	Input.prototype.getErrorMsgs = function()
	{
		var errorMsgs = false;
		
		if(this.element.getAttribute('errormsg'))
		{
			var temp2;
			var temp = this.element.getAttribute('errormsg');
			temp = temp.replace(/(;)$/g,"");
			errorMsgs = new Array();
			if(temp.indexOf(';') > 0)
			{
				temp = temp.split(';');
				for(var i = 0; i < temp.length; i++)
				{
					if(temp[i].indexOf(':') > 0)
					{
						temp2 = temp[i].split(':');
						errorMsgs[temp2[0]] = temp2[1];
					}
				}
			}
			else if(temp.indexOf(':') > 0)
			{
				temp = temp.split(':');
				errorMsgs[temp[0]] = temp[1];
			}
			else
			{
				errorMsgs['all'] = temp;
			}
		}
		
		return errorMsgs;
	};
	
	Input.prototype.getCustomValidator = function()
	{
		var customValidator = false;
		
		if(this.element.getAttribute('customvalidator'))
		{
			customValidator = this.element.getAttribute('customvalidator');
		}
		
		return customValidator;
	};
	
	Input.prototype.setGroupValidated = function(bln)
	{
		this.groupValidated = bln;
	};
	
	Input.prototype.setValidated = function(bln)
	{
		this.validated = bln;
	};
	
	
/****
 * String Extensions
 */
	
	String.prototype.trim = function()
	{
		return this.replace(/^(\s*)|(\s*)$/g, "");
	};
	
	String.prototype.trimLeft = function()
	{
		return this.replace(/^(\s*)/g, "");
	};
	
	String.prototype.trimRight = function()
	{
		return this.replace(/(\s*)$/g, "");
	};




/****
 * Custom Error Handler
 */
function aoeFormErrorHandler(errors,name) {
	var errs, ul, li, id, a, t;
	var errorDiv = document.getElementById('errorMessage');
	
	if(errorDiv.getElementsByTagName('ul')[0])
	{
		errorDiv.removeChild(errorDiv.getElementsByTagName('ul')[0]);
	}
	
	ul = document.createElement('ul');
	errorDiv.appendChild(ul);
	
	for(var i = 0; i < errors.length; i++)
	{
		id = errors[i]['id'];
		input = document.getElementById(errors[i]['id']);
		input.style.backgroundColor = '#695C4D';
		input.style.color = '#fff';
		input.onfocus = function(e) {
			this.style.backgroundColor = '#fff';
			this.style.color = '#000';
		};
		
		li = document.createElement('li');
		a = document.createElement('a');
		a.setAttribute('href','#'+id);
		a.setAttribute('onclick','jumpTo("'+id+'");return false;');
		t = document.createTextNode(errors[i]['msg']);
		
		a.appendChild(t);
		li.appendChild(a);
		ul.appendChild(li);
	}
	
	errorDiv.style.display = 'block';
	//document.location.href = '#errorMessage';
}

function jumpTo(id) {
	var input = document.getElementById(id);
	input.focus();
	return false;
}


function checkURL(inputs, index) {
	var errorType = false;
	
	var value = inputs[index].value;
	
	if(value.indexOf('http') >= 0){	
		errorType = true;
	}
	else
	{
		if(value.indexOf('/') == 0 && value.indexOf('.') > 1)
		{
			errorType = true;
		}
		else if(value.indexOf('#') == 0)
		{
			errorType = true;
		}
		else
		{
			errorType = 'nonURL';
		}	
		
	}
	
	return errorType;
}
