// VARIABLE DECLARATIONS
var newline = "\n";
var digits = "0123456789";
var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz";
var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// whitespace characters
var whitespace = " \t\n\r";
// decimal point character differs by language and culture
var decimalPointDelimiter = ".";
// non-digit characters which are allowed in phone numbers
var phoneNumberDelimiters = "()- .";
// characters which are allowed in US phone numbers
var validUSPhoneChars = digits + phoneNumberDelimiters;
// characters which are allowed in international phone numbers
// (a leading + is OK)
var validWorldPhoneChars = digits + phoneNumberDelimiters + "+";
// non-digit characters which are allowed in 
// Social Security Numbers
var SSNDelimiters = "- ";
// characters which are allowed in Social Security Numbers
var validSSNChars = digits + SSNDelimiters;
// U.S. Social Security Numbers have 9 digits.
// They are formatted as 123-45-6789.
var digitsInSocialSecurityNumber = 9;
// U.S. phone numbers have 10 digits.
// They are formatted as 123 456 7890 or (123) 456-7890.
var digitsInUSPhoneNumber = 10;
// non-digit characters which are allowed in ZIP Codes
var ZIPCodeDelimiters = "-";
// our preferred delimiter for reformatting ZIP Codes
var ZIPCodeDelimeter = "-";
// characters which are allowed in Social Security Numbers
var validZIPCodeChars = digits + ZIPCodeDelimiters;
// U.S. ZIP codes have 5 or 9 digits.
// They are formatted as 12345 or 12345-6789.
var digitsInZIPCode1 = 5;
var digitsInZIPCode2 = 9;
// non-digit characters which are allowed in credit card numbers
var creditCardDelimiters = " -";
// i is an abbreviation for "invalid"
var iEmptyString = "This field must have some value entered into it.  Please enter now.";
var iStateCode = "This field must be a valid two character U.S. state abbreviation (like CA for California). Please reenter it now.";
var iZIPCode = "This field must be a 5 or 9 digit U.S. ZIP Code (like 94043). Please reenter it now.";
var iUSPhone = "This field must be a 10 digit U.S. phone number (like 415 555 1212). Please reenter it now.";
var iWorldPhone = "This field must be a valid international phone number. Please reenter it now.";
var iSSN = "This field must be a 9 digit U.S. social security number (like 123 45 6789). Please reenter it now.";
var iEmail = "This field must be a valid email address (like foo@bar.com). Please reenter it now.";
var iCreditCardPrefix = "The credit card number entered is not a valid ";
var iCreditCardSuffix = " number.";
var iDay = "This field must be a day number between 1 and 31.  Please reenter it now.";
var iMonth = "This field must be a month number between 1 and 12.  Please reenter it now.";
var iYear = "This field must be a 2 or 4 digit year number.  Please reenter it now.";
var iDatePrefix = "The Day, Month, and Year for ";
var iDateSuffix = " do not form a valid date.  Please reenter them now.";
var iCCFullName = "You must enter the credit card holder's name as it appears on the card.";
var iCreditCard = "You must enter a valid credit card number.";
var iExpDate = "The expiration date entered has already expired.";
// p is an abbreviation for "prompt"
var pStateCode = "Please enter a 2 character code (like CA).";
var pZIPCode = "Please enter a 5 or 9 digit U.S. ZIP Code (like 94043).";
var pUSPhone = "Please enter a 10 digit U.S. phone number (like 415 555 1212).";
var pWorldPhone = "Please enter a international phone number.";
var pSSN = "Please enter a 9 digit U.S. social security number (like 123 45 6789).";
var pEmail = "Please enter a valid email address (like foo@bar.com).";
var pCreditCard = "Please enter a valid credit card number.";
var pDay = "Please enter a day number between 1 and 31.";
var pMonth = "Please enter a month number between 1 and 12.";
var pYear = "Please enter a 2 or 4 digit year number.";
var pCCFullName = "Please enter the full name as it appears on the credit card.";

