﻿/*
VERSION:
10/20/2009	1.0.6 Moved the `validateString` method to a global function called `isValid` and made validateString an alias. 
10/5/2009 	1.0.5 Updated class so that instead of using border and background styles to hilite an element, 
						it updates the className of the element.
12/13/2008	1.0.4 Added methods `clearHilite` and `setHilite`. 
					Added 4th argument to`addItem` - "hiliteElem".
					Reorganized several methods.
10/22/2008	1.0.3 Changed the `validationType` argument of method `addItem` to `validator`, 
					and gave it the ability to also pass a callback function.
					Removed the 4th argument `comparison`, but it can still be passed for a `validator` of type "select".
					Updated method `isChecked`. Updated some error trapping. Streamlined the file a bit.
7/10/2008		1.0.2	Reversed the array before joining the string at the end of method `callAlert`.
3/5/2008		1.0.1	Updated 'numeric' regular expression to accept
					negative and decimal numeric values
					1.0.0	Begin versioning.
*/
function Validation(formRef, alertType) {
//this object is for simple validation only
//if an item's validation relies upon multiple factors
//other than its own value, then other means are necessary
//as of this writing
this.f = formRef;
this.alertType = alertType; //1 = window.alert only, 2 = hilite only, anything else = both
this.alertOverride = ''; //will replace default alert if present
this.hiliteClass = 'hilite';
this.inputs = new Array();

/**METHODS**/
/*
The argument `validator` can be of type string or function.
If `validator` is a function, it passes the element itself to the callback, which should return a true or false value.
If `validator` is a string (defaults to "string"), it must be one of the built-in validators listed in method `validateString`.
The following built-in validators have different behaviors:
	select - returns true if select.selectedIndex > 0. If a 5th argument is provided, returns true if selected option value matches argument.
	selectmultiple - returns true if at least one item is selected.
	radio, checkbox - returns true if at least one item is checked.
`hiliteElem	` is an optional string id of a an html element to hilite alternatively to the element itself.
*/
this.addItem = function(formElementName, alertMsg, validator, hiliteElem) {

if(this.f[formElementName] == undefined) {
	alert(('The element "' + formElementName + '" does not exist.'));
	return false;
}
this.inputs.push({	
	elem: formElementName, //string req
	msg: alertMsg, //req
	val: validator, //opt
	comparison: arguments[4], //opt
	ok: true, //internal use,
	he: hiliteElem
});
};
this.callAlert = function() {
	var arMsg = new Array();
	var thisItem;
	var thisElem;
	for(var i = this.inputs.length-1; i >= 0; i--) {
		thisItem = this.inputs[i];
		thisElem = typeof(thisItem.he) == 'string' && thisItem.he != '' ? document.getElementById(thisItem.he) : this.f[thisItem.elem];
		if(!thisItem.ok) {
			switch(this.alertType) {
				case 1 :
					arMsg.push(thisItem.msg);
					break;
				case 2 :
					this.setHilite(thisElem);
					break;
				default :
					arMsg.push(thisItem.msg);
					this.setHilite(thisElem);
					break;
			}
		}
	}
	if(arMsg.length > 0) {
		if(this.alertOverride.length > 0) {
			window.alert(this.alertOverride);
			return false;
		}
		window.alert('Please correct the following:\n' + arMsg.reverse().join('\n'));
	}
	return false;
};
this.clearHilite = function(elem) {
	if(elem['className']) {
		var cn = elem.className+'';
		cn = cn.replace(this.hiliteClass, '').replace(/^\s+/,'').replace(/\s+$/,'');
		elem.className = cn;
	}
};
this.clearHilites = function() {
	var inp;
	for(var i = this.inputs.length-1; i >= 0; i--) {
		inp = this.inputs[i];
		inp = typeof(inp.he) == 'string' && inp.he != '' ? document.getElementById(inp.he) : this.f[inp.elem];
		this.clearHilite(inp);
	}
};
this.isChecked = function(checkGroup) {
	/**** RETURNS FALSE IF NO BOXES ARE CHECKED, TRUE OTHERWISE ****/
	if(checkGroup === undefined) { return false; }
	if(typeof(checkGroup[0]) == 'undefined') {
		return checkGroup.checked; //if only one element in the group
	}
	for(var i = checkGroup.length-1; i >= 0; i--) {
		if(checkGroup[i].checked) {
			return true;
		}
	}
	return false;
};
this.setHilite = function(elem) {
	var cn = elem.className+'';
	if(cn.indexOf(this.hiliteClass) == -1) {
		cn += (cn != '' ? ' ' : '') + this.hiliteClass;
	}
	elem.className = cn;
};
this.validate = function() {
	this.clearHilites();
	var i = this.inputs.length-1;
	var thisItem;
	var thisElem;
	var er = false;
	while(i >= 0) {
		thisItem = this.inputs[i];
		if(typeof(this.f[thisItem.elem]) == 'undefined') { alert('Error: The item \'' + thisItem.elem + '\' is not a valid field.'); i--; continue; }
		thisElem = this.f[thisItem.elem];
		if(typeof(thisItem.val) != 'function') {
			switch(thisItem.val) {
			case 'radio' : case 'checkbox' :
				thisItem.ok = this.isChecked(thisElem);
				break;
			case 'select' :
				if(thisItem.comparison === undefined) {
					if(thisElem.selectedIndex == 0) {
						thisItem.ok = false;
					}
				} else {
					if(thisElem[thisElem.selectedIndex].value != thisItem.comparison) {
						thisItem.ok = false;
					}
				}
				break;
			case 'selectmultiple' :
				if(thisElem.selectedIndex == -1) {
					thisItem.ok = false;
				}
				break;
			default :
				thisItem.ok = this.validateString(thisElem.value, thisItem.val);
			} //end switch
		} else {
			thisItem.ok = thisItem.val(thisElem);
		}
		if(!thisItem.ok) { er = true; }
		i--;
	}
	if(er == true) {
		return this.callAlert();
	} else {
		return true;
	}
	
};
this.validateString = function(str, validator) {
	return isValid(str, validator);
};

}; //end Validation

