//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Usage:
// <form ... onsubmit="return validateForm(this);" >
//
// This function will pop-up a message to the user if a require form element is not filled in.  It also can run
// regex validation on form elements.
//
// Syntax:
// validateForm(form_to_validate, [form where validation criteria is defined], [form where validation regex's are defined])
//
// If the second argument is not provided, the code will look for a form named 'validationCriteriaForm'
// If the thirs argument is not provided, the code will look for a form named 'validationRegexForm'
//
// Depending on the calling context, "form_to_validate" might be one of the following:
// - this (from the <FORM> tag)
// - document.forms[0] (anywhere)
// - document.formName (anywhere)
// - this.form (from a button/submit <INPUT> tag)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// The Validation Criteria Form (VCF) can have the three following hidden form elements:
// - requiredTextFields
// - requiredSelectFields
// - requiredRadioCheckboxFields
// 
// All of three of these fields can contain data in this format:
//   [form_element1]:[display_message1]|[form_element2]:[display_message2]|[form_element3]:[display_message3]
// 
// validateForm.js will ensure that every form_element in that list is not empty.
//
// Example:
//   <form name="validationCriteriaForm">
//      <input type="hidden" name="requiredTextFields" value="SSN:Social Security Number">
//      <input type="hidden" name="requiredSelectFields" value="">
//      <input type="hidden" name="requiredRadioCheckboxFields" value="">
//   </form>
//
// Note:
// For <SELECT> fields, this code makes sure the TEXT is not empty.  So to use this functionality for select's, 
// it is best to have your web page start off with an empty option like the example below:
//
// <select name=color>
// <option></option>
// <option>Red</option>
// <option>Blue</option>
// <option>Green</option>
// <option>Black</option>
// </select>
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// The Validation Regex Form (VRF) should have one hidden form element per regex comparison needed.
// The format is as follows:
//   name="[form_element]:[display_message]" value="[regex]"
//
// Example:
//   <form name="validationRegexForm">
//      <input type="hidden" name="SSN:Social Security Number" value="^\d\d\d\-\d\d\-\d\d\d\d$">
//      <input type="hidden" name="homePhone:Home Phone" value="^\(\d\d\d\) \d\d\d\-\d\d\d\d$">
//   </form>
//
// I would suggest presenting the user with their formatting requirement in the form you are validating.
//
// Exmaple:
// <form ... onsubmit="return validateForm(this);">
// ...
// Social Security Number: <input type="text" name="SSN"> <font size="-1">Format: nnn-nn-nnnn</font>
// ...
// </form>
//
// Notes:
// - If you require double quotes in your regex, use &quot;, not a double quote character (")
// - You do not have to include the ^ and $ in your regex--use this only if you want an exact regex match
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function validateForm(form, vcfInput, vrfInput, debug) {
	var vcf = (vcfInput == null) ? document.validationCriteriaForm : vcfInput;
	
	if (debug == 1)
		alert("Starting validateForm...");
	
	if (vcf == null)
		return true;
	
	var errorString = "";

	/////////////
	// Check text and textarea form elements
	/////////////
	if ( (vcf.requiredTextFields != null) && (vcf.requiredTextFields.value != "") ) {
		var dataHash = [];
		dataHash = getFieldInfo(vcf.requiredTextFields.value);

		var key;

		for (key in dataHash) {
			if (eval("form." + key) != null) { // Handle if that form element isn't on the page!
				if (eval("form." + key + ".value") == "") {
					errorString += dataHash[key] + " is a required field.\n";
				}
			}			
		}
	}
	
	if (debug == 1)
		alert("Past text...");	

	/////////////
	// Check select form elements
	/////////////
	if ( (vcf.requiredSelectFields != null) && (vcf.requiredSelectFields.value != "") ) {
		var dataHash = [];
		dataHash = getFieldInfo(vcf.requiredSelectFields.value);

		var key;
		var elementOK = 0;
		var i;

		for (key in dataHash) {
			// We have to make sure at least one of the options in this select is selected
			elementOK = 0;
			
			if ( (eval("form." + key) != null) && (eval("form." + key + ".options") != null) ) { // Handle if that form element isn't on the page!
				for (i=0; i<eval("form." + key + ".options.length;"); i++) {
					if (eval("form." + key + ".options[" + i + "].selected")) {
						elementOK = 1;
						break;
					}
				}
			}
			else {
				// Skip this form element b/c it doesn't exist and therefore there is no way this form will be able to go on!
				elementOK = 1;
			}

			if (elementOK == 1)
			{
				// Check to make sure the value is not empty
				if ( (eval("form." + key) != null) && (eval("form." + key + ".options") != null) ) {
					if (eval("form." + key + ".options.selectedIndex") >= 0)
					{
						// [value]
						//if (eval("form." + key + ".options[" + "form." + key + ".options.selectedIndex].value") == "")
						//	elementOK = 0;

						// [text]
						if (eval("form." + key + ".options[" + "form." + key + ".options.selectedIndex].text") == "")
							elementOK = 0;
						
					}
				}
			}

			if (elementOK == 0) {
				errorString += dataHash[key] + " is a required field.\n";
			}
		}
	}
	
	if (debug == 1)
		alert("Past select...");		

	/////////////
	// Check radio and checkbox form elements
	/////////////
	if ( (vcf.requiredRadioCheckboxFields != null) && (vcf.requiredRadioCheckboxFields.value != "") ) {
		var dataHash = [];
		dataHash = getFieldInfo(vcf.requiredRadioCheckboxFields.value);
		
		var key;
		var elementOK = 0;
		var i;

		for (key in dataHash) {
			// We have to make sure at least one of the radio buttons are checked
			elementOK = 0;

			if (eval("form." + key) != null) { // Handle if that form element isn't on the page!
				if (eval("form." + key + ".length;") == null) {
					if (eval("form." + key + ".checked")) {
						elementOK = 1;
					}
				}
				else {
					for (i=0; i<eval("form." + key + ".length;"); i++) {
						if (eval("form." + key + "[" + i + "].checked")) {
							elementOK = 1;
							break;
						}
					}
				}
			}
			else {
				// Skip this form element b/c it doesn't exist and therefore there is no way this form will be able to go on!
				elementOK = 1;
			}

			if (elementOK == 0) {
				errorString += dataHash[key] + " is a required field.\n";
			}
		}
	}
	
	if (debug == 1)
		alert("Past radio...");


	if (errorString != "") {
		alert(errorString);
		return false;
	}
	
	// The basic check is done i.e. all of the required form elements are filled in
	// Now run through the regex checks if applicable
	var vrf = (vrfInput == null) ? document.validationRegexForm : vrfInput;
	
	if (debug == 1)
		alert("About to start regex...");
	
	if (vrf == null)
		return true;
		
	var i;
	var elName;
	var displayName;
	var regex;	
	var array;
	var elValue;
	
	for (i = 0; i < vrf.elements.length; i++)
	{
		// vrf.elements[i].name		-- contains: [form element name:display value]
		// vrf.elements[i].value	-- contains: regex expression
		array = vrf.elements[i].name.split(":");
		elName = array[0];
		displayName = array[1];
		regex = vrf.elements[i].value;
		
		// alert("elName:" + elName + " -- displayName:" + displayName + " -- regex:" + regex);
		
		// alert("form." + elName + ":" + eval("form." + elName));
		
		elValue = eval("form." + elName + ".value");
		
		if ( (elValue == null) || (elValue == "") )
			continue;
		
		// alert("elValue:" + elValue);
		
		if(!elValue.match(new RegExp(regex)))
		{
			errorString += displayName + " is not in the correct format.\n";
		}
	}
	
	if (debug == 1)
		alert("Done with regex...");
	
	if (errorString != "") {
		alert(errorString);
		return false;
	}

	return true;
}

