// FormCheck.js
//
// This is a JavaScript for validating input on an HTML form.
//
//
// Many of the below functions take an optional parameter eok (for "emptyOK")
// which determines whether the empty string will return true or false.
// Default behavior is controlled by global variable defaultEmptyOK.
//
// BASIC DATA VALIDATION FUNCTIONS:
//
// isWhitespace (s)               		    	Check whether string s is empty or whitespace.
// isDigit (c)                 			        Check whether character c is a digit 
// isEmail (s [,eok])              		 	    True if string s is a valid email address.
// isAlphanumeric (s [,eok])   			        True if string s is English letters and numbers only.
// isYear (s [,eok])              			    True if string s is a valid Year number.
// isDay (s [,eok])			                    True if string s is a valid day between 1 and 31.
//
//
// FUNCTIONS TO INTERACTIVELY CHECK FIELD CONTENTS:
//
// checkString (theField, s [,eok])    			Check that theField.value is allowable.
// checkEmail (theField [,eok])			        Check that theField.value is a valid Email.
// checkValid (theField, s, [,eok])			    Check that theField.value isn't empty or whitespace.
// checkPass (theField1, theField2)     		Check that theField1.value = theField2.value.
// checkDate (yearField, monthField, dayField)  Check that field values form a valid date.
//
//
//
// checkCreditCard (radio, theField)			Validate credit card info.
//
// CREDIT CARD DATA VALIDATION FUNCTIONS
// 
// isCreditCard (st)              True if credit card number passes the Luhn Mod-10 test.
// isVisa (cc)                    True if string cc is a valid VISA number.
// isMasterCard (cc)              True if string cc is a valid MasterCard number.
// isDinersClub (cc)              True if string cc is a valid Diner's Club number.
// isDiscover (cc)                True if string cc is a valid Discover card number.
// isJCB (cc)                     True if string cc is a valid JCB card number.
// isCardMatch (Type, Number)     True if Number is valid for credic card of type Type.
//
//
// FUNCTIONS TO PROMPT USER:
//
// warnEmpty (theField, s)           			Notify user that required field theField is empty.
// warnInvalid (theField, s)         			Notify user that contents of field theField are invalid.




// VARIABLE DECLARATIONS ########################################################
//
var defaultEmptyOK = false

var digits = "0123456789";

var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"

var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"


// whitespace characters
var whitespace = " \t\n\r";

// non-digit characters which are allowed in credit card numbers
var creditCardDelimiters = " "

function makeArray(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;

//
//
//
//
//

// s is an abbreviation for "string"

var sLastName = "Last Name"
var sFirstName = "First Name"
var sName = "Name"
var sMI = "MI"
var sPass = "Password"
var sStreet = "Street"
var sCity = "City"
var sStateCode = "State Code"
var sWorldState = "State, Province, or Prefecture"
var sCountry = "Country"
var sZip = "Zip"
var sPhone = "Phone Number"
var sFax = "Fax Number"
var sDateOfBirth = "Date of Birth"
var sExpirationDate = "Expiration Date"
var sEmail = "Email"
var sYear = "Year"
var sCreditCardNumber = "Credit Card Number"
var sHearFrom = "Hear About Us"


var sComputerManufacturer = "Computer manufacturer"
var sComputerModel = "Computer model"
var sComputerSerialNumber = "Computer serial number"
var sComputerMarketValue = "Computer market value"
var sYourInternetServicesProvider = "Your Internet Services Provider"
var sUniqueDescription = "Unique description"

//
//

var mPrefix = "You did not enter a value into the "
var mSuffix = " field. This is a required field. Please enter it now."

//
//
// i is an abbreviation for "invalid"
var iCreditCardPrefix = "This is not a valid "
var iCreditCardSuffix = " credit card number. Please reenter it now."

var iEmail = "This field must be a valid email address (like yourname@yourdomain.com). Please reenter it now."
var iPass = "Please enter a correct password now."
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 4 digit year number.  Please reenter it now."
var iYear_1 = "You entered an invalid value for the Date of Birth field. Please reenter it now."

// BASIC DATA VALIDATION FUNCTIONS ##############################################
//
// 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;
}

