var defaultEmptyOK = false;

//set up basic character groups
var digits = "0123456789";
var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz";
var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var whitespace = " \t\n\r";
var nameDelimiters = "-. ";
var decimalPointDelimiter = ".";
var daysInMonth = makeArrayFromString(31,29,31,30,31,30,31,31,30,31,30,31);

//set up allowed characters for specific data types

//phone numbers
var phoneNumberDelimiters = "()-. ";
var digitsInUSPhoneNumber = 10;
var validUSPhoneChars = digits + phoneNumberDelimiters;
var validWorldPhoneChars = digits + phoneNumberDelimiters + "+";

//electronic addresses
var validDomainNameChars = digits + uppercaseLetters + lowercaseLetters + "-_.";

//snail mail addresses
var addressDelimiters = ".,/-" + whitespace;
var validAddressCharacters = addressDelimiters + digits + uppercaseLetters + lowercaseLetters;

//US mail address parameters
var digitsInZIPCode1 = 5;
var digitsInZIPCode2 = 9;
var ZIPCodeDelimiters = "-";
var ZIPCodeDelimeter = "-";
var validZIPCodeChars = digits + ZIPCodeDelimiters;
var USStateCodes = "AL|AK|AS|AZ|AR|CA|CO|CT|DE|DC|FM|FL|GA|GU|HI|ID|IL|IN|IA|KS|KY|LA|ME|MH|MD|MA|MI|MN|MS|MO|MT|NE|NV|NH|NJ|NM|NY|NC|ND|MP|OH|OK|OR|PW|PA|PR|RI|SC|SD|TN|TX|UT|VT|VI|VA|WA|WV|WI|WY|AE|AA|AE|AE|AP";
var USStateCodeDelimiter = "|";

//Canadian mail address parameters
var cdnProvCodes = "BC|AB|SK|MB|ON|QC|NF|PE|NS|NB|NT|YT|NU";
var postalCodeDelimiter = " ";
var validPostalCodeChars = digits + lowercaseLetters + uppercaseLetters + postalCodeDelimiter;
var charsInPostalCode = 6;
var cdnProvCodeDelimiter = "|";

//credit card parameters
var creditCardDelimiters = " -.";

function handle_enter (field, e)
{
	var keyCode = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
	if (keyCode == 13)
	{
		var i;
		for (i = 0; i < field.form.elements.length; i++)
			if (field == field.form.elements[i])
				break;
		i = (i + 1) % field.form.elements.length;
		field.form.elements[i].focus();
		return false;
	}
	else
		return true;
}

function advance_three(field)
{
	if (field.value.length == 3)
	{
		var i;
		for (i = 0; i < field.form.elements.length; i++)
			if (field == field.form.elements[i])
				break;
		i = (i + 1) % field.form.elements.length;
		field.form.elements[i].focus();
		return false;
	}
}

function numbers_only(e)
{
	var keyCode = e.keyCode ? e.keyCode : e.which ? e.which : ex.charCode;
	var keychar = String.fromCharCode(keyCode);

	if ((keyCode == null) || (keyCode == 0) || (keyCode == 8) ||
		(keyCode == 9) || (keyCode == 13) || (keyCode == 27))
		return true;
	else if (("1234567890").indexOf(keychar) > -1)
		return true;
	else
		return false;
}

