/* Form Validation. Version 1.1 */

var warning_class = "validation_error";
var warning_element_id = "validation_error_message";
var error_msg_element;
var error_msg;
var error_count;
var form_element;

	/* Parses an array of form fields that require validation.
	 * Validates them, highlights the invalid ones (if any), 
	 * outputs some helpful messages regarding the errors onto the 
	 * HTML page calling the function (above the first form field) 
	 * if any errors exist.  
	 * 
	 * @param	array_of_params		a 2D array containing a collection
	 * of form fields that require validation, each entry includes an 
	 * internal array containing 3 values.  
	 * 1st field is the value of the form field attribute name. 
	 * 2nd field type of data the form field must contain. 
	 * 3rd field indicates if the input field MUST be completed 
	 * ("required") or can be left empty "optional".
	 *
	 * @param	form_element	the form element.
	*/
	function validate_form(array_of_params, form_element){
		this.form_element = form_element;
		error_msg = "";
		error_count = 0;
		//loop the array of function calls 
		for (count=1;count < array_of_params.length; count++){
		    // split the string into its 3 variables
			var input_array = array_of_params[count].split(",",3);
			// format the input variables
			var form_field = Trim(input_array[0]).toLowerCase();
			var type = Trim(input_array[1]).toLowerCase();
			var requirement = Trim(input_array[2]).toLowerCase();
			
			if (type.match("radio") == "radio"){
				is_radio_selected(form_field);
			}
			else if (type.match("checkbox")){
				is_checked(form_field);
			}
			// if a filled form field should contain a string
			else if (type.match("string") == "string"){
				is_string(form_field);
			}
			// if a filled form field should contain a number
			else if (type.match("number") == "number"){
				is_number(form_field, requirement);
			}
			else if (type.match("email") == "email"){
				is_email(form_field, requirement);
			}	
		}
		
		
		if (error_count > 0){ 
			if (error_count == 1){
				error_msg = "There is a mistake in the form." + error_msg;
			}
			else{
				error_msg = "Please fix the following " + error_count + " errors and submit again." + error_msg;
			}
			create_error_message(form_element);
			return false;
		}
		return true;
	}
	
	/*	Creates a div with an id attribute set to the value of
	 *  warning_element_id, adds an error message if there is one.
	 *
	 *	@param	form_element	the form to add the div to as the first
	 * 							child.
	*/
	function create_error_message(form_element){
		if (document.getElementById(warning_element_id) == null){
			// Create error message element and id attribute
			error_msg_element = window.document.createElement("div");
			var error_msg_id = window.document.createAttribute("id");
			// Make the error message element the first child of the form element
			form_element.insertBefore(error_msg_element, form_element.firstChild);
			// add the id attribute to the error message element
			error_msg_element.setAttributeNode(error_msg_id);
			// set the name of the element
			error_msg_element.id = warning_element_id;
		}
		// set the error message's value
		error_msg_element.innerHTML = error_msg;	
	}
	
	/* Get the innerHTML of the 'for' attribute for the form field's label */
	function get_field_name(form_field){
		var label = get_label(form_field);
		if (label != null){
			return label.innerHTML;
		}
		else{
			// IF a label is not found for that is for this field then use the id of the field
			return form_field;
		}
	}
	
	
	/* Returns the label element that has a 'for' attribute that matches the
	 * 'name' attribute of the input (form) element. 
	*/
	function get_label(form_field){
		 // get all the labels for the form
		 var labels = form_element.getElementsByTagName("label");
		 // loop labels in form
		 for (var count=0 ; count<labels.length; count = count+1){
		  	/* The XMLDOM getAttribute() function works with the 'for' attribute
		  	in Firefox as it should, but it doesn't work in IE.  In IE it will
		  	return null whatever the value of the 'for' attribute.*/
		  	
		  	/* This works with FF.  IF the getAttribute is not null and the
		  	value is equal that of the form_field we're searching for then return
		  	its innerHTML*/
		  	if (labels[count].getAttribute('for') != null && labels[count].getAttribute('for').indexOf(form_field) != -1){
				return labels[count];
			}
			/* This works with IE 6.  IF the 'for' attrbute is null and the
		  	value of the 79th attribute (which is 'for' in IE) of the 'label' 
		  	element matches the form_field we're searching for then return
		  	its innerHTML*/
		  	else if (labels[count].getAttribute('for') == null && labels[count].attributes.item(79).nodeName == 'for'){
		  		if (labels[count].attributes.item(79).nodeValue.indexOf(form_field) != -1){
					return labels[count];
				}
			}
			/* Same as the previous conditional but will work if Microsoft change
			the value of the 79th attribute to an attribute that isn't 'for'*/
			else if (labels[count].getAttribute('for') == null){
				for (var attribute_count=0; attribute_count < labels[count].attributes.length; attribute_count++) {
					if (labels[count].attributes.item(attribute_count).nodeName == 'for') {
						if (labels[count].attributes.item(attribute_count).nodeValue.indexOf(form_field) != -1){
							return labels[count];
						}
					}
				}
			}
		}
		return null;
	}
	
	
	/*	Establishes if the user has selected a radio button out of the group of
	 *  radio buttons.  The button group is uniquely identified by the field_name
	 *  and form_element combination.
	*/
	function is_radio_selected(field_name){
		var selected = false;
		
		var inputs = form_element.getElementsByTagName("input");
		// loop the radio buttons whose "attribute" matches the field_name
		for(var count=0; count < inputs.length && selected == false; count = count + 1){
			// if we find an input element with the field_name then it is a radio button
			// that we are looking for
			if (inputs[count].getAttribute('name').indexOf(field_name) != -1){
				// true if button is selected (and thus ends loop)
				selected = evaluate_radio_button(field_name, count);
			}
		}
		
		// if no buttons were selected in the group
		if(selected == false) {
			set_validation_error(field_name);
			error_msg = error_msg + "<br>Please select a radio button answer in the field called: " + get_field_name(field_name);
		}
		else{
			set_validated(field_name);	
		}
		
	}
	
	/*	Return true if the radio button is selected, else false
	*/
	function evaluate_radio_button(field_name, button_number){
		if (form_element.elements[field_name][button_number].checked == true){
			set_validated(field_name);
			return true;
		}
		else {
			return false;
		}
	}
	
	/* Sets the error message using the text from the form element's alt 
	 * attribute if it exists and isn't and empty string.  Else it will
	 * try to use the title attribute if is not an empty string. Else it
	 * will produce a generic message using the elements label innerHTML
	 *	
	 * @param	@field_name 	name of the of the form element
	 */
	function set_error_message(field_name){
		if (form_element.elements[field_name].alt != null && form_element.elements[field_name].alt != ""){
			error_msg = error_msg + "<br />" + form_element.elements[field_name].alt;
		}
		else if(form_element.elements[field_name].title != null && form_element.elements[field_name].title != ""){
			error_msg = error_msg + "<br />" + form_element.elements[field_name].title;
		}
		else{
			error_msg = error_msg + "<br>Please complete the textbox called " + get_field_name(field_name) + ".";
		}
	}
	
	
	/**  Evaluate whether a form field contains a non-empty value. 
	 *	@param	field_name	the input field to validate for a string.
	 *
	*/
	function is_string(field_name){
		// if it is empty then highlight in form
		if (form_element.elements[field_name].value == ""){
			set_validation_error(field_name);
			set_error_message(field_name);
		}
		else {
			set_validated(field_name);
		}
	}
	
	/**  Evaluate whether a checkbox has been checked. 
	 *	@param	field_name	the name of the input checkbox element.
	 *
	*/
	function is_checked(field_name){
		if(form_element.elements[field_name].checked != true){
			set_validation_error(field_name);
			set_error_message(field_name);
		}
		else{
			set_validated(field_name);
		}
	}
	
	/*  Evaluate if a form field contains a number.
	 *  @param  field_name	 name of the input field to valid for a number.
	 *  @param	requirement	 indicates if the the input field can
	 * 						 be left empty by the user or not by the user.
	 */
	function is_number(field_name, requirement){
		//establish if the field contains a number 
		var is_number = !isNaN(form_element.elements[field_name].value);
		if (form_element.elements[field_name].value == ""){
			is_number = false;
		}

		// IF the field must contain a number (but doesn't)
		if (requirement == "required" && is_number == false && form_element.elements[field_name].value == ""){
			 set_validation_error(field_name);
			 error_msg = error_msg + "<br>Please enter a numeric value into the box called " + get_field_name(field_name) + ".";
		}
		// ELSE IF it is not a number and can be blank, but isn't either
		else if (requirement == "optional" && !isNaN(form_element.elements[field_name].value)){
			set_validation_error(field_name);
			error_msg = error_msg + "<br>Please enter a numeric value or leave the field called " + get_field_name(field_name) + " empty.";
		}
		else {
			if (requirement == "required"){
				set_validated(field_name);
			}
		}
	}
	
	/*  Evaluate if a form field contains a number.
	 *  @param	field_name	 name of the input field to valid for an email address.
	 *  @param	requirement	 indicates if the the input field can be left empty 
	 *						 by the user or not by the user.
	 */
	function is_email(field_name, requirement){
		// IF it contains an '@' and a '.' then assume it is an email.
		if (form_element.elements[field_name].indexOf("@") == "-1" && form_element.elements[field_name].indexOf("@") == "-1"){
			var is_email = true;
		}
		else{
			var is_email = false;
		}
		
		//IF it doesn't contain an email address and it cannot be blank, but it is
		if(is_email == false && requirement == "required"){
			set_validation_error(field_name);
			error_msg = error_msg + "<br>Please enter a valid email address into the field called " + get_field_name(field_name) + ".";
		}
		// ELSE IF it can be blank or contain an email address, but it contains something
		// that isn't an email address
		else if (requirement == "optional" && form_element.elements[field_name].value != "" && is_email == false){
			set_validation_error(field_name);
			error_msg = error_msg + "<br>Please enter a valid email address or leave the field called " + get_field_name(field_name) + " empty." ;
		}
		else{
			set_validated(field_name);
		}
	}
	
	
	
	/*  Switches the class of the input field (field_name) so CSS 
	 *  can be applied to its label to highlight that the field in invalid.
	 *  @param	field_name	 the form input whose class is to change.
	 */
	function set_validation_error(field_name){
		var label = get_label(field_name);
		error_count = error_count + 1;
		
		if (label != null){
			// IF the warning class doesn't already exist in the class attribute then
			// add it
			if (label.className.match(warning_class) == null){
				label.className = warning_class + " " + label.className;
			}
			
			if (error_count == 1){
				// if the form field is a radio or checkbox then focus
				// on its first element
				if (form_element.elements[field_name][1] != null){
					form_element.elements[field_name][0].focus();
				}
				// else focus on the element (as the id is the input parameter)
				else {
					form_element.elements[field_name].focus();
				}
			}
		}
		// else there is no label for the form element
	}
	
	/*  Set the class of the form field to indicate that it's content is valid.
	 */
	function set_validated(field_name){
		var label = get_label(field_name);
		if (label != null){
			label.className = label.className.replace(warning_class,"");
		}
		// else there is no label for the form element
	}
	
	
	function Trim(value){
		return LTrim(RTrim(value));
	}
	
	// Removes leading whitespaces
	function LTrim(value) {
		var re = /\s*((\S+\s*)*)/;
		return value.replace(re, "$1");
	}

	// Removes trailing whitespaces
	function RTrim(value) {
		var re = /((\s*\S+)*)\s*/;
		return value.replace(re, "$1");
	}
	