// ==============================================================================
//                     JavaScript Form Validation Libarary
// ==============================================================================
// Validate the fields on a form using specific, custom attributes
// required="true" => this field must have something in it. Use this
//	in combination with another rule normally.
// validate="rule" => validate this field, provided it has a rule and is a text object
// validate="rule" => use the rule specified to validate this input "value"
//	Options for "rule":
//		"txt"		= Text Only (and spaces)
//		"num"		= Numbers Only (allow decimal)
//		"int"		= Integers only
//		"phone" 	= Phone number, (0-9()-+)
//		"date"		= Date Format (dd/mm/yyyy)
//		"email" 	= Email addresses
//		"alpha" 	= Alpha-Numeric Values
//		"curr"		= Currency ($xxxx..."."xx)
//		"addr"  	= Address characters (a-zA-Z0-9-,#)
//		"name" 		= Most likely characters in a name  (a-zA-Z,.-)
//		"free"		= Freeform text (a-zA-Z0-9+-_()[]/&)
//		"nobadsql"	= Denies harmful SQL syntax
//		"notnull"	= Must enter something
//		RegExp		= Any valid regular expression you may specify
//					ie. "/^[\d]{0,3}.[\d]{0,3}.[\d]{0,3}$/" = an IP number
// display="Extended String" => the display name for the field if validation fails. (without a
//	newline character at the end or a bullet at the start, they are added)
//
//  elements
//		"required"  = Field is required, only works for text fields now
//      "condition" = one field is dependant on another
//      "display"      = Display Name
//
// ==============================================================================


// Default Operational Values, Modify these to change default actions of
// the data validation library.

// This is the rule to use to validate a value if none is specified.
defaultValidationRule	= "free";

// This is the first part of the error message, set to blank to just show a list
// of errors.
defaultErrorPrefix	= "This form cannot be submitted with the following errors;\n";

// [ELEMENT] is replaced with the field name when displayed.
defaultErrorMessage	= "The value in the '[ELEMENT]' field is invalid.";


// ==============================================================================
//							   ACTUAL VALIDATION CODE
// ==============================================================================