/****************************************************/
/* Description : This function proctors the form		*/
/*  validation process.	Inputs are:									*/
/*	 thisForm : the form to be validated						*/
/*	 requiredFields : array of required field names	*/
/*	 fieldNames : array of descriptions							*/
/*																									*/
/****************************************************/
function validateRequiredFields2(thisForm, requiredFields, requiredDescriptions)
{
	var fieldCheck   = true;
  var fieldsNeeded = "\nA value must be entered in the following field(s):\n\n\t";

	// Check fields for content
  for(var fieldNum=0; fieldNum < requiredFields.length-1; fieldNum++)
  {
  	// Check Text fields
		if (thisForm.elements[requiredFields[fieldNum]].type == "text")
		{
    	if ((thisForm.elements[requiredFields[fieldNum]].value == "") ||
         	(thisForm.elements[requiredFields[fieldNum]].value == " "))
			{
      	fieldsNeeded += requiredDescriptions[fieldNum] + "\n\t";
        fieldCheck = false;
      }
    }
	}

	// required cc payment type
	if ((!thisForm.refund_card_type[0].checked) && (!thisForm.refund_card_type[1].checked))
	{
		fieldsNeeded += requiredDescriptions[20] + "\n\t";
		fieldCheck = false;
	}
	
	// required governmental unit
	if ((!thisForm.unit[0].checked) &&
			(!thisForm.unit[1].checked) &&
			(!thisForm.unit[2].checked) &&
			(!thisForm.unit[3].checked) &&
			(!thisForm.unit[4].checked) &&
			(!thisForm.unit[5].checked) &&
			(!thisForm.unit[6].checked))
	{
		fieldsNeeded += requiredDescriptions[5] + "\n\t";
		fieldCheck = false;
	}
	if ((thisForm.unit[6].checked) && (thisForm.othertext.value == ""))
	{
		fieldsNeeded += requiredDescriptions[5] + "\n\t";
		fieldCheck = false;
	}

	// required sex
	if ((!thisForm.sex[0].checked) && (!thisForm.sex[1].checked))
	{
		fieldsNeeded += requiredDescriptions[2] + "\n\t";
		fieldCheck = false;
	}

  // All required fields have been entered
  if (fieldCheck == true)
  {
   	// Check email address
  	if (!isEmail(thisForm.elements["email"].value))
  	{
  		alert("Please enter a valid email address.");
			thisForm.elements.email.select();
			thisForm.elements.email.focus();
			return false;
		}
		// Check email address
  	if (!isEmail(thisForm.elements["email2"].value))
  	{
  		alert("Please enter a valid email2 address.");
			thisForm.elements.email2.select();
			thisForm.elements.email2.focus();
			return false;
		}
		if (thisForm.elements["email"].value != thisForm.elements["email2"].value)
		{
			alert("Email addresses do not match.");
			thisForm.elements.email.select();
			thisForm.elements.email.focus();
			return false;
		}

		/* only validate the number of chars in the field */
		if (thisForm.elements.telephone_areacode.value.length < 3)
		{
			alert("Please enter a valid phone number.");
			thisForm.elements.telephone_areacode.select();
			thisForm.elements.telephone_areacode.focus();
			return false;
		}
		if (thisForm.elements.telephone_prefix.value.length < 3)
		{
			alert("Please enter a valid phone number.");
			thisForm.elements.telephone_prefix.select();
			thisForm.elements.telephone_prefix.focus();
			return false;
		}
		if (thisForm.elements.telephone_number.value.length < 4)
		{
			alert("Please enter a valid phone number.");
			thisForm.elements.telephone_number.select();
			thisForm.elements.telephone_number.focus();
			return false;
		}
		if (thisForm.elements.payment[0].checked)
		{
			if (thisForm.elements.po_number.value == "")
			{
				alert("Please enter your PO Number.");
				thisForm.elements.po_number.select();
				thisForm.elements.po_number.focus();
				return false;
			}
		}
		
		return true;
  }
  // Alert with the missing fields
  else
  {
      alert(fieldsNeeded);
      return false;
  }
}