var defaultEmptyOK = false
var defaultInteractive = true;
function makeArray(n) {
//*** BUG: If I put this line in, I get two error messages:
//(1) Window.length can't be set by assignment
//(2) daysInMonth has no property indexed by 4
//If I leave it out, the code works fine.
//   this.length = n;
   for (var i = 1; i <= n; i++) {
      this[i] = 0
   } 
   return this
}
var daysInMonth = makeArray(12);
daysInMonth[1] = 31;
daysInMonth[2] = 29;   // must programmatically check this
daysInMonth[3] = 31;
daysInMonth[4] = 30;
daysInMonth[5] = 31;
daysInMonth[6] = 30;
daysInMonth[7] = 31;
daysInMonth[8] = 31;
daysInMonth[9] = 30;
daysInMonth[10] = 31;
daysInMonth[11] = 30;
daysInMonth[12] = 31;
// Valid U.S. Postal Codes for states, territories, armed forces, etc.
// See http://www.usps.gov/ncsc/lookups/abbr_state.txt.
var USStateCodeDelimiter = "|";
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";
// Check whether string s is empty.
function isEmpty(s){
	return ((s == null) || (s.length == 0))
}
// Returns true if string s is empty or 
// whitespace characters only.
function isWhitespace (s){
	var i;

    // Is s empty?
    if (isEmpty(s)) return true;

    // Search through string's characters one by one
    // until we find a non-whitespace character.
    // When we do, return false; if we don't, return true.

    for (i = 0; i < s.length; i++)
    {   
        // Check that current character isn't whitespace.
        var c = s.charAt(i);

        if (whitespace.indexOf(c) == -1) return false;
    }

    // All characters are whitespace.
    return true;
}
// Removes all characters which appear in string bag from string s.
function stripCharsInBag (s, bag) {
	var i;
    var returnString = "";

    // Search through string's characters one by one.
    // If character is not in bag, append to 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;
}
// Removes all characters which do NOT appear in string bag 
// from string s.
function stripCharsNotInBag (s, bag)

{   var i;
    var returnString = "";

    // Search through string's characters one by one.
    // If character is in bag, append to 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;
}
// Removes all whitespace characters from s.
// Global variable whitespace (see above)
// defines which characters are considered whitespace.
function stripWhitespace (s) {
	return stripCharsInBag (s, whitespace)
}
// Removes initial (leading) whitespace characters from s.
// Global variable whitespace (see above)
// defines which characters are considered whitespace.
function stripInitialWhitespace (s){
	var i = 0;
    while ((i < s.length) && charInString (s.charAt(i), whitespace))
       i++;
    return s.substring (i, s.length);
}

function isLetter (c){
	return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) )
}

// Returns true if character c is a digit 
// (0 .. 9).
function isDigit (c) {
	return ((c >= "0") && (c <= "9"))
}
// Returns true if character c is a letter or digit.
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);

    // Search through string's characters one by one
    // until we find a non-numeric character.
    // When we do, return false; if we don't, return true.

    for (i = 0; i < s.length; i++) {   
        // Check that current character is number.
        var c = s.charAt(i);

        if (!isDigit(c)) return false;
    }

    // All characters are numbers.
    return true;
}

function isExpiredDate( tiCheckMonth, tiCheckYear ){
	var lRetVal = true;
	var dtNow = new Date();
	var iThisYear = dtNow.getYear();
	var iThisMonth = (dtNow.getMonth()+1);
	
//	alert("In isExpiredDate checking: " + tiCheckMonth + "/" + tiCheckYear + " against: " + iThisMonth + "/" + iThisYear);

	// Check for 2-digit year...  If it is, then bump it to 4 digits...
	if (iThisYear < 1900) {
		iThisYear += 1900;
	}
	
//	alert("In isExpiredDate checking: " + tiCheckMonth + "/" + tiCheckYear + " against: " + iThisMonth + "/" + iThisYear);
	if (tiCheckYear > iThisYear) { lRetVal = false; }
	else if (tiCheckYear == iThisYear) {
		if (tiCheckMonth >= iThisMonth) { lRetVal = false; }
	}
	
	return lRetVal;
}
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;
}