function getFieldInfo(str) {
	var a = str.split("|");
	var ret = [];

	var i;
	var elementName;
	var displayName;

	for (i=0; i<a.length; i++) {
		//alert("a[" + i + "] --> " + a[i]);
		var a2 = a[i].split(":");

		elementName = a2[0];
		displayName = a2[1];

		// If no display name is given, use the for element name
		if (displayName == null)
			displayName = elementName;

		//alert("form element name:" + elementName + "\ndisplay name:" + displayName);

		// Build the ret hash
		ret[elementName] = displayName;
	}

	return ret;
}

////////////////////////////////////////////////////////////////////////////
//      Credit Card Validator
//      By Mark Morley (mark@islandnet.com)
//
//      This routine analyzes a string to determine if it represents a
//      valid credit card number and if so, returns which type of card
//      it is.  It returns the string "BAD" if the string does not
//      represent a valid credit card, otherwise it returns one of these
//      self explanatory values:
//
//      Visa, Master Card, American Express, Discover, enroute, dinersclub, or jcb
//
//      NOTE: This routine merely indicates if a given string represents
//            a valid card number.  It cannot tell you if that number has
//            actually been assigned to someone!
////////////////////////////////////////////////////////////////////////////

function checkCC(string)
{
   // First we loop through the string, building a reversed copy of the
   // digits as we go.  We strip out any non-digits too, so it doesn't
   // matter if the user used spaces or dashes (or any other garbage).

   rstring = "";
   for( l = string.length - 1; l >= 0; l-- )
   {
      c = string.substr( l, 1 );
      if( c >= "0" && c <= "9" )
	 rstring += c;
   }

   // Grab the last 4 digits...

   l4 = rstring.substr( rstring.length - 4, 4 );

   // Special case for "enRoute".  If there are exactly 15 digits and it
   // begins with "2014" or "2149" then it's a valid enRoute number.  They
   // do not use the LUHN formula.

   if( rstring.length == 15 && (l4 == "4102" || l4 == "9412") )
      return "enroute";

   // Using the LUHN formula we calculate a checksum value.  All the other
   // cards we recognise must pass this test.

   sum = 0;
   for( l = 1; l <= rstring.length; l++ )
   {
      c = rstring.substr( l - 1, 1 );
      if( l % 2 == 0 )
      {
	 n = 2 * parseInt( c );
	 if( n > 9 )
	 {
	    sum += 1;
	    n -= 10;
	 }
	 sum += n;
      }
      else
	 sum += parseInt( c );
   }

   // If the sum is not an even multiple of 10 then it fails.

   if( sum % 10 )
      return "BAD";

   // If the number is 16 digits long and starts with "6011" then it's
   // a Discover card.

   if( rstring.length == 16 && l4 == "1106" )
      return "Discover";

   // If the number is 15 digits long and begins with either "2131" or
   // "1800" then it's a JCB card.

   if( rstring.length == 15 && (l4 == "1312" || l4 == "0081") )
      return "jcb";

   // Grab the right most digit...

   l1 = rstring.substr( rstring.length - 1, 1 );

   // If the number is 16 digits long and begins with "3" then it's also a
   // JCB card.

   if( rstring.length == 16 && l1 == "3" )
      return "jcb";

   // If the number is 13 or 16 digits long and begins with "4" then it's
   // a VISA card.

   if( (rstring.length == 13 || rstring.length == 16) && l1 == "4" )
      return "Visa";

   // Grab the last two digits...

   l2 = rstring.substr( rstring.length - 2, 2 );

   // If the number is 16 digits long and the first two digits are "51", "52",
   // "53", "54", or "55" then it's a MasterCard.

   if( rstring.length == 16 && (l2 == "15" || l2 == "25" || l2 == "35" || l2 == "45" || l2 == "55" ) )
      return "Master Card";

   // If the number is 15 digits long and it begins with either "34" or "37"
   // then it's an American Express card.

   if( rstring.length == 15 && (l2 == "43" || l2 == "73" ) )
      return "American Express";

   // Grab the last 3 digits...

   l3 = rstring.substr( rstring.length - 3, 3 );

   // If the number is 14 digits long and begins with "300", "301", "302",
   // "303", "304", "305", "36", or "38", then it's a Diners Club or
   // Cart Blanche card.

   if( rstring.length == 14 && (l2 == "63" || l2 == "83" || l3 == "003" || l3 == "103" || l3 == "203" || l3 == "303" || l3 == "403" || l3 == "503" ) )
      return "dinersclub";

   // If it's none of those then it's "BAD".

   return "BAD";
}