/****************************************************/
/* Description : This function proctors the form		*/
/*  validation process.	Inputs are:									*/
/*	 thisForm : the form to be validated						*/
/*	 requiredFields : array of required field names	*/
/*	 fieldNames : array of descriptions							*/
/*																									*/
/****************************************************/
function validateRequiredFields(thisForm, requiredFields, requiredDescriptions)
{
	var fieldCheck   = true;
  var fieldsNeeded = "\nA value must be entered in the following field(s):\n\n\t";

	// Check fields for content
  for(var fieldNum=0; fieldNum < requiredFields.length-1; fieldNum++)
  {
  	// Check Text fields
		if (thisForm.elements[requiredFields[fieldNum]].type == "text")
		{
    	if ((thisForm.elements[requiredFields[fieldNum]].value == "") ||
         	(thisForm.elements[requiredFields[fieldNum]].value == " "))
			{
				if (requiredFields[fieldNum] == "refund_name")
				{
					if (thisForm.payment[2].checked)
					{
						fieldsNeeded += requiredDescriptions[fieldNum] + "\n\t";
        		fieldCheck = false;
					}
				}
				else
				{
      		fieldsNeeded += requiredDescriptions[fieldNum] + "\n\t";
        	fieldCheck = false;
      	}
      }
    }
	}

	// required cc payment type
	if ((!thisForm.refund_card_type[0].checked) && (!thisForm.refund_card_type[1].checked))
	{
		fieldsNeeded += requiredDescriptions[20] + "\n\t";
		fieldCheck = false;
	}
	
	//if ((thisForm.refund_type[1].checked) && (thisForm.check_name.value == ""))
	//{
	//	fieldsNeeded += "Name on Check\n\t";
	//	fieldCheck = false;
	//}
	
	// required governmental unit
	if ((!thisForm.unit[0].checked) &&
			(!thisForm.unit[1].checked) &&
			(!thisForm.unit[2].checked) &&
			(!thisForm.unit[3].checked) &&
			(!thisForm.unit[4].checked) &&
			(!thisForm.unit[5].checked) &&
			(!thisForm.unit[6].checked))
	{
		fieldsNeeded += requiredDescriptions[5] + "\n\t";
		fieldCheck = false;
	}
	if ((thisForm.unit[6].checked) && (thisForm.othertext.value == ""))
	{
		fieldsNeeded += requiredDescriptions[5] + "\n\t";
		fieldCheck = false;
	}

	// required sex
	if ((!thisForm.sex[0].checked) && (!thisForm.sex[1].checked))
	{
		fieldsNeeded += requiredDescriptions[2] + "\n\t";
		fieldCheck = false;
	}

  // All required fields have been entered
  if (fieldCheck == true)
  {
   	// Check email address
  	if (!isEmail(thisForm.elements["email"].value))
  	{
  		alert("Please enter a valid email address.");
			thisForm.elements.email.select();
			thisForm.elements.email.focus();
			return false;
		}
		// Check email address
  	if (!isEmail(thisForm.elements["email2"].value))
  	{
  		alert("Please enter a valid email2 address.");
			thisForm.elements.email2.select();
			thisForm.elements.email2.focus();
			return false;
		}
		if (thisForm.elements["email"].value != thisForm.elements["email2"].value)
		{
			alert("Email addresses do not match.");
			thisForm.elements.email.select();
			thisForm.elements.email.focus();
			return false;
		}

		/* only validate the number of chars in the field */
		if (thisForm.elements.telephone_areacode.value.length < 3)
		{
			alert("Please enter a valid phone number.");
			thisForm.elements.telephone_areacode.select();
			thisForm.elements.telephone_areacode.focus();
			return false;
		}
		if (thisForm.elements.telephone_prefix.value.length < 3)
		{
			alert("Please enter a valid phone number.");
			thisForm.elements.telephone_prefix.select();
			thisForm.elements.telephone_prefix.focus();
			return false;
		}
		if (thisForm.elements.telephone_number.value.length < 4)
		{
			alert("Please enter a valid phone number.");
			thisForm.elements.telephone_number.select();
			thisForm.elements.telephone_number.focus();
			return false;
		}
		if (thisForm.elements.payment[1].checked)
		{
			if (thisForm.elements.po_number.value == "")
			{
				alert("Please enter your PO Number.");
				thisForm.elements.po_number.select();
				thisForm.elements.po_number.focus();
				return false;
			}
		}
		
		return true;
  }
  // Alert with the missing fields
  else
  {
      alert(fieldsNeeded);
      return false;
  }
}

/****************************************************/
/* Description : This function will validate that 	*/
/*  the text passed in is a valid email address.		*/
/*																									*/
/****************************************************/
function isEmail(s)
{
	if (isEmpty(s))
	{
		if (isEmail.arguments.length == 1)
		{
			return defaultEmptyOK;
		}
		else
		{
			return (isEmail.arguments[1] == true);
		}
	}

	if (isWhitespace(s))
	{
		return false;
	}

	var i = 1;
	var sLength = s.length;
	while ((i < sLength) && (s.charAt(i) != "@"))
	{
		i++;
	}

	if ((i >= sLength) || (s.charAt(i) != "@"))
	{
		return false;
	}
	else
	{
		atloc = i;
	}

	j = i+1;
	i += 1;
	while ((j < sLength) && (validDomainNameChars.indexOf(s.charAt(j)) != -1)) {
		j++;
	}
	if (j < sLength) {
		return false;
	}
	while ((i < sLength) && (s.charAt(i) != ".")) {
		i++;
	}
	if (i == sLength) {
		return false;
	}

	if (i == (atloc +1)) {
		return false;
	}

	k = atloc+1;
	while (k < sLength){
		if ((s.charAt(k) == ".") && (s.charAt(k+1) == ".")) {
			return false;
		}
		k++;
	}
	l = sLength;
	while ((i < sLength -2) && (l != i) && (s.charAt(l) != ".")) {
		l = l-1
	}
	if ((i >= sLength - 2) || (s.charAt(i) != ".") || (l >= sLength - 2)) {
		return false;
	}
	else {
		return true;
	}
}