// Returns true if character c is a digit 
// (0 .. 9).

function isDigit (c)
{   return ((c >= "0") && (c <= "9"))
}

// 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;
}

//
// isEmail (STRING s [, BOOLEAN emptyOK])
// 
// Email address must be of form a@b.c -- in other words:
// * there must be at least one character before the @
// * there must be at least one character before and after the .
// * the characters @ and . are both required
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

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 k = 0;
    var sLength = s.length;
	
	// how many @ in the s
	for (var i = 0; i < sLength; i++)
	{
		if (s.charAt(i) == "@") k++;
	}
	
	if (k >= 2) return false;
	
	i = 1;
	
    // 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;
}

// Only numeric 0..9
function isAlphanumeric (s)

{   var i;

    if (isEmpty(s)) 
       if (isAlphanumeric.arguments.length == 1) return defaultEmptyOK;
       else return (isAlphanumeric.arguments[1] == true);

    // Search through string's characters one by one
    // until we find a non-alphanumeric 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 or letter.
        var c = s.charAt(i);

        if (! (isDigit(c)) )
        return false;
    }

    // All characters are numbers or letters.
    return true;
}


function isAllowable (s)

{   var i;
	var v = s.value; 
	for (i = 1; i < v.length; i++)
    {   
        // Check that current character is number or letter.
        var c = v.charAt(i);
		if ((c == "!") || (c == "?") || (c == ":") || (c == ";") || (c == "<") || (c == ">"))	return false;
    }

    // All characters are numbers or letters.
    return true;
}


function isDay (s)
{   if (isEmpty(s)) 
       if (isDay.arguments.length == 1) return defaultEmptyOK;
       else return (isDay.arguments[1] == true);   
    return ((s >= 1) && (s <= 31));
}

function isYear (s)
{   
	var sLength = s.length;
	if (isEmpty(s)) 
       if (isYear.arguments.length == 1) return defaultEmptyOK;
       else return (isYear.arguments[1] == true);
	   
	if (sLength == 4) return true;
	else return false;
}

//
//
// Notify user that required field theField is empty.
// String s describes expected contents of theField.value.
// Put focus in theField and return false.

function warnEmpty (theField, s)
{   theField.focus()
	theField.select()
    alert(mPrefix + s + mSuffix)
    return false
}



// Notify user that contents of field theField are invalid.
// String s describes expected contents of theField.value.
// Put select theField, pu focus in it, and return false.

function warnInvalid (theField, s)
{   theField.focus()
    theField.select()
    alert(s)
    return false
}





// FUNCTIONS TO INTERACTIVELY CHECK FIELD CONTENTS ############################
//
//
//
// checkString (theField, w [,eok])

function checkString (theField, s, emptyOK)
{
	if (!isAllowable(theField)) return warnEmpty (theField, s);
	else return true;

}


// Get checked value from radio button.

function checkSelection (radio)
{   for (var i = 0; i < radio.length; i++)
    {   if (radio[i].checked) { return true }
    }
    return alert("Please select YOUR subscription !");
}

//
//
// checkEmail (TEXTFIELD theField [, BOOLEAN emptyOK==false])
//
// Check that string theField.value is a valid Email.
//
// For explanation of optional argument emptyOK,
// see comments of function isInteger.

function checkEmail (theField, emptyOK)
{   
	if (isEmpty(theField.value)) return warnEmpty (theField, sEmail);
	if (checkEmail.arguments.length == 1) emptyOK = defaultEmptyOK;
    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
    else 
		if (!isEmail(theField.value, false)) return warnInvalid (theField, iEmail);
	    else return true;
}