function isValid(str, validator) {
if(typeof(str) != 'string' && typeof(str) != 'number') { return false; }
var rex;
switch(validator) {
case '' : //any non-empty string
	var v = str.replace(/^\s+/,'').replace(/\s+$/,''); //trim spaces
	return (v.length > 0 ? true : false);
case 'phone_full' : //xxX-XXX-XXX-XXXX w/req country code digits, strict
	rex  = /^\d{1,3}\-\d{3}\-\d{3}\-\d{4}$/;
	break;
case 'phone' :  //xxx-XXX-XXX-XXXX w/opt country code digits and req area code.
	rex  = /^(\d{1,3}\-)?\d{3}\-\d{3}\-\d{4}$/;
	break;
case 'phone_part' :  //xxx-XXX-XXXX w/opt area code
	rex  = /^(\d{3}\-)?\d{3}\-\d{4}$/;
	break;
case 'email' :
	rex  = /^[a-z0-9]([a-z0-9_\-\.]*)@([a-z0-9_\-\.]*)(\.[a-z]{2,3}(\.[a-z]{2}){0,2})$/i;
	break;
case 'state' :
	rex = /^(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NB|NC|ND|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY)$/i; 
	break;
case 'zip' :
	rex  = /(^\d{5}$)|(^\d{5}-\d{4}$)/;
	break;
case 'ssn' :
	rex  = /^\d{3}\-\d{2}\-\d{4}$/;
	break;
case 'alpha' :
	rex = /[^A-Za-z]/;
	break;
case 'int' :
	rex = /^-?[0-9]+$/;
	break;
case 'numeric' :
	rex = /^-?[0-9]+\.?[0-9]+?$/;
	break;
case 'alphanumeric' : //numbers and letters only
	objRegExpr = /[^A-Za-z0-9]/;
	break;
case 'max' :
	if(arguments.length > 2 && !isNaN(arguments[2])) {
		if(isNaN(str)) {
			return str.length <= arguments[2];
		}
		return str <= arguments[2];
	}
case 'min' :
	if(arguments.length > 2 && !isNaN(arguments[2])) {
		if(isNaN(str)) {
			return str.length >= arguments[2];
		}
		return str >= arguments[2];
	}
default : //any non-empty string
	var v = str.replace(/^\s+/,'').replace(/\s+$/,''); //trim spaces
	return (v.length > 0 ? true : false);
} //end switch
return (str+'').search(rex) > -1;
};