/****************************************************/
/* Description : This function will validate that 	*/
/*  the text passed in is a valid zipcode.			*/
/*													*/
/****************************************************/
function checkZIPCode (theField, emptyOK){
	if (checkZIPCode.arguments.length == 1) {
   	emptyOK = defaultEmptyOK;
   }
   if ((emptyOK == true) && (isEmpty(theField.value))) {
   	return true;
   }
   else {
   	var normalizedZIP = stripCharsInBag(theField.value, ZIPCodeDelimiters);
      if (!isZIPCode(normalizedZIP, false)) {
         return false;
      }
      else {
         theField.value = reformatZIPCode(normalizedZIP);
         return true;
      }
   }
}

function reformatZIPCode (ZIPString){
	if (ZIPString.length == 5) {
   	return ZIPString;
   }
   else {
   	return (reformat (ZIPString, "", 5, "-", 4));
   }
}

function isZIPCode (s){
	if (isEmpty(s)) {
   	if (isZIPCode.arguments.length == 1) {
   		return defaultEmptyOK;
   	}
      else {
      	return (isZIPCode.arguments[1] == true);
      }
	}
   return (isInteger(s) && ((s.length == digitsInZIPCode1) || (s.length == digitsInZIPCode2)));
}

/****************************************************/
/* Description : This function will validate that 	*/
/*  the text passed in is a valid phone number.		*/
/*													*/
/****************************************************/
function checkUSPhone(theField, emptyOK) {
	if (checkUSPhone.arguments.length == 1) {
		emptyOK = defaultEmptyOK;
	}
	if ((emptyOK == true) && (isEmpty(theField.value))) {
		return true;
	}
	else {
		var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters);
	   if (!isUSPhoneNumber(normalizedPhone, false)) {
	      return false;
	   }
	   else {
	   	theField.value = reformatUSPhone(normalizedPhone);
	      return true;
	   }
	}
}

function isUSPhoneNumber (s){
	if (isEmpty(s)) {
   	if (isUSPhoneNumber.arguments.length == 1) {
      	return defaultEmptyOK;
      }
      else {
      	return (isUSPhoneNumber.arguments[1] == true);
      }
   }
   return (isInteger(s) && s.length == digitsInUSPhoneNumber)
}

function reformatUSPhone (USPhone) {
	return (reformat (USPhone, "(", 3, ") ", 3, "-", 4));
}

/****************************************************/
/* Description : This validates the number of 		*/
/*  digits based on credit card type				*/
/*													*/
/****************************************************/
function validateCreditCardInformation(thisForm, cardName, cardNumber, cardType, cardDate) {
	// Validate for data
	if (cardName == "") {
		alert("Please enter a name for the credit card.");
		thisForm.cc_name.select();
		thisForm.cc_name.focus();
		return false;
	}
	if (cardNumber == "") {
		alert("Please enter a credit card number.");
		thisForm.cc_num0.select();
		thisForm.cc_num0.focus();
		return false;
	}
	if (cardType == "") {
		alert("Please choose a credit card type.");
		thisForm.cc_type.select();
		thisForm.cc_type.focus();
		return false;
	}
	if (cardDate == "") {
		alert("Please enter a expiration Date.");
		thisForm.cc_expmonth.select();
		thisForm.cc_expmonth.focus();
		return false;
	}
	var newCardNumber;
	// Remove dashes if they exist
	if (cardNumber.indexOf("-") != -1) {
		newCardNumber = cardNumber.replace(/-/g, "");
	}
	else {
		newCardNumber = cardNumber;
	}
	// Check card number length
	// Mastercard and Visa
	if ((cardType == "mastercard") || (cardType == "visa")) {
		if ((newCardNumber.length != 16) || (!isCardMatch(cardType, cardNumber))) {
			// Return as incorrect
			alert("Please check card number");
			thisForm.cc_num0.select();
			thisForm.cc_num0.focus();
			return false;
		}
	}
	// American Express
	else if (cardType == "amex") {
		if ((newCardNumber.length != 15) || (!isCardMatch(cardType, cardNumber))) {
			// Return as incorrect
			alert("Please check card number");
			thisForm.cc_num0.select();
			thisForm.cc_num0.focus();
			return false;
		}
	}
	// Verify credit card date
	// First check digits before the slash
	if (cardDate.indexOf("/") != -1) {
		var splitdate = cardDate.split("/")

		var nowTime = new Date();
		var month = nowTime.getMonth();
		var year = nowTime.getFullYear();
		var exMonth = splitdate[0];
		var exYear = splitdate[1];
		month++;
		if ((exMonth < month) && (exYear <= year))
		{
			alert("Sorry, your credit card has expired.");
			thisForm.elements.cc_expmonth.focus();
			return false;
		}
		return true;
	}
	else {
		alert("Expiration date is incorrect.");
		thisForm.cc_expmonth.select();
		thisForm.cc_expmonth.focus();
		return false;
	}
}