// Actual code for determining and then applying a regular expression
// to the designated form element. Returns true if the pattern matches,
// false if it doesn't.
function validateField(fieldValue, validation_code) {
	// This is a switch used for reversing the desired regular
	// expression result.
	// Set to "true" if matching a regexp successfully should
	// result in a failure.
	invertResult = false;

	// Check if they supplied a custom RegExp or just a rule name
	if (validation_code.charAt(0) != "/") {
		// These are the pre-defined rules available, you can
		// edit this list by using one as an example.
		switch (validation_code.toLowerCase()) {
			case "txt" : 			// Text only allowed
				re = /^[a-z ]*$/i
				break;
			case "num" :			// Only numbers (allow decimal)
				re = /^[\d\.]*$/
				break;
			case "int" :			// Integers only
				re = /^\d*$/
				break;
			case "phone" :			// Phone number characters
				re = /^[\d \-\(\)\+]*$/
				break;
			case "date" :			// Well-formed Dates
				re = /^(()|(\d{1,2}(\/|-)\d{1,2}(\/|-)\d{4}))$/
				break;
			case "email" :			// Something valid-ish for an email
				re = /^[a-z\d\.\-_]+@[a-z\d\.\-_]{2,}\.[a-z]{2,10}$/i
				break;
			case "alpha" :			// Alpha-numeric characters
				re = /^[\w ]*$/i
				break;
			case "curr" :			// Currency (using "$", "," and ".")
				re = /^\$?\d*,?\.?\d{0,2}$/i
				break;
			case "addr" :			// Base Address rule
				re = /^[\w- \.,#]*$/gi
				break;
			case "name" :			// Good for validating names
				re = /^[a-z,\-\. ]*$/gi
				break;
/*			case "free" :			// Freeform, text, num and some chars
				re = /^[\w\-\+\(\)\[\]\\/&, ]*$/i
				break;*/
			case "nobadsql" :		// Denies SQL which could be harmful
				re = /((delete|drop|update|replace|kill|lock) )/gi
				invertResult = true;
				break;
			case "notnull" :		// Requires *anything*
				re = /.+/
				break;
			/*default :			// Default - See "free"
				re = /^[\w\-\+\(\)\[\]\\/&, ]*$/i*/
		}
	}
	
	// This means they specified a RegExp of their own, which should be
	// in the form "/<RegExp>/" and needs to be "eval"ed before using.
	else {
		re = eval(validation_code);
	}
	
	// Do the actual regular expression testing against the string
	// and return the result.
	if (re.test(fieldValue)) {
		// If this rule uses result inversion, then return the opposite
		if (invertResult == true) {
			return false;
		}
		else {
			return true;
		}
	}
	else {
		// If this rule uses result inversion, then return the opposite
		if (invertResult == true) {
			return true;
		}
		else {
			return false;
		}
	}
}



// This is the mainline function which needs to be called on form submission to
// check required fields
function validateForm(theForm) {
	masterErrorMsg = defaultErrorPrefix;
	checkedFields = new Array()
	availableRules= "+txt+num+int+phone+date+email+alpha+curr+"
	//checkedFields = new Array('', '', '');
	// Loop through every element in the form specified.
	for (i = 0; i < theForm.elements.length;i++) {
	//alert(theForm.elements[i].name)
	rule = "";
	msg  = "";
	
	//Check to see if the field is required
	error = false
	if (!in_array("+" + theForm.elements[i].name, checkedFields) && theForm.elements[i].attributes.required) {
		checkedFields[checkedFields.length] = "+" + theForm.elements[i].name
		if (theForm.elements[i].type.toLowerCase().substr(0,4) == "text" && theForm.elements[i].value == "") {
			error=true;
		}
		
		if (theForm.elements[i].type.toLowerCase().substr(0,6) =="select"  && !theForm.elements[i].selectedIndex ) {
			error=true;
		}
		
		if (error) { 
			theForm.elements[i].style.backgroundColor="#ffffcc"
			if (theForm.elements[i].attributes.display && theForm.elements[i].attributes.display.value != "")
				masterErrorMsg += "  - " + theForm.elements[i].attributes.display.value + " Is a required field \n"
			else
			 	masterErrorMsg += "  - " + theForm.elements[i].name + " Is a required field \n"
		}
	}
	
	//Check to see if there is a condtion where 
	//if another form field is populated then this 
	//field is required. ie if a check box of other is 
	//checked then require some text input field. 
	//valid only for check boxes or radio buttons
	//reqires to params, the name of the form element to check is value 
	//and the value of that form element to check for. 
	if (theForm.elements[i].attributes.condition && !in_array("+" + theForm.elements[i].attributes.field.value, checkedFields)) {
		checkedFields[checkedFields.length] = "+" + theForm.elements[i].attributes.field.value
		error = false
		formField = theForm.elements[i].attributes.field.value;
		if(theForm.elements[i].type.toLowerCase() == "checkbox" && theForm.elements[i].checked) {
			alert("checkBox")
			if ( !theForm[formField]) {
				error = true		
			}
			else {
				if (theForm.elements[i].type.toLowerCase().substr(0,4) == "text" && theForm[formField].value =="" ) 
					error = true
			}
		}
		
		if (error) {
			if (theForm[formField].attributes.display && theForm[formField].attributes.display.value != "")
				masterErrorMsg += "  - " + theForm[formField].attributes.display.value + " Is a required field \n"
			else
			 	masterErrorMsg += "  - " + theForm[formField] + " Is a required field \n"
				
			theForm[formField].style.backgroundColor="#ffffcc"
		}
			
	}



	if (theForm.elements[i].attributes.validate) {
		checkedFields[checkedFields.length] = "+" + theForm.elements[i].name
		if (theForm.elements[i].type.toLowerCase().substr(0,4) == "text" && theForm.elements[i].value !="")
			rule = theForm.elements[i].attributes.validate.value.toLowerCase() 
			if (rule.length > 0  && availableRules.indexOf("+"+rule+"+") != -1) {
				if (!validateField(theForm.elements[i].value, rule) ) {
					if (theForm.elements[i].attributes.display && theForm.elements[i].attributes.display.value != "")
						masterErrorMsg += "  - " + defaultErrorMessage.replace('\[ELEMENT\]', theForm.elements[i].attributes.display.value)+ "\n";
					else
						masterErrorMsg += "  - " + defaultErrorMessage.replace('\[ELEMENT\]', theForm.elements[i].name)+ "\n";
					
					theForm.elements[i].style.backgroundColor="#ffffcc"
				}
			}
	//	   Do we really want "programming error to show in the popup????
	//  else {
	//		masterErrorMsg+= "  - Programming error, invalid or no rule associated with validation for "+theForm.elements[i].name+"\n"
	//		theForm.elements[i].style.backgroundColor="#ffffcc"
	//		}
	}
	
}		
	
	
	// This checks the current error message against the default,
	// if it has been changed (which means an error occurred) then
	// it displays the error and then returns false to prevent the
	// form from being submitted.
	if (masterErrorMsg != defaultErrorPrefix) {
		// Consider implementing customisable pop-up window
		// here rather than basic alert.
		// Customise window title, colours
		alert(masterErrorMsg);
		return false;
	}
	// If the current error message is the same as the default,
	// then that means nothing has gone wrong, so just return
	// true and allow the form to be submitted and be processed
	// as per normal.
	else {
			return true;
	}
}


// Returns true or false based on whether the specified string is found
// in the array.
// This is based on the PHP function of the same name.
function in_array(stringToSearch, arrayToSearch) {
	for (s = 0; s < arrayToSearch.length; s++) {
		thisEntry = arrayToSearch[s];
		if (thisEntry.indexOf(stringToSearch) != -1) {
			return true;
			exit;
		}
	}
	return false;
}


