/*
 *
 *	validation.js
 *	v2.4, Feb 2008
	http://www.benjaminkeen.com/software/js_validation
	Ben Keen with additional code contributed by Mihai Ionescu and Nathan Howard. 

  	Function	: validateFields()
  	Purpose		: generic form field validation.
  	Parameters	: form  - the name of the form to validate
				  rules - an array of the validation rules. Each rule is a string of the form:

   "[if:FIELDNAME=VALUE,]REQUIREMENT,fieldname[,fieldname2 [,fieldname3, date_flag]],error message"
	if:FIELDNAME=VALUE, This allows us to only validate a field only if a fieldname 
	FIELDNAME has a value VALUE. This option allows for nesting; i.e. you can have multiple if clauses, 
	separated by a comma. They will be examined in the order in which they appear in the line.

	Valid REQUIREMENT strings are: 
		"required"     - field must be filled in
		"digits_only"  - field must contain digits only
		"is_alpha"     - field must only contain alphanumeric characters (0-9, a-Z)
		"custom_alpha" - field must be of the custom format specified.
	
	fieldname:  the name of the field

	fieldname2: a character or sequence of special characters. These characters are:
	L   An uppercase Letter.          V   An uppercase Vowel.
	l   A lowercase letter.           v   A lowercase vowel.
	D   A letter (upper or lower).    F   A vowel (upper or lower).
	C   An uppercase Consonant.       x   Any number, 0-9.
	c   A lowercase consonant.        X   Any number, 1-9.
	E   A consonant (upper or lower).
		"reg_exp"      - field must match the supplied regular expression.  

	fieldname:  the name of the field
	fieldname2: the regular expression
	fieldname3: (optional) flags for the reg exp (like i for case insensitive
		"letters_only" - field must only contains letters (a-Z)
		"length=X"     - field has to be X characters long
		"length=X-Y"   - field has to be between X and Y (inclusive) characters long
		"length>X"     - field has to be greater than X characters long
		"length>=X"    - field has to be greater than or equal to X characters long
		"length<X"     - field has to be less than X characters long
		"length<=X"    - field has to be less than or equal to X characters long
		"valid_email"  - field has to be a valid email address
		"valid_date"   - field has to be a valid date

	fieldname:  MONTH 
	fieldname2: DAY 
	fieldname3: YEAR
	date_flag:  "later_date" / "any_date"
		"same_as"      - fieldname is the same as fieldname2 (for password comparison)
		"range=X-Y"    - field must be a number between the range of X and Y inclusive
		"range>X"      - field must be a number greater than X
		"range>=X"     - field must be a number greater than or equal to X
		"range<X"      - field must be a number less than X
		"range<=X"     - field must be a number less than or equal to X

	Comments:	With both digits_only, is_alpha, letters_only and valid_email options, if the empty 
				string is passed in it won't generate an error, thus allowing validation of 
				non-required fields. So, for example, if you want a field to be a valid email address, 
				provide validation for both "required" and "valid_email".
 */
	function validateFields(form, rules)
	{
	
	  // loop through rules
	
	  for (var i=0; i<rules.length; i++)
	
	  {
	
		// split row into component parts 
	
		var row = rules[i].split(",");
	
	
	
		// while the row begins with "if:..." test the condition. If true, strip the
	
		// if:..., part and continue evaluating the rest of the line. Keep repeating 
	
		// this while the line begins with an if-condition. If it fails any of the 
	
		// conditions, don't bother validating the rest of the line.
	
		var satisfiesIfConditions = true;
	
		while (row[0].match("^if:"))
	
		{
	
		  var condition = row[0];
	
		  condition = condition.replace("if:", "");
	
	
	
		  // check if it's a = or != test
	
		  var comparison = "equal";
	
		  var parts = new Array();
	
		  if (condition.search("!=") != -1)
	
		  {
	
			parts = condition.split("!=");
	
			comparison = "not_equal";
	
		  }
	
		  else
	
			parts = condition.split("=");
	
		
	
		  var fieldToCheck = parts[0];
	
		  var valueToCheck = parts[1];
	
		  
	
		  // find value of FIELDNAME for conditional check
	
		  var fieldnameValue = "";
	
		  if (form[fieldToCheck].type == undefined) // RADIO
	
		  {
	
			for (var j=0; j<form[fieldToCheck].length; j++)
	
			{
	
			  if (form[fieldToCheck][j].checked)
	
				fieldnameValue = form[fieldToCheck][j].value;
	
			}
	
		  }
	
		  // single checkbox      
	
		  else if (form[fieldToCheck].type == "checkbox")
	
		  {
	
			if (form[fieldToCheck].checked)
	
			  fieldnameValue = form[parts[0]].value;
	
		  }      
	
		  // all other field types
	
		  else
	
			fieldnameValue = form[parts[0]].value;
	
	
	
		  // if the VALUE is NOT the same, we don't need to validate this field. Return.
	
		  if (comparison == "equal" &&  fieldnameValue != valueToCheck)
	
		  {
	
			satisfiesIfConditions = false;
	
			break;
	
		  }
	
		  else if (comparison == "not_equal" && fieldnameValue == valueToCheck)
	
		  {
	
			satisfiesIfConditions = false;
	
			break;      
	
		  }
	
		  else
	
			row.shift();    // remove this if-condition from line, and continue validating line
	
		}
	
	
	
		if (!satisfiesIfConditions)
	
		  continue;
	
	
	
	
	
		var requirement = row[0];
	
		var fieldName   = row[1];
	
	
	
		// depending on the validation test, store the incoming strings for use later...
	
		if (row.length == 6)        // valid_date
	
		{
	
		  var fieldName2   = row[2];
	
		  var fieldName3   = row[3];
	
		  var date_flag    = row[4];
	
		  var errorMessage = row[5];
	
		}
	
		else if (row.length == 5)     // reg_exp (WITH flags like g, i, m)
	
		{
	
		  var fieldName2   = row[2];
	
		  var fieldName3   = row[3];
	
		  var errorMessage = row[4];
	
		}
	
		else if (row.length == 4)     // same_as, custom_alpha, reg_exp (without flags like g, i, m)
	
		{
	
		  var fieldName2   = row[2];
	
		  var errorMessage = row[3];
	
		}
	
		else
	
		  var errorMessage = row[2];    // everything else!
	
	
	
	
	
		// if the requirement is "length...", rename requirement to "length" for switch statement
	
		if (requirement.match("^length"))
	
		{
	
		  var lengthRequirements = requirement;
	
		  requirement = "length";
	
		}
	
	
	
		// if the requirement is "range=...", rename requirement to "range" for switch statement
	
		if (requirement.match("^range"))
	
		{
	
		  var rangeRequirements = requirement;
	
		  requirement = "range";
	
		}
	
	
	
	
	
		// now, validate whatever is required of the field
	
		switch (requirement)
	
		{
	
		  case "required":
	
		 
	
			// if radio buttons or multiple checkboxes:
	
			if (form[fieldName].type == undefined)
	
			{
	
			  var oneIsChecked = false;
	
			  for (var j=0; j<form[fieldName].length; j++)
	
			  {
	
				if (form[fieldName][j].checked)
	
				  oneIsChecked = true;
	
			  }
	
			  if (!oneIsChecked)
	
			  {
	
				alertMessage(form[fieldName], errorMessage);
	
				return false;           
	
			  }
	
			}
	
			else if (form[fieldName].type == "select-multiple")
	
			{          
	
			  var oneIsSelected = false;
	
			  for (k=0; k<form[fieldName].length; k++)
	
			  {
	
				if (form[fieldName][k].selected)
	
				  oneIsSelected = true;
	
			  }
	
	
	
			  // if no options have been selected, or if there ARE no options in the multi-select 
	
			  // dropdown, return false
	
			  if (!oneIsSelected || form[fieldName].length == 0)
	
			  {
	
				alertMessage(form[fieldName], errorMessage);
	
				return false;          
	
			  }
	
			}
	
			// a single checkbox
	
			else if (form[fieldName].type == "checkbox")
	
			{
	
			  if (!form[fieldName].checked)
	
			  {
	
				alertMessage(form[fieldName], errorMessage);
	
				return false;           
	
			  }
	
			}
	
			// otherwise, just perform ordinary "required" check.
	
			else if (!form[fieldName].value)
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}
	
			break;
	
	
	
		  case "digits_only":       
	
			if (form[fieldName].value && form[fieldName].value.match(/\D/))
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}
	
			break;
	
		  case "digits_withDot":       
	
			if (form[fieldName].value && form[fieldName].value.match(/[^0-9\.]/))
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}
	
			break;
	
	
	
		  case "letters_only": 
	
			if (form[fieldName].value && form[fieldName].value.match(/[^a-zA-Z]/))
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}
	
			break;
	
	
	
		  case "is_alpha": 
	
			if (form[fieldName].value && form[fieldName].value.match(/\W/))
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}
	
			break;
	
	
	
		  case "custom_alpha":
	
			var conversion = {
	
			  "L": "[A-Z]",
	
			  "V": "[AEIOU]",
	
			  "l": "[a-z]",
	
			  "v": "[aeiou]",
	
			  "D": "[a-zA-Z]",
	
			  "F": "[aeiouAEIOU]",
	
			  "C": "[BCDFGHJKLMNPQRSTVWXYZ]",
	
			  "x": "[0-9]",
	
			  "c": "[bcdfghjklmnpqrstvwxyz]",
	
			  "X": "[1-9]",
	
			  "E": "[bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ]"
	
				};
	
	
	
			var reg_exp_str = "";
	
			for (j=0; j<fieldName2.length; j++)
	
			{
	
			  if (conversion[fieldName2.charAt(j)])
	
				reg_exp_str += conversion[fieldName2.charAt(j)];
	
			  else
	
				reg_exp_str += fieldName2.charAt(j);
	
			}
	
			var reg_exp = new RegExp(reg_exp_str);
	
	
	
			if (form[fieldName].value && reg_exp.exec(form[fieldName].value) == null)
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}        
	
			break;
	
	
	
		  case "reg_exp":
	
			var reg_exp_str = fieldName2;
	
	
	
			// rather crumby, but...
	
			if (row.length == 5)
	
			  var reg_exp = new RegExp(reg_exp_str, fieldName3);
	
			else
	
			  var reg_exp = new RegExp(reg_exp_str);
	
	
	
			if (form[fieldName].value && reg_exp.exec(form[fieldName].value) == null)
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}        
	
			break;
	
	
	
		  case "length":
	
			comparison_rule = "";
	
			rule_string = "";
	
	
	
			// if-else order is important here: needs to check for >= before >
	
			if      (lengthRequirements.match(/length=/))
	
			{ 
	
			  comparison_rule = "equal"; 
	
			  rule_string = lengthRequirements.replace("length=", ""); 
	
			}
	
			else if (lengthRequirements.match(/length>=/))
	
			{
	
			  comparison_rule = "greater_than_or_equal"; 
	
			  rule_string = lengthRequirements.replace("length>=", "");
	
			}
	
			else if (lengthRequirements.match(/length>/))
	
			{
	
			  comparison_rule = "greater_than"; 
	
			  rule_string = lengthRequirements.replace("length>", "");
	
			}
	
			else if (lengthRequirements.match(/length<=/))
	
			{
	
			  comparison_rule = "less_than_or_equal"; 
	
			  rule_string = lengthRequirements.replace("length<=", "");
	
			}        
	
			else if (lengthRequirements.match(/length</))
	
			{
	
			  comparison_rule = "less_than"; 
	
			  rule_string = lengthRequirements.replace("length<", "");
	
			}
	
	
	
			// now perform the appropriate validation
	
			switch (comparison_rule)
	
			{
	
			  case "greater_than_or_equal":
	
				if (!(form[fieldName].value.length >= parseInt(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "greater_than":
	
				if (!(form[fieldName].value.length > parseInt(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "less_than_or_equal":
	
				if (!(form[fieldName].value.length <= parseInt(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "less_than":
	
				if (!(form[fieldName].value.length < parseInt(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "equal":
	
				var range_or_exact_number = rule_string.match(/[^_]+/);
	
				var fieldCount = range_or_exact_number[0].split("-");
	
		
	
				// if the user supplied two length fields, make sure the field is within that range
	
				if (fieldCount.length == 2)
	
				{
	
				  if (form[fieldName].value.length < fieldCount[0] || form[fieldName].value.length > fieldCount[1])
	
				  {
	
					alertMessage(form[fieldName], errorMessage);
	
					return false;
	
				  }
	
				}
	
		
	
				// otherwise, check it's EXACTLY the size the user specified 
	
				else
	
				{
	
				  if (form[fieldName].value.length != fieldCount[0])
	
				  {
	
					alertMessage(form[fieldName], errorMessage);
	
					return false;
	
				  }
	
				}     
	
	
	
				break;
	
			}
	
			break;
	
	
	
		  // this is also true if field is empty [should be same for digits_only]
	
		  case "valid_email":
	
			if (form[fieldName].value && !isValidEmail(form[fieldName].value))
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;         
	
			}
	
			break;
	
	
	
		  case "valid_date":
	
	
	
			// this is written for future extensibility of isValidDate function to allow 
	
			// checking for dates BEFORE today, AFTER today, IS today and ANY day.
	
			var isLaterDate = false;
	
			if    (date_flag == "later_date")
	
			  isLaterDate = true;
	
			else if (date_flag == "any_date")
	
			  isLaterDate = false;
	
	
	
			if (!isValidDate(form[fieldName].value, form[fieldName2].value, form[fieldName3].value, isLaterDate))
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}
	
			break;
	
	
	
		  case "same_as":
	
			if (form[fieldName].value != form[fieldName2].value)
	
			{
	
			  alertMessage(form[fieldName], errorMessage);
	
			  return false;
	
			}       
	
			break;
	
	
	
		  case "range":
	
		 
	
			comparison_rule = "";
	
			rule_string = "";
	
	
	
			// if-else order is important here: needs to check for >= before >
	
			if      (rangeRequirements.match(/range=/))
	
			{ 
	
			  comparison_rule = "equal";
	
			  rule_string = rangeRequirements.replace("range=", ""); 
	
			}
	
			else if (rangeRequirements.match(/range>=/))
	
			{
	
			  comparison_rule = "greater_than_or_equal";
	
			  rule_string = rangeRequirements.replace("range>=", "");
	
			}
	
			else if (rangeRequirements.match(/range>/))
	
			{
	
			  comparison_rule = "greater_than";
	
			  rule_string = rangeRequirements.replace("range>", "");
	
			}
	
			else if (rangeRequirements.match(/range<=/))
	
			{
	
			  comparison_rule = "less_than_or_equal";
	
			  rule_string = rangeRequirements.replace("range<=", "");
	
			}        
	
			else if (rangeRequirements.match(/range</))
	
			{
	
			  comparison_rule = "less_than";
	
			  rule_string = rangeRequirements.replace("range<", "");
	
			}
	
	
	
			// now perform the appropriate validation
	
			switch (comparison_rule)
	
			{
	
			  case "greater_than_or_equal":
	
				if (!(form[fieldName].value >= Number(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "greater_than":
	
				if (!(form[fieldName].value > Number(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "less_than_or_equal":
	
				if (!(form[fieldName].value <= Number(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "less_than":
	
				if (!(form[fieldName].value < Number(rule_string)))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
	
	
			  case "equal":
	
				var rangeValues = rule_string.split("-");
	
				
	
				// if the user supplied two length fields, make sure the field is within that range
	
				if ((form[fieldName].value < Number(rangeValues[0])) || (form[fieldName].value > Number(rangeValues[1])))
	
				{
	
				  alertMessage(form[fieldName], errorMessage);
	
				  return false;
	
				}
	
				break;
	
			}
	
			break;
	
	
	
		  case "function":
	
			custom_function = fieldName;
	
			if (!(eval(custom_function + "()")))
	
			  return false;
	
			break;
	
	
	
		  default:
	
			alert("Unknown requirement flag in validateFields(): " + requirement);
	
			return false;
	
		}
	
	  }
	  
	  return true;
	
	}
	/*
	 * Function: alertMessage()
	 * Purpose:  simple helper function which alerts a message, then focuses on and highlights a particular field.
	 */
	
	function alertMessage(obj, message)
	{ 
	  var backgroundColor 	= "#F2F9FF";
	  var bordersColor 		= "#AADDFF";
	  alert(message);
	  
	  // if "obj" is an array: it's a radio button. Focus on the first element.
	  if (obj.type == undefined)
	  
		obj[0].focus();
		
	  else
	  {
		obj.style.background 	= backgroundColor;
		obj.style.borderColor 	= bordersColor;
		obj.focus();
	  }
	
	  return false;
	  
	}

	/*
	 *	Function: isValidEmail
	 *	Purpose:  tests a string is a valid email
	 */

	function isValidEmail(str)
	{
	
	  // trim starting / ending whitespace
	
	  str = str.replace(/^\s*/, "");
	
	  str = str.replace(/\s*$/, "");
	
	  
	
	  var at="@"
	
	  var dot="."
	
	  var lat=str.indexOf(at)
	
	  var lstr=str.length
	
	  var ldot=str.indexOf(dot)
	
	
	
	  var reg1 = /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/; // not valid
	
	  var reg2 = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/; // valid
	
	  if (!(!reg1.test(str) && reg2.test(str))) 
	
		return false;   
	
	
	
	  if (str.indexOf(at)==-1)
	
		return false
	
	  
	
	  if (str.indexOf(at)==-1 || str.indexOf(at)==0 || str.indexOf(at)==lstr)
	
		return false
	
	  
	
	  if (str.indexOf(dot)==-1 || str.indexOf(dot)==0 || str.indexOf(dot)==lstr)
	
		return false
	
	
	
	  if (str.indexOf(at,(lat+1))!=-1)
	
		return false
	
	
	
	  if (str.substring(lat-1,lat)==dot || str.substring(lat+1,lat+2)==dot)
	
		return false
	
	
	
	  if (str.indexOf(dot,(lat+2))==-1)
	
		return false
	
	
	
	  if (str.indexOf(" ")!=-1)
	
		return false
	
	
	
	  return true;
	
	}


	// helper function to check to see if a string is empty
	function isEmpty(str)
	{  	
	  return ((str == null) || (str.length == 0));	
	}

	/*
	 *	Function: isWhitespace()
	 *	Purpose:  Returns true if string parameter is empty or whitespace characters only.
	 */

	function isWhitespace(s)
	{
	
	  var i;
	
	
	  // Is s empty?
	
	  if (isEmpty(s)) return true;
	
	
	
	  for (var i=0; i<s.length; i++)
	
	  {   
	
		var c = s.charAt(i);
	
		if (whitespace.indexOf(c) == -1)
	
		  return false;
	
	  }
	
	
	
	  return true;
	
	}


	/*
	 *	Function:   isValidDate()
	 *	Purpose:    to check an incoming date is valid. If any of the date parameters  
	 *	 
              fail, it returns a string message denoting the problem.
			  Parameters: month       - an integer between 1 and 12
			
						  day         - an integer between 1 and 31 (depending on month)
			
						  year        - a 4-digit integer value
			
						  isLaterDate - a boolean value. If true, the function verifies the date being passed in is LATER than the current date.

	 */

	function isValidDate(month, day, year, isLaterDate)
	{
	
	  // depending on the year, calculate the number of days in the month
	
	  if (year % 4 == 0)      // LEAP YEAR 
	
		var daysInMonth = new Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	
	  else
	
		var daysInMonth = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
	
	
	
	
	
	  // first, check the incoming month and year are valid. 
	
	  if (!month || !day || !year)          return false;
	
	  if (1 > month || month > 12)          return false;
	
	  if (year < 0)                         return false;
	
	  if (1 > day || day > daysInMonth[month-1]) return false;
	
	
	
	
	
	  // if required, verify the incoming date is LATER than the current date.
	
	  if (isLaterDate)
	
	  {
	
		// get current date
	
		var today = new Date();
	
		var currMonth = today.getMonth() + 1; // since returns 0-11
	
		var currDay   = today.getDate();
	
		var currYear  = today.getFullYear();
	
	
	
		// zero-pad today's month & day
	
		if (String(currMonth).length == 1)  currMonth = "0" + currMonth;
	
		if (String(currDay).length == 1)  currDay   = "0" + currDay;    
	
		currDate = String(currYear) + String(currMonth) + String(currDay);
	
		
	
		// zero-pad incoming month & day
	
		if (String(month).length == 1)  month = "0" + month;
	
		if (String(day).length == 1)  day   = "0" + day;
	
		incomingDate = String(year) + String(month) + String(day);
	
	
	
		if (Number(currDate) > Number(incomingDate))
	
		  return false;
	
	  }
	
	  
	
	  return true;
	
	}