function makeArray(n) {
   for (var i = 1; i <= n; i++) {
      this[i] = 0;
   }
   this[length] = n;
   return this;
}

function makeArrayFromString() {
   for (var i = 1; i <= makeArrayFromString.arguments.length; i++){
      this[i] = makeArrayFromString.arguments[i];
   }
   this[length] = makeArrayFromString.arguments.length;
   return this;
}

function isEmpty(s) {
    return ((s == null) || (s.length == 0));
}

function isWhitespace(s) {
    var i;

    if (isEmpty(s)) return true;
    for (i = 0; i < s.length; i++)
    {
        var c = s.charAt(i);
        if (whitespace.indexOf(c) == -1) return false; // not whitespace
    }
    return true;
}
function stripCharsInBag (s, bag){
    var i;
    var returnString = "";
    for (i = 0; i < s.length; i++)
    {
        // Check that current character isn't whitespace.
        var c = s.charAt(i);
        if (bag.indexOf(c) == -1) returnString += c;
    }
    return returnString;
}

function stripCharsNotInBag (s, bag){
    var i;
    var returnString = "";
    for (i = 0; i < s.length; i++)
    {
        var c = s.charAt(i);
        if (bag.indexOf(c) != -1) returnString += c;
    }
    return returnString;
}

function isLetter (c){
   return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}

function isDigit (c){
   return ((c >= "0") && (c <= "9"))
}

function isLetterOrDigit (c){
   return (isLetter(c) || isDigit(c))
}

function isInteger (s){
    var i;
    if (isEmpty(s))
       if (isInteger.arguments.length == 1) return defaultEmptyOK;
       else return (isInteger.arguments[1] == true);
    for (i = 0; i < s.length; i++)
    {
        var c = s.charAt(i);
        if (!isDigit(c)) return false;
    }
    return true;
}

function reformat (s){
    var arg;
    var sPos = 0;
    var resultString = "";
    for (var i = 1; i < reformat.arguments.length; i++) {
       arg = reformat.arguments[i];
       if (i % 2 == 1) resultString += arg;
       else {
           resultString += s.substring(sPos, sPos + arg);
           sPos += arg;
       }
    }
    return resultString;
}

function checkCreditCard (cardType, theField){
    var normalizedCCN = stripCharsInBag(theField.value, creditCardDelimiters)
    if (!isCardMatch(cardType, normalizedCCN))
       return false;
    else
    {  theField.value = normalizedCCN
       return true
    }
}

function isCreditCard(st) {
	if (st.length > 19){
   	return (false);
   }
  	sum = 0;
  	mul = 1;
  	l = st.length;
  	for (i = 0; i < l; i++) {
    	digit = st.substring(l-i-1,l-i);
    	tproduct = parseInt(digit ,10) * mul;
    	if (tproduct >= 10){
      	sum += (tproduct % 10) + 1;
      }
    	else{
      	sum += tproduct;
      }
    	if (mul == 1){
      	mul++;
      }
    	else{
      	mul--;
      }
  	}
  	if ((sum % 10) == 0){
   	return (true);
   }
  	else{
    	return (false);
	}
}

function isVisa(cc){
  if (((cc.length == 16) || (cc.length == 13)) &&
      (cc.substring(0,1) == 4))
    return isCreditCard(cc);
  return false;
}

function isMasterCard(cc){
  firstdig = cc.substring(0,1);
  seconddig = cc.substring(1,2);
  if ((cc.length == 16) && (firstdig == 5) &&
      ((seconddig >= 1) && (seconddig <= 5)))
    return isCreditCard(cc);
  return false;
}

function isAmericanExpress(cc){
  firstdig = cc.substring(0,1);
  seconddig = cc.substring(1,2);
  if ((cc.length == 15) && (firstdig == 3) &&
      ((seconddig == 4) || (seconddig == 7)))
    return isCreditCard(cc);
  return false;
}

function isCardMatch (cardType, cardNumber){
	cardType = cardType.toUpperCase();
	var doesMatch = true;
	if ((cardType == "VISA") && (!isVisa(cardNumber)))
		doesMatch = false;
	if ((cardType == "MASTERCARD") && (!isMasterCard(cardNumber)))
		doesMatch = false;
	if ((cardType == "AMEX") && (!isAmericanExpress(cardNumber)))
		doesMatch = false;
	return doesMatch;
}