// isUSPhoneNumber (STRING s [, BOOLEAN emptyOK])
// 
// isUSPhoneNumber returns true if string s is a valid U.S. Phone
// Number.  Must be 10 digits.
//
// NOTE: Strip out any delimiters (spaces, hyphens, parentheses, etc.)
// from string s before calling this function.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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 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)))
}
function isEmail (s) {
	if (isEmpty(s)) 
    	if (isEmail.arguments.length == 1) return defaultEmptyOK;
    	else return (isEmail.arguments[1] == true);
   
    // is s whitespace?
    if (isWhitespace(s)) return false;
    
    // there must be >= 1 character before @, so we
    // start looking at character position 1 
    // (i.e. second character)
    var i = 1;
    var sLength = s.length;

    // look for @
    while ((i < sLength) && (s.charAt(i) != "@")) {
		i++;
    }

    if ((i >= sLength) || (s.charAt(i) != "@")) return false;
    else i += 2;

    // look for .
    while ((i < sLength) && (s.charAt(i) != ".")) {
		i++;
    }

    // there must be at least one character after the .
    if ((i >= sLength - 1) || (s.charAt(i) != ".")) return false;
    else return true;
}




function promptEntry (s){
	window.status = s;
}
function warnEmpty (theField, s) {
	theField.focus();
	alert(mPrefix + s + mSuffix);
	return false;
}
function warnInvalid (theField, s) {
    alert(s);
	theField.focus();
    theField.select();
    return false;
}
function checkString (theField, s, emptyOK, interactive)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
	if (checkString.arguments.length < 2) s = iEmptyString;
    if (checkString.arguments.length < 3) emptyOK = defaultEmptyOK;
    if (checkString.arguments.length < 4) interactive = defaultInteractive;
	
	if (!interactive) s += newline;
	
    if ((emptyOK == true) && (isEmpty(theField.value))) return (interactive ? true : '');
    if (isWhitespace(theField.value)) return (interactive ? warnInvalid (theField, s) : s);
    else return (interactive ? true : '');
}
function checkStateCode (theField, s, emptyOK, interactive) {
	if (checkStateCode.arguments.length < 2) s = iStateCode;
	if (checkStateCode.arguments.length < 3) emptyOK = defaultEmptyOK;
	if (checkStateCode.arguments.length < 4) interactive = defaultInteractive;

	if (!interactive) s = s + newline;
	
    if ((emptyOK == true) && (isEmpty(theField.value))) return (interactive ? true : '');
    else {
		theField.value = theField.value.toUpperCase();
		if (!isStateCode(theField.value, false)) 
			return (interactive ? warnInvalid (theField, s) : s);
		else return (interactive ? true : '');
    }
}
function checkExpDate (tiMonth, tiYear, s) {
	if (checkExpDate.arguments.length < 3) s = iExpDate;

	var interactive = false;
	if (!interactive) s = s + newline;
	
//	alert("checking isExpiredDate with " + tiMonth + " and " + tiYear);
	if (isExpiredDate(tiMonth, tiYear, false)) return s;
	else return (interactive ? true : '');
}
function reformatZIPCode (ZIPString)
{   if (ZIPString.length == 5) return ZIPString;
    else return (reformat (ZIPString, "", 5, "-", 4));
}
function checkZIPCode (theField, s, emptyOK, interactive) {
	if (checkZIPCode.arguments.length < 2) s = iZIPCode;
	if (checkZIPCode.arguments.length < 3) emptyOK = defaultEmptyOK;
	if (checkZIPCode.arguments.length < 4) interactive = defaultInteractive;

	if (!interactive) s = s + newline;
	
    if ((emptyOK == true) && (isEmpty(theField.value))) return (interactive ? true : '');
    else {
		var normalizedZIP = stripCharsInBag(theField.value, ZIPCodeDelimiters);
		if (!isZIPCode(normalizedZIP, false)) 
			return (interactive ? warnInvalid (theField, s) : s);
		else {
			// if you don't want to insert a hyphen, comment next line out
			theField.value = reformatZIPCode(normalizedZIP);
			return (interactive ? true : '');
		}
	}
}
function reformatUSPhone (USPhone) {
	return (reformat (USPhone, "(", 3, ") ", 3, "-", 4));
}
function checkUSPhone (theField, s, emptyOK, interactive) {
	if (checkUSPhone.arguments.length < 2) s = iUSPhone;
	if (checkUSPhone.arguments.length < 3) emptyOK = defaultEmptyOK;
	if (checkUSPhone.arguments.length < 4) interactive = defaultInteractive;

	if (!interactive) s = s + newline;
	
    if ((emptyOK == true) && (isEmpty(theField.value))) return (interactive ? true : '');
    else {
		var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters);
		if (!isUSPhoneNumber(normalizedPhone, false)) 
			return (interactive ? warnInvalid (theField, s) : s);
		else {
			// if you don't want to reformat as (123) 456-789, comment next line out
			theField.value = reformatUSPhone(normalizedPhone);
			return (interactive ? true : '');
		}
	}
}
function checkEmail (theField, s, emptyOK, interactive) {
	if (checkEmail.arguments.length < 2) s = iEmail;
	if (checkEmail.arguments.length < 3) emptyOK = defaultEmptyOK;
	if (checkEmail.arguments.length < 4) interactive = defaultInteractive;

	if (!interactive) s = s + newline;
	
    if ((emptyOK == true) && (isEmpty(theField.value))) return (interactive ? true : '');
    else if (!isEmail(theField.value, false)) 
       return (interactive ? warnInvalid (theField, s) : s);
    else return (interactive ? true : '');
}
function checkCreditCard (cardType, theField, s, emptyOK, interactive) {
	if (checkCreditCard.arguments.length < 3) s = iCreditCard;
	if (checkCreditCard.arguments.length < 4) emptyOK = defaultEmptyOK;
	if (checkCreditCard.arguments.length < 5) interactive = defaultInteractive;

	if (!interactive) s = s + newline;
	
//	var cardType = getRadioValue (radio);
    var normalizedCCN = stripCharsInBag(theField.value, creditCardDelimiters);
    if (!isCardMatch(cardType, normalizedCCN)) {
	       return (interactive ? warnInvalid (theField, s) : s);
	}
    else {
		theField.value = normalizedCCN;
		return (interactive ? true : '');
    }
}
function isCreditCard(st) {
  // Encoding only works on cards with less than 19 digits
  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--;
  }