// Check Password
function checkPass (theField, theField1)
{   
	if (theField.value != theField1.value) return warnInvalid (theField1, iPass);
	else return true;
}


function checkValid (theField, s)
{   
	if (isEmpty(theField.value)) return warnEmpty (theField, s);
	else return true;
}



function checkDate (dayField, monthField, yearField)
{   // Next line is needed on NN3 to avoid "undefined is not a number" error
    // in equality comparison below.
	 
	 var Day = dayField.value;
	 var Month = monthField.value;
	 var Year = yearField.value;

	 var date = new Date();
	 var yy = date.getYear();
	 var nowYear = (yy < 1000) ? yy + 1900 : yy;
	 var age = nowYear - Year;
	 if (!isAlphanumeric(Day)) return warnInvalid (dayField, iYear_1);
	 if (Day > daysInMonth[Month]) return warnInvalid (dayField, iYear_1);
	 if (!isAlphanumeric(Year)) return warnInvalid (yearField, iYear);
	 if (!isYear(yearField.value)) return warnInvalid (yearField, iYear);
	 if ((Year < 1900) || (Year > 2100)) return warnInvalid (yearField, iYear_1);
     if (age <= 13 ) { 
		alert("Individuals younger than 13 yeas old are not allowed to proceed with this registration.  Thank you.")
		window.opener.location.href="http://www.ztrace.com";
		window.close();
		return false; 
		}
	 return true
}


// Validate credit card info.

function checkCreditCard (cardType, theField)
{  
	if (isEmpty(theField.value)) return warnEmpty (theField, sCreditCardNumber);
	var normalizedCCN = stripCharsInBag(theField.value, creditCardDelimiters);
	if (!isCardMatch(cardType, normalizedCCN)) 
       return warnInvalid (theField, iCreditCardPrefix + cardType + iCreditCardSuffix);
    else 
    {  theField.value = normalizedCCN
       return true
    }
}

/*  ================================================================
    FUNCTION:  isCreditCard(st)
 
    INPUT:     st - a string representing a credit card number

    RETURNS:  true, if the credit card number passes the Luhn Mod-10
		    test.
	      false, otherwise
    ================================================================ */

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()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid VISA number.
		    
	      false, otherwise

    Sample number: 4111 1111 1111 1111 (16 digits)
    ================================================================ */

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()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid MasterCard
		    number.
		    
	      false, otherwise

    Sample number: 5500 0000 0000 0004 (16 digits)
    ================================================================ */

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:  isDinersClub()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid Diner's
		    Club number.
		    
	      false, otherwise

    Sample number: 30000000000004 (14 digits)
    ================================================================ */

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:  isDiscover()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid Discover
		    card number.
		    
	      false, otherwise

    Sample number: 6011000000000004 (16 digits)
    ================================================================ */

function isDiscover(cc)
{
  first4digs = cc.substring(0,4);
  if ((cc.length == 16) && (first4digs == "6011"))
    return isCreditCard(cc);
  return false;

} // END FUNCTION isDiscover()


/*  ================================================================
    FUNCTION:  isJCB()
 
    INPUT:     cc - a string representing a credit card number

    RETURNS:  true, if the credit card number is a valid JCB
		    card number.
		    
	      false, otherwise
    ================================================================ */

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:  isCardMatch()
 
    INPUT:    cardType - a string representing the credit card type
	      cardNumber - a string representing a credit card number

    RETURNS:  true, if the credit card number is valid for the particular
	      credit card type given in "cardType".
		    
	      false, otherwise
    ================================================================ */

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 == "DINERS CLUB") && (!isDinersClub(cardNumber)))
		doesMatch = false;	
	if ((cardType == "DISCOVER") && (!isDiscover(cardNumber)))
		doesMatch = false;
	if ((cardType == "JCB") && (!isJCB(cardNumber)))
		doesMatch = false;
	
	return doesMatch;

}  // END FUNCTION CardMatch()
