// Validation_class
// Version 0.1

function addEventListener( 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 );
}

function isArray(testObject) {   
    return testObject && !(testObject.propertyIsEnumerable('length')) && typeof testObject === 'object' && typeof testObject.length === 'number';
}

function isInput(element) {
	return (element.tagName == "INPUT") ? true : false;
}

function isSelect(element) {
	return (element.tagName == "SELECT") ? true : false;
}

/****************************************/
/***********CLASS DEFINITIONS************/
/****************************************/
/*
CLASS:		Validation
PURPOSE: 	Easily validate a form element.
PARAMETER: 	Form Element - form element to validate.
PARAMETER:	Function - callback function.
EXAMPLE:	var V = new Validation();
			var v1 = new Validator(document.forms[0].elements[0], function() {});
			v1.addValidation(function () {return false});
			V.add(v1);
			V.validate();
NOTES:		This class is really just to keep track of all the Validator objects.
*/
function Validation() {
	this.m_objects = new Array();
}

/*
FUNCTION:	add
PURPOSE:	add Validator object.
PARAMETER:	Validator Object - object of type Validator.
*/
Validation.prototype.add = function(object) {
	if (isArray(object)) {
		for (var i = 0; i < object.length; ++i) {
			this.m_objects.push(object[i]);
		}
	} else {
		this.m_objects.push(object);
	}
};

/*
FUNCTION:	get
PURPOSE:	get Validator object.
PARAMETER:	Integet - index of object in class array.
*/
Validation.prototype.get = function(index) {
	if (!index) {
		index = this.m_objects.length - 1;
	}
	return this.m_objects[index];
};

/*
FUNCTION:	size
PURPOSE:	get amount of objects in class array.
*/
Validation.prototype.size = function() {
	return this.m_objects.length;
};

/*
FUNCTION:	addEventToAll
PURPOSE:	add an event to all current objects in class array.
PARAMETER:	String - event type... IE "blur" "click" "mouseout".
*/
Validation.prototype.addEventToAll = function(evt) {
	for (var i = 0; i < this.m_objects.length; ++i) {
		this.m_objects[i].addEvent(evt);
	}
};

/*
FUNCTION:	validate
PURPOSE:	Validate all passed Validator objects.
*/
Validation.prototype.validate = function() {
	var returns = true;
	for (var i = 0; i < this.m_objects.length; ++i) {
		if (!this.m_objects[i].validate()) {
			returns = false;
		}
	}
	return returns;
};

/*
CLASS:		Validator
PURPOSE: 	Easily validate a form element.
PARAMETER: 	Form Element - form element to validate.
PARAMETER:	Function - callback function.
EXAMPLE:	var v1 = new Validator(document.forms[0].elements[0], function() {});
			v1.addValidation(function () {return false});
			v1.validate();
NOTES:		This is a wrapper class.
			It appends 2 new objects to all variables.
				1. Array - an array of methods that keeps track of 
				   all the validation required for this object.
				2. Funciton - A CALLBACK function.
							  	PARAMETER: Form Element - the form element the validation was for.
								PARAMETER: Boolean - the result of the validation attempt.
*/
function Validator(object, callback) {
	this.object = object;
	this.object.validation = new Array();
	this.object.callback = callback;
	this.object.validator = this;
}

/*
FUNCTION:	getObject
PURPOSE:	return the object.
*/
Validator.prototype.getObject = function () {
	return this.object;
};

/*
FUNCTION:	addValidation
PURPOSE:	add another validator to the object.
PARAMETER:	Function - function that will do the validation.
NOTES:		The passed function should have 1 parameter.
				PARAMETER: Form Element - The form element that will be validated.
*/
Validator.prototype.addValidation = function (method) {
	if (!method) {
		return;
	}
	this.object.validation.push(method);
};

/*
FUNCTION:	addEvent
PURPOSE:	add an event handler that will call the validate function.
PARAMETER:	String - event type... IE "blur" "click" "mouseout".
*/
Validator.prototype.addEvent = function (evt) {
	if (!evt) {
		return;
	}
	addEventListener( this.object, evt, this.validate, false );
};

/*
FUNCTION:	validate
PURPOSE:	validate the class object. Call the callback method directly after validation.
*/
Validator.prototype.validate = function(e) {
	if (!e) {
		e = window.event;
	}
	var object = (this.object) ? this.object : ((e) ? ((e.srcElement) ? e.srcElement : e.target) : null);;
	if (!object) {
		return false;
	}
	for (var i = 0; i < object.validation.length; ++i) {
		if (!object.validation[i](object)) {
			object.callback(object, false);
			return false;
		}
	}
	object.callback(object, true);
	return true;
};