// Uncomment the following line to help create credit card numbers
// 1. Create a dummy number with a 0 as the last digit
// 2. Examine the sum written out
// 3. Replace the last digit with the difference between the sum and
//    the next multiple of 10.

//  document.writeln("<BR>Sum      = ",sum,"<BR>");
//  alert("Sum      = " + sum);

  if ((sum % 10) == 0)
    return (true);
  else
    return (false);

} // END FUNCTION isCreditCard()
function isVisa(cc) {
	if (((cc.length == 16) || (cc.length == 13)) && (cc.substring(0,1) == 4))
		return isCreditCard(cc);
  return false;
}  // END FUNCTION isVisa()
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;

} // END FUNCTION isMasterCard()
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;

} // END FUNCTION isAmericanExpress()
function isDinersClub(cc)
{
  firstdig = cc.substring(0,1);
  seconddig = cc.substring(1,2);
  if ((cc.length == 14) && (firstdig == 3) &&
      ((seconddig == 0) || (seconddig == 6) || (seconddig == 8)))
    return isCreditCard(cc);
  return false;
}
function isCarteBlanche(cc)
{
  return isDinersClub(cc);
}
function isDiscover(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 16) && (first4digs == "6011"))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isDiscover()
function isEnRoute(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 15) &&
      ((first4digs == "2014") ||
       (first4digs == "2149")))
    return isCreditCard(cc);
  return false;
}
function isJCB(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 16) &&
      ((first4digs == "3088") ||
       (first4digs == "3096") ||
       (first4digs == "3112") ||
       (first4digs == "3158") ||
       (first4digs == "3337") ||
       (first4digs == "3528")))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isJCB()
function isAnyCard(cc)
{
  if (!isCreditCard(cc))
    return false;
  if (!isMasterCard(cc) && !isVisa(cc) && !isAmericanExpress(cc) && !isDinersClub(cc) &&
      !isDiscover(cc) && !isEnRoute(cc) && !isJCB(cc)) {
    return false;
  }
  return true;

} // END FUNCTION isAnyCard()
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 == "AMERICANEXPRESS") || (cardType == "AMEX") )
                && (!isAmericanExpress(cardNumber))) doesMatch = false;
	if ((cardType == "DISCOVER") && (!isDiscover(cardNumber)))
		doesMatch = false;
	if ((cardType == "JCB") && (!isJCB(cardNumber)))
		doesMatch = false;
	if ((cardType == "DINERS") && (!isDinersClub(cardNumber)))
		doesMatch = false;
	if ((cardType == "CARTEBLANCHE") && (!isCarteBlanche(cardNumber)))
		doesMatch = false;
	if ((cardType == "ENROUTE") && (!isEnRoute(cardNumber)))
		doesMatch = false;
	return doesMatch;

}  // END FUNCTION CardMatch()