// class definition

function AdzFormValidator(form){
	this.useForm(form);
	this.elements = new Array();
	this.isValid = false;
	this.select = true;
	this.focus = true;
	this.func = null;
}

function AdzFormElement(name){
	this.name = name;
	this.desc = name;
	this.required = false;
	this.trim = false; // only used with text fields
	this.stripWhite = false; // only used with text fields
	this.isValid = false;
	this.alert = "The field '$desc' is required";
}

AdzFormElement.prototype.validate = function(element){
	
	var i, objForm;
	
	this.type = element.type;

	if (!this.type) this.type = element[0].type;

	this.isValid = true;

	objForm = element.form;
	
	// -- check if field is empty

	switch (this.type){

		case "text":
		case "textarea":
		case "hidden":
		case "file":
			if (this.stripWhite) element.value = element.value.replace(/\s/g,'');
			if (this.trim) element.value = element.value.trim();
			this.data = !isBlank(element.value);
			this.empty = (element.value == '');
			break;

		case "checkbox":
		case "radio":
			this.empty = !(this.data = false);
			if (element.checked){
				this.empty = !(this.data = true);
				break;
			}
			for (i=0;i<element.length;i++){
				if (element[i].checked){
					this.empty = !(this.data = true);
					break;
				}
			}
			break;

		case "select-multiple":
			this.empty = !(this.data = false);
			for (i=0;i<element.length;i++) {
				if (element[i].selected){
					this.empty = !(this.data = true);
					break;
				}
			}
			break;

		default:
			// can't detect a valid / invalid response
			this.empty = !(this.data = true);
	}
	
	if (this.required && !this.data){
		//alert("required, no data");
		this.isValid = false;
	} else if (!this.required && this.empty){
		//alert("not required, is empty");
		this.isValid = true;
	} else if (this.data){
		//alert("has data, may or may not be required");
		if (!this.restrictType){
			//alert("has data, may or may not be required - no restrict");
			this.isValid = true;
		} else {
			//alert("has data, may or may not be required - has restrict");
			// -- validate the restriction
			switch (this.restrictType.toLowerCase()){

				case "numeric":
				case "number":
				case "num":
					this.isValid = isNumeric(element.value);
					if (this.isValid && this.restrictCond.length > 0) this.isValid = eval(this.restrictCond.replace(/n/g,element.value));
					break;
				
				case "int":
				case "integer":
					this.isValid = isInteger(element.value);
					if (this.isValid && this.restrictCond.length > 0) this.isValid = eval(this.restrictCond.replace(/n/g,element.value));
					break;
					
				case "length":
					this.isValid = eval(this.restrictCond.replace(/n/g,element.value.length));
					break;
	
				case "selected":
					this.isValid = eval(this.restrictCond.replace(/n/g,element.selectedIndex));
					break;
	
				case "email":
					this.isValid = isEmail(element.value);
					break;
	
				case "regex":
					this.restrictCond = (typeof this.restrictCond == "string") ? new RegExp(this.restrictCond,"i") : this.restrictCond;
					this.isValid = this.restrictCond.test(element.value);
					break;
					
				case "in_array":
					this.isValid = this.restrictCond.in_array(element.value);
					break;
					
				case "!in_array":
					this.isValid = !this.restrictCond.in_array(element.value);
					break;
					
				case "currency":
					this.isValid = isCurrency(element.value);
					break;
					
				case "date":
					this.isValid = validateDateString(element.value,this.restrictCond);
					break;
					
			}
		}
	} else {
		//alert("else");
		this.isValid = true;
	}
	
	//alert("field "+element.name+" is "+(this.isValid?"valid":"invalid"));
	
	return this.isValid;
}

AdzFormElement.prototype.restrict = function(type,cond){
	this.restrictType = type;
	this.restrictCond = cond;
}

