/***********************************************************************************************
filename    : validForm2.js
description : generic form validation script
author      : Phil Ewington (phil@n-igma.net) - http://www.n-igma.net
************************************************************************************************
revision history
************************************************************************************************
history     : added ability to remove required fields (1.1)
history     : added xOf functionality for checkboxes (1.1)
history     : added Focus() on error and style change via classes (1.2)
history     : added errorHandler functionality; now runs a custom function on error (1.2)
history     : added validation for hidden fields; useful when forms have additional fields in 
              popup windows (1.2)
history     : changed to OOP and added taskList to ensure correct order of validation (2)
history     : moved type validation to custom functions to save unwanted overhead (2)
history     : moved style changes to errHandler (2)        
history		: updated xOf to handle multiple xOf checks in form
history		: updated addItem and validation functions to make an alert message optional (2.1) (AH)
history		: changed to stop the script trying to focus on hidden or disabled fields
history		: changed remoteItem functions to allow flag to suppress alert if not existing
history		: updated required functions to skip through to the end function  if there is no error msg supplied (WM)
history		: added error checking for warning developer if they have faild to provide to BOTH sErrorMsg & sFunction (WM)
************************************************************************************************/

// object constructor
function validForm(formIndex, sEndFunction) {
    // initialise object properties
    this.taskList = new Array();
    this.formIndex = formIndex;
    this.endFunction = sEndFunction ? sEndFunction : null;
    this.submitted  = false;

    // define object methods
    this.addItem = addItem;                         // adds required field to tasklist
    this.xOfAddItem = xOfAddItem;                   // adds an xOf element to the tasklist
    this.removeItem = removeItem;                   // removes an item from tasklist
    this.validate = validate;                       // start validation process
}

// adds a required field to the taskList
function addItem(sField, sErrorMsg, sFunction, sErrHandler) {

    this.taskList[this.taskList.length] = new Array;
    this.taskList[this.taskList.length-1][0] = 'required';
    this.taskList[this.taskList.length-1][1] = sField;
	if (sErrorMsg) {
    	this.taskList[this.taskList.length-1][2] = sErrorMsg;
	}
    if (sFunction) {
        this.taskList[this.taskList.length-1][3] = sFunction;
    }
    if (sErrHandler) {
        this.taskList[this.taskList.length-1][4] = sErrHandler;
    }
}

// removes an item from the taskList
function removeItem(sField,bSuppressError) {
 
    for (var i = 0; i < this.taskList.length; i++) {
        if (this.taskList[i][1] == sField) {
            return this.taskList.splice(i, 1);
        }
    }
    return false;
}

// adds an xOf element to the taskList
function xOfAddItem(sField, iCount, iMax, sErrorMsg, sFunction, sErrHandler) {

    this.taskList[this.taskList.length]= new Array();
    this.taskList[this.taskList.length-1][0] = 'xOf';
    this.taskList[this.taskList.length-1][1] = sField;
    this.taskList[this.taskList.length-1][2] = iCount;
    this.taskList[this.taskList.length-1][3] = iMax;
    this.taskList[this.taskList.length-1][4] = sErrorMsg;
    if (sFunction) {
        this.taskList[this.taskList.length-1][5] = sFunction;
    }
    if (sErrHandler) {
        this.taskList[this.taskList.length-1][6] = sErrHandler;
    }
}

// validates ietms in taskList
function validate() {

	// nothing to do if taskList is empty
	if (!this.taskList.length) {
		return true;
	}
	
	// do not allow submit button to be pressed more than once
	if (this.submitted) {
		alert('The form has already been submitted, please wait for a response from the remote server.');
		return false;
	}

	for (var i = 0; i < this.taskList.length; i++) {
	
		// set current field from taskList
		if(isNaN(this.formIndex))
		{
			currentField = eval('document.forms[\''+this.formIndex+'\'].elements[\''+ this.taskList[i][1] + '\']');
		}
		else
		{
			currentField = eval('document.forms['+this.formIndex+'].elements[\''+ this.taskList[i][1] + '\']');
		}
		
		// determine validationType
		switch (this.taskList[i][0]) {

			case "required":
			
				// check that developers have not called addItem with an invalid parameter combination
				if(!this.taskList[i][2] && !this.taskList[i][3])
				{
					alert('You cannot supply a non-required field without and end function.');
					this.submitted = false;
					return false;
				}
				
				// validation of text,password,file,textarea,hidden fields
				if (currentField.type == "text" || currentField.type == "password" ||
					currentField.type == "textarea" || currentField.type == "file" ||
					currentField.type == "hidden") {
					if (!currentField.value || currentField.value == null) {
						if(this.taskList[i][2]) {
							alert(this.taskList[i][2]);
							
							//focus on field if not hidden or disabled
							if(currentField.type != "hidden" && !currentField.disabled) {
								currentField.focus();
							}
							this.submitted = false;
							
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;							
						}
					}
					// custom function check
					if(currentField.value && currentField.value != null)
					{
						if (this.taskList[i][3] && !eval(this.taskList[i][3])) {
							this.submitted = false;
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;
						}
					}
				}

				// validation of select lists
				if (currentField.type == "select-one" || currentField.type == "select-multiple") {
					if (currentField.value == "null") {
						if(this.taskList[i][2]) {
							alert(this.taskList[i][2]);
								
							if(!currentField.disabled) {
								currentField.focus();
							}
							this.submitted = false;
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;							
						} 
						
					}
					if(currentField.value && currentField.value != null)
					{					
						// custom function check
						if (this.taskList[i][3] && !eval(this.taskList[i][3])) {
							this.submitted = false;
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;
						}
					}
				}

				// validation of checkboxes
				if (currentField.type == "checkbox") {
					if (!currentField.checked) {
						if(this.taskList[i][2]) {
							alert(this.taskList[i][2]);

							this.submitted = false;
							currentField.focus();
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;							
						}
					}
					if(currentField.value && currentField.value != null)
					{					
						// custom function check
						if (this.taskList[i][3] && !eval(this.taskList[i][3])) {
							this.submitted = false;
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;
						}
					}
				}

				// validation of radio buttons
				if (currentField.length && currentField[0].type == "radio") {
					var optionSelected = false;
					for (var j = 0; j < currentField.length; j++) {
						if (currentField[j].checked) {
							optionSelected = true;
							break;
						}
					}
					if (!optionSelected) {
						if(this.taskList[i][2]) {
							alert(this.taskList[i][2]);
							
							this.submitted = false;
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;
						}
					}
					if(optionSelected)
					{					
						// custom function check
						if (this.taskList[i][3] && !eval(this.taskList[i][3])) {
							this.submitted = false;
							//errHandler
							if (this.taskList[i][4]) {
								eval(this.taskList[i][4]);
							}
							return false;
						}
					}
				}
				break;

			case "xOf":
				var noSelected = 0;
				for (var k = 0; k < currentField.length; k++) {
					if (currentField[k].checked) {
						noSelected++;
					}
				}
				// check for max value
				if (noSelected < this.taskList[i][2] || 
					(this.taskList[i][3] && noSelected > this.taskList[i][3])) {
					if(this.taskList[i][4]) {
						alert(this.taskList[i][4]);
					}
					this.submitted = false;
					return false;
				}
				// custom function check
				if (this.taskList[i][3] && !eval(this.taskList[i][3])) {
					this.submitted = false;
					return false;
				}
		}
	}
	// ensure form is only submitted once
	this.submitted = true;
	
	// check for end function
	if (this.endFunction) {
		return eval(this.endFunction);
	}
	return true;
}	