/*
FUNCTION:	validate2
PURPOSE:	validate the class object. Does not Call the callback method directly after validation!!
*/
Validator.prototype.validate2 = function (e) {
	if (!e) {
		e = window.event;
	}
	var object = (this.object) ? this.object : ((e) ? ((e.srcElement) ? e.srcElement : e.target) : null);
	if (!object) {
		return false;
	}
	for (var i = 0; i < object.validation.length; ++i) {
		if (!object.validation[i](object)) {
			return false;
		}
	}
	return true;
};
/****************************************/
/************VALIDATION METHODS**********/
/****************************************/
/*
NOTES: 	These methods are created specifically for use with the 'Validator' class.
	   	They all return a function which can be used in the Validator.
EXAMPLE: var v = Validator(object, callback);
		 v.addValidation(isAlpha(val, true));
*/



Validation.prototype.isAlpha = function(whitespace) {
	var reg = null;
	if (whitespace) {
		reg = /^[a-zA-Z\s]*[a-zA-Z\s]*[a-zA-Z\s]*$/;
	} else {
		reg = /^[a-zA-Z]*[a-zA-Z]*[a-zA-Z]*$/;
	}
	return function (object) {
		return (isInput(object)) ? 
			reg.test(object.value) : reg.test(object.options[object.selectedIndex].value);
	};
}

Validation.prototype.isNumeric = function(whitespace) {
	var reg = null;
	if (whitespace) {
		reg = /^[0-9\s]*[0-9\s]*[0-9\s]*$/;
	} else {
		reg = /^[0-9]*[0-9]*[0-9]*$/;
	}
	return function (object) {
		return (isInput(object)) ? 
			reg.test(object.value) : reg.test(object.options[object.selectedIndex].value);
	};
}

Validation.prototype.isAlphaNumeric = function(whitespace) {
	var reg = null;
	if (whitespace) {
		reg = /^[a-zA-Z0-9\s]*[a-zA-Z0-9\s]*[a-zA-Z0-9\s]*$/;
	} else {
		reg = /^[a-zA-Z0-9]*[a-zA-Z0-9]*[a-zA-Z0-9]*$/;
	}
	return function (object) {
		return (isInput(object)) ? 
			reg.test(object.value) : reg.test(object.options[object.selectedIndex].value);
	};
}

Validation.prototype.isAddress = function(whitespace) {
	var reg = null;
	if (whitespace) {
		reg = /^[a-zA-Z0-9\s.]*[a-zA-Z0-9\s.]*[a-zA-Z0-9\s.]*$/;
	} else {
		reg = /^[a-zA-Z0-9]*[a-zA-Z0-9]*[a-zA-Z0-9]*$/;
	}
	return function (object) {
		return (isInput(object)) ? 
			reg.test(object.value) : reg.test(object.options[object.selectedIndex].value);
	};
}

//this allows for any type of phone number to be added. The only catch is it must be one of the main formats.
//EXAMPLES: 949 555 5555, 949 5555555, (949) 5555555, (949) 555 5555, 
//(949) 555-5555, 949 555-5555, 9495555555, 949-5555555, 949-555-5555
//These are all exceptable formats.
Validation.prototype.isPhone = function() {
	return function (object) {
		var reg = /^(([0-9]{3})|(\([0-9]{3}\)))[\s\-\|]{0,1}[0-9]{3}[\s\-\|]{0,1}([0-9]{4})$/;
		return (isInput(object)) ? 
			reg.test(object.value) : reg.test(object.options[object.selectedIndex].value);
	};
}

Validation.prototype.isEmail = function() {
	return function (object) {
		var reg = /^([a-zA-Z0-9_.-])+@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})+$/;
		return (isInput(object)) ? 
			reg.test(object.value) : reg.test(object.options[object.selectedIndex].value);
	};
}

Validation.prototype.isBounded = function(minlen, maxlen) {
	return function (object) {
		if (isInput(object) && 
			object.value.length >= minlen && 
			object.value.length <= maxlen) {
			return true;
		} else if (isSelect(object) &&
			object.options[object.selectedIndex].value.length >= minlen && 
			object.options[object.selectedIndex].value.length <= maxlen) {
			return true;
		}
		return false;
	};
}