AdzFormElement.prototype.showAlert = function(){
	var strAlert = this.alert;
	if (strAlert !== false){
		strAlert = strAlert.replace(/\$desc/g, this.desc);
		strAlert = strAlert.replace(/\$name/g, this.name);
		strAlert = strAlert.replace(/\$type/g, this.type);
		alert(strAlert);
	}
}

AdzFormValidator.prototype.useForm = function(form){
	if (typeof form != "undefined"){
		if (typeof form == "object"){
			this.form = form;
		} else if (typeof form == "string" || typeof form == "number"){
			this.form = document.forms[form];
		} else {
			alert("AdzFormValidator :: DEVELOPER ALERT :: useForm()\n\nInvalid form definition");
		}
	}
}

AdzFormValidator.prototype.add = function(name){
	if (!this[name] && !isBlank(name)){
		this[name] = new AdzFormElement(name);
		this.elements[this.elements.length] = this[name];
	}
}

AdzFormValidator.prototype.addAll = function(){
	var i, field;

	if (!this.form){
		alert("AdzFormValidator :: DEVELOPER ALERT :: addAll()\n\nNo form selected!\n\nuse: obj.useForm('formName');");
	} else {
		// run through all of the form elements and add each one that has a name and is not a button
		for (i=0;i<this.form.elements.length;i++){
			field = this.form.elements[i];
			if (!isBlank(field.name)){
				if (field.type != "button" && field.type != "submit" && field.type != "reset"){
					this.add(field.name);
				}
			}
		}

	}

}

AdzFormValidator.prototype.remove = function(element){
	var i, temp_array;
	temp_array = new Array();
 	for (i=0;i<this.elements.length;i++){
 		if (this.elements[i].name != element){
			temp_array[temp_array.length] = this.elements[i];
     		}
  	}
	this.elements = temp_array;
	delete this[name];
}

AdzFormValidator.prototype.removeElementsByType = function(){
	var i, j, args;
	
	args = AdzFormValidator.prototype.removeElementsByType.arguments;
	
	// traverse through form and remove from AdzFormValidator object
	// if type of element is in the function arguments
	for (i=0;i<args.length;i++){
		for (j=0;j<this.form.elements.length;j++){
			if (this.form.elements[j].type == args[i]){
				this.remove(this.form.elements[j].name);
			}
		}
	}
}

AdzFormValidator.prototype.removeAll = function(){
	var i;

	// remove elements from validation object
	for (i in this.elements){
		delete this[this.elements[i].name];
	}

	// reset elements array
	this.elements = new Array();
}

AdzFormValidator.prototype.describe = function(){
	var element, i;
	var strMsg = "";

	if (this.elements.length == 0){
		strMsg += "No form elements!";
	} else {
		strMsg += "List of form elements:\n";
		for (i=0;i<this.elements.length;i++){
			element = this.elements[i];
			strMsg += " - "+element.name+" : "+element.restrictType+"\n";
		}
	}
	alert(strMsg);
}

AdzFormValidator.prototype.setGlobal = function(){
	
	var args, setType, setValue, element, i;
	
	args = AdzFormValidator.prototype.setGlobal.arguments;
	
	if (args.length <= 1){
		alert("AdzFormValidator :: DEVELOPER ALERT :: setGlobal()\n\nIncorrect number of arguments supplied.");
		return false;
	}
	
	setType = args[0];
	setValue = args[1];
	
	// run through all of the form elements and add each one that has a name and is not a button
	for (i=0;i<this.elements.length;i++){
		element = this.elements[i];
		
		switch (setType.toLowerCase()){
			
			// put custom ones in here
			
			// generic
			
			default:
				element[setType] = setValue;
				break;
			
		}
		
	}

}

AdzFormValidator.prototype.setUserFunction = function(func,func_alert){
	this.func = func;
	this.func_alert = func_alert;
}


AdzFormValidator.prototype.validate = function(){
	var element, elementName, formElement, i;
	
	if (!this.form){
		alert("AdzFormValidator :: DEVELOPER ALERT :: validate()\n\nNo form selected!\n\nuse: obj.useForm('formName');");
		this.isValid = false;
		return false;
	} else {
		// -- start the validation
		this.isValid = true;
		for (i=0;i<this.elements.length;i++){
			
			element = this.elements[i];
			elementName = element.name;
			formElement = this.form.elements[elementName];
			
			// -- return false if invalid element
			if (formElement.type == "button" || formElement.type == "submit" || formElement.type == "reset"){
				this.debug(this.DEBUG_DEV,"validate()","Form element '"+formElement.name+"' is of an unacceptable type.\n\nAdzFormValidator will not validate buttons.");
				this.isValid = false;
				return false;
			}

			// -- validate the form element
			if ( !element.validate(formElement) ) {

				this.isValid = false;
				element.showAlert();
				if (formElement.focus && this.focus && !formElement.disabled) formElement.focus();
				if (formElement.select && this.select && !formElement.disabled) formElement.select();
				return false;
			}
		}
		
		// -- call the user defined function to validate more advanced stuff
		if (this.func){
			if (!this.func(this.form)){
				alert(this.func_alert);
				this.isValid = false;
			}
		}
		
		return this.isValid;
	}
}

// these are all our aliases (for backwards compatibility)

AdzFormValidator.prototype.addAllElements = AdzFormValidator.prototype.addAll;
AdzFormValidator.prototype.removeAllElements = AdzFormValidator.prototype.removeAll;
AdzFormValidator.prototype.addFormElement = AdzFormValidator.prototype.add;
AdzFormValidator.prototype.removeFormElement = AdzFormValidator.prototype.remove;

AdzFormValidator.prototype.DEBUG_DEV = 1;

AdzFormValidator.prototype.debug = function(type,method,message){
	switch (type){
		case this.DEBUG_DEV:
			alert("!! DEVELOPER ALERT !!\n\n"+message);
			break;
	}
}

AdzFormElement.prototype.debug = AdzFormValidator.prototype.debug;

// validation checks


function isCurrency(s){
	var re = /^\$?\d{1,3}(,?\d{3})*(\.\d{1,2})?$/;
	return re.test(s);
}

function isEmail(s) {
	var re = /^[\w-\._]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,3}$/i;
	return re.test(s);
}

function isNumeric(s){
	var re = /^[\-]?[0-9]+[.]?[0-9]*$/;
	return re.test(s);
}

function isInteger(s){
	var re = /^[\-]?[0-9]+$/;
	return re.test(s);
}
/*
function isAlpha(s){
	var re = /^[\-]?[0-9]+[.]?[0-9]*$/;
	return re.test(s);
}

function isAlphaNumeric(s){
	var re = /^[\-]?[0-9]+[.]?[0-9]*$/;
	return re.test(s);
}*/

function isBlank(s){
	var re = /^\s*$/;
	return re.test(s);
}

String.prototype.trim = function(){
	return this.replace(/^\s+/,'').replace(/\s+$/,'');
}

Array.prototype.in_array = function(value){
	for (var i=0;i<this.length;++i)
		if (this[i].toString().toLowerCase() == value.toString().toLowerCase()) return true;
	return false;
}

if (!Array.prototype.push){
	Array.prototype.push = function(value){
		this[this.length] = value;
	}
}

function defangRegexChars(strInput){
	var find = [ "\\", ".", "^", "$", "+", "*", "?", "(", ")", "[", "]", "{", "}" ];
	for (i=0;i<find.length;i++)
		strInput = strInput.replace( new RegExp("\\"+find[i],"g") , "\\"+find[i] );
	return strInput;
}

function getFullYear(y){
	var iy = parseInt(y,10);
	if ( isNaN(iy) || iy < 0 || iy > 99 ) return y; // no hard feelings
	return iy + (iy < 50 ? 2000 : 1900);
}

function isValidDate(y,m,d){
	if (!y || !m || !d) return false;
	m--; // month is zero-indexed
	var oDate = new Date(y,m,d);
	if (oDate.getDate() == d &&
		oDate.getMonth() == m &&
		oDate.getFullYear() == y) return true;
	return false;
}

function validateDateString(strDate,strPattern){
	
	var d, m, y, yy, yyyy;
	var re, h, i, j;
	var aDate, aPositions, aMatches;
	var aMatchList, aMatchListValues, strPatternPos, tmpArr;
	
	// quick check
	
	if (!strPattern || !strDate) return false;
	
	// set our replacement regex
	
	d = "([0-3]?[0-9])";
	m = "([0-1]?[0-9])";
	y = "([0-9]{2}|[0-9]{4})";
	yy = "([0-9]{2})";
	yyyy = "([0-9]{4})";
	
	// preliminary replacements (defang regex chars);
		
	strPattern = defangRegexChars(strPattern);
	
	// we may need this later (to work out positioning)
	
	strPatternPos = strPattern;
	
	// create our pattern
	
	strPattern = strPattern.replace(/d+/gi,'d');
	strPattern = strPattern.replace(/d/gi,d);
	strPattern = strPattern.replace(/m+/gi,'m');
	strPattern = strPattern.replace(/m/gi,m);
	strPattern = strPattern.replace(/y{4,}/gi,'yyyy');
	strPattern = strPattern.replace(/y{4}/gi,yyyy);
	strPattern = strPattern.replace(/y{2,}/gi,'yy');
	strPattern = strPattern.replace(/y{2}/gi,yy);
	strPattern = strPattern.replace(/y/gi,y);
	
	// test our pattern against the string
	
	re = new RegExp('^'+strPattern+'$');
	if (!re.test(strDate)) return false;
	
	// extract date portions from our string
	
	aMatches = re.exec(strDate);

	// structure replacements
	
	strPatternPos = "#"+strPatternPos+"#";
	strPatternPos = strPatternPos.replace(/d+/gi,'#d#');
	strPatternPos = strPatternPos.replace(/m+/gi,'#m#');
	strPatternPos = strPatternPos.replace(/([^y])y([^y])/gi,'$1#y#$2');
	strPatternPos = strPatternPos.replace(/([^y])y{2,3}([^y])/gi,'$1#yy#$2');
	strPatternPos = strPatternPos.replace(/y{4,}/gi,'#yyyy#');
	
	aMatchList = ['#d#','#m#','#y#','#yy#','#yyyy#'];
	aMatchListValues = ['d','m','y','y','y'];

	tmpArr = [];
	for (h=0;h<aMatchList.length;h++){
		for (i=0;i<strPatternPos.length;i++){
			j = strPatternPos.indexOf(aMatchList[h],i);
			if (j == -1) break; // none of this character - go to next character
			// record this info - a match has been found
			tmpArr[j] = aMatchListValues[h];
			i = j;
		}
	}

	aPositions = [null]; // need this value so the index reference against aMatch is right
	for (i=0;i<tmpArr.length;i++) 
		if (tmpArr[i]) aPositions.push(tmpArr[i]);
	
	aDate = [];
	for (i=0;i<aPositions.length;i++) aDate[aPositions[i]] = aMatches[i];

	return isValidDate(getFullYear(aDate.y), aDate.m, aDate.d);
	
}


/*************************************
switch (field.type){

	case "text":
		alert("text field");
		break;

	case "textarea":
		alert("textarea");
		break;

	case "checkbox":
		alert("checkbox");
		break;

	case "select-one":
		alert("select-one");
		break;

	case "select-multiple":
		alert("select-multiple");
		break;

	case "radio":
		alert("radio");
		break;

	case "file":
		alert("file");
		break;

	case "hidden":
		alert("hidden");
		break;

	case "button":
		alert("button");
		break;

	case "submit":
		alert("submit");
		break;

	case "reset":
		alert("reset");
		break;

	default:
		alert("what the hell is this??");

}**********************************/


