// Validator.js

/*
==================================================================================
FUNCTION DESCRIPTIONS
==================================================================================

	==================================================================================
	BASIC DATA VALIDATION FUNCTIONS
	==================================================================================

	   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.

	   isWhitespace (s).....................Check whether string s is empty or whitespace.
	   isLetter (c).........................Check whether character c is an English letter 
	   isDigit (c)..........................Check whether character c is a digit 
	   isNameDelimiter (c)..................Check whether character c is a valid Name delimiter.
	   isLetterOrDigit (c)..................Check whether character c is a letter or digit.
	   isInteger (s [,eok]).................True if all characters in string s are numbers.
	   isSignedInteger (s [,eok])...........True if all characters in string s are numbers; leading + or - allowed.
	   isPositiveInteger (s [,eok]).........True if string s is an integer > 0.
	   isNonnegativeInteger (s [,eok])......True if string s is an integer >= 0.
	   isNegativeInteger (s [,eok]).........True if s is an integer < 0.
	   isNonpositiveInteger (s [,eok])......True if s is an integer <= 0.
	   isFloat (s [,eok])...................True if string s is an unsigned floating point (real) number. (Integers also OK.)
	   isSignedFloat (s [,eok]).............True if string s is a floating point number; leading + or - allowed. (Integers also OK.)
	   isAlphabetic (s [,eok])..............True if string s is English letters 
	   isAlphanumeric (s [,eok])............True if string s is English letters and numbers only.
	   isSSN (s [,eok]).....................True if string s is a valid U.S. Social Security Number.
	   isName (s [,eok])....................True if string s is a valid Name; NO numbers. 
	   isPhoneNumber (s [,eok]).............True if string s is a valid U.S. Phone Number. 
	   isIntPhoneNumber (s [,eok])..........True if string s is a valid international phone number.
	   isZIPCode (s [,eok]).................True if string s is a valid U.S. ZIP code.
	   isEmail (s [,eok])...................True if string s is a valid email address.
	   isYear (s [,eok])....................True if string s is a valid 4-digit Year number.
	   isIntegerInRange (s, a, b [,eok])....True if string s is an integer between a and b, inclusive.
	   isMonth (s [,eok])...................True if string s is a valid month between 1 and 12.
	   isDay (s [,eok]).....................True if string s is a valid day between 1 and 31.
	   daysInFebruary (year)................Returns number of days in February of that year.
	   isDate (year, month, day)............True if string arguments form a valid date.

	==================================================================================
	FORMATING FUNCTIONS
	==================================================================================

	   stripCharsInBag (s, bag).............Removes all characters in string bag from string s.
	   stripCharsNotInBag (s, bag)..........Removes all characters NOT in string bag from string s.
	   stripWhitespace (s)..................Removes all whitespace characters from s.
	   stripInitialWhitespace (s)...........Removes initial (leading) whitespace characters from s.
	   reformat (TARGETSTRING, STRING,INTEGER, STRING, INTEGER ... )....
	                                    ....Function for inserting formatting characters or delimiters into TARGETSTRING.                                       
	   reformatZIPCode (ZIPString)..........If 9 digits, inserts separator hyphen.
	   reformatSSN (SSN)....................Reformats as 123-45-6789.
	   reformatPhone (Phone)................Reformats as 123-456-789.

	==================================================================================
	PROMPT USER FUNCTIONS
	==================================================================================

	   prompt (s)...........................Display prompt string s in status bar.
	   promptEntry (s)......................Display data entry prompt string s in status bar.
	   warnEmpty (theField, s)..............Notify user that required field theField is empty.
	   warnInvalid (theField, s)............Notify user that contents of field theField are invalid.

	==================================================================================
	CHECK FIELD CONTENT FUNCTIONS
	==================================================================================

	   checkName (theField, s [,eok]).......Check that theField.value can be a valid Name.
	   checkString (theField, s [,eok]).....Check that theField.value is not empty or all whitespace.
	   checkCaseNumber (theField, s [,eok]).....Check that theField.value is not empty or all whitespace and is a number.
	   checkZIPCode (theField [,eok]).......Check that theField.value is a valid ZIP code.
	   checkPhone (theField, s [,eok])......Check that theField.value is a valid US Phone.
	   checkInternationalPhone (theField [,eok])...Check that theField.value is a valid International Phone.
	   checkEmail (theField, s [,eok])......Check that theField.value is a valid Email.
	   checkSSN (theField [,eok])...........Check that theField.value is a valid SSN.
	   checkYear (theField [,eok])..........Check that theField.value is a valid Year.
	   checkMonth (theField [,eok]).........Check that theField.value is a valid Month.
	   checkDay (theField [,eok])...........Check that theField.value is a valid Day.
	   checkDate (yearField, monthField, dayField, labelString, OKtoOmitDay)....
	  									....Check that field values form a valid date.
	   getRadioButtonValue (radio)..........Get checked value from radio button.
	   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.
	   isAmericanExpress (cc)...............True if string cc is a valid American Express number.
	   isAnyCard (cc).......................True if string cc is a valid card number for any of the accepted types.
	   isCardMatch (Type, Number)...........True if Number is valid for credic card of type Type.

*/

/*
==================================================================================
VARIABLE DECLARATIONS
==================================================================================
*/

	var digits = "0123456789";

	var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"

	var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

	// whitespace characters
	var whitespace = " \t\n\r";

	// non-letter characters which are allowed in Names
	var nameDelimiters = "-., ";

	// decimal point character
	var decimalPointDelimiter = "."

	// non-digit characters which are allowed in phone numbers
	var phoneNumberDelimiters = "()- ";

	// characters which are allowed in US phone numbers
	var validPhoneChars = 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.
	var digitsInPhoneNumber = 10;

	// non-digit characters which are allowed in ZIP Codes
	var ZIPCodeDelimiters = "-";

	// our preferred delimiter for reformatting ZIP Codes
	var ZIPCodeDelimiter = "-"

	// 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 = " "

/*
==================================================================================
CONSTANT STRING DECLARATIONS
==================================================================================
*/

		// m is short for "missing"

		var mPrefix = "Please enter your "
		var mSuffix = "."

	/*==================================================================================*/

		// s is short for "string"

		var sLastName = "Last Name"
		var sFirstName = "First Name"		
		var sWorldLastName = "Family Name"
		var sWorldFirstName = "Given Name"
		var sName = "Name"
		var sTitle = "Title"
		var sCompany = "Company Name"
		var sAddress = "Address"
		var sWorldAddress = "Address"
		var sCity = "City"
		var sState = "State"
		var sWorldState = "State, Province, or Prefecture"
		var sCountry = "Country"
		var sZIPCode = "ZIP Code"
		var sWorldPostalCode = "Postal Code"
		var sPhone = "Phone Number"
		var shPhone = "Home Phone Number"
		var swPhone = "Work Phone Number"
		var scPhone = "Cellular Phone Number"
		var sFax = "Fax Number"
		var sDateOfBirth = "Date of Birth"
		var sExpirationDate = "Expiration Date"
		var sEmail = "Email Address"
		var sWebsite = "Company URL"
		var sSSN = "Social Security Number"
		var sCreditCardNumber = "Credit Card Number"
		var sLegal = "Legal Disclaimer"
		var sDate = "Date"

		var sClassYear = "Class Year"
		var sStudentName = "Student Name"
		var sCaseNum = "Case Number"
		var sCaseName = "Case Name"
		var sLi1Type = "Learning Issue 1 Type"
		var sLi1Descript = "Learning Issue 1 Description"
		

	/*==================================================================================*/

		// i is short for "invalid"

		var iZIPCode = "Please enter a valid 5 or 9 digit U.S. ZIP Code."
		var iPhone = "Please enter phone number with area code."
		var iWorldPhone = "Please enter a valid international phone number."
		var iSSN = "Please enter a 9 digit U.S. social security number."
		var iEmail = "Please enter a valid email address."
		var iCreditCardPrefix = "This is not a valid "
		var iCreditCardSuffix = " credit card number."
		var iDay = "Please enter a day number between 1 and 31."
		var iMonth = "Please enter a month number between 1 and 12."
		var iYear = "Please enter a 2 or 4 digit year number."
		var iDatePrefix = "The Day, Month, and Year for "
		var iDateSuffix = " do not form a valid date."
		var iValue = "This is not a valid value for this field."

	/*==================================================================================*/

		// p is short for "prompt"

		var pEntryPrompt = "Please enter a "
		var pZIPCode = "5 or 9 digit U.S. ZIP Code."
		var pPhone = "10 digit U.S. phone number."
		var pWorldPhone = "valid international phone number."
		var pSSN = "9 digit U.S. social security number."
		var pEmail = "valid email address."
		var pCreditCard = "valid credit card number."
		var pDay = "day number between 1 and 31."
		var pMonth = "month number between 1 and 12."
		var pYear = "4 digit year number."
		

	/*==================================================================================*/

		/*
		   The global variable defaultEmptyOK defines default return value 
		   for many functions when they are passed the empty string. 
		   By default, they will return defaultEmptyOK.
		  
		   defaultEmptyOK is false, which means that by default, 
		   these functions will do "strict" validation.  Function
		   isInteger, for example, will only return true if it is
		   passed a string containing an integer; if it is passed
		   the empty string, it will return false.
		  
		   Change this default behavior globally (for all 
		   functions which use defaultEmptyOK) by changing the value
		   of defaultEmptyOK.
		  
		   Most of these functions have an optional argument emptyOK
		   which allows you to override the default behavior for 
		   the duration of a function call.
		  
		   This functionality is useful because it is possible to
		   say "if the user puts anything in this field, it must
		   be an integer (or a phone number, or a string, etc.), 
		   but it's OK to leave the field empty too."
		   This is the case for fields which are optional but which
		   must have a certain kind of content if filled in.
		*/

		var defaultEmptyOK = false

/*
==================================================================================
UTILITY FUNCTIONS & VERIFICATION FUNCTIONS
==================================================================================
*/

		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 auto check because of leap year
		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;

	/*==================================================================================*/

		// 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.
		    // If 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.

		function stripWhitespace (s)

		{   return stripCharsInBag (s, whitespace)
		}

	/*==================================================================================*/

		function charInString (c, s)
		{   for (i = 0; i < s.length; i++)
		    {   if (s.charAt(i) == c) return true;
		    }
		    return false
		}

	/*==================================================================================*/

		// Removes initial (leading) whitespace characters from s.

		function stripInitialWhitespace (s)

		{   var i = 0;

		    while ((i < s.length) && charInString (s.charAt(i), whitespace))
		       i++;
		    
		    return s.substring (i, s.length);
		}

	/*==================================================================================*/

		// Returns true if character c is an English letter (A .. Z, a..z).

		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 valid name delimiter.

		function isNameDelimiter (c)
		{   return ((c == "-") || (c == ".") || (c == " ") || (c == ","))
		}

	/*==================================================================================*/

		// Returns true if character c is a letter or digit.

		function isLetterOrDigit (c)
		{   return (isLetter(c) || isDigit(c))
		}

	/*==================================================================================*/

		// isInteger (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if all characters in string s are numbers.
		// Accepts non-signed integers only. Does not accept floating 
		// point, exponential notation, etc.

		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.
		    // If 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;
		}

	/*==================================================================================*/

		// isSignedInteger (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if all characters are numbers; 
		// first character is allowed to be + or - as well.
		// Does not accept floating point, exponential notation, etc.

		function isSignedInteger (s)

		{   if (isEmpty(s)) 
		       if (isSignedInteger.arguments.length == 1) return defaultEmptyOK;
		       else return (isSignedInteger.arguments[1] == true);

		    else {
		        var startPos = 0;
		        var secondArg = defaultEmptyOK;

		        if (isSignedInteger.arguments.length > 1)
		            secondArg = isSignedInteger.arguments[1];

		        // skip leading + or -
		        if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
		           startPos = 1;    
		        return (isInteger(s.substring(startPos, s.length), secondArg))
		    }
		}

	/*==================================================================================*/

		// isPositiveInteger (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is an integer > 0.

		function isPositiveInteger (s)
		{   var secondArg = defaultEmptyOK;

		    if (isPositiveInteger.arguments.length > 1)
		        secondArg = isPositiveInteger.arguments[1];

		    // The next line is a bit strange.  What it means is:
		    // a) s must be a signed integer, AND
		    // b) one of the following must be true:
		    //    i)  s is empty and we are supposed to return true for
		    //        empty strings
		    //    ii) this is a positive, not negative, number

		    return (isSignedInteger(s, secondArg)
		         && ( (isEmpty(s) && secondArg)  || (parseInt (s) > 0) ) );
		}

	/*==================================================================================*/

		// isNonnegativeInteger (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is an integer >= 0.

		function isNonnegativeInteger (s)
		{   var secondArg = defaultEmptyOK;

		    if (isNonnegativeInteger.arguments.length > 1)
		        secondArg = isNonnegativeInteger.arguments[1];

		    // The next line is a bit strange.  What it means is:
		    // a) s must be a signed integer, AND
		    // b) one of the following must be true:
		    //    i)  s is empty and we are supposed to return true for
		    //        empty strings
		    //    ii) this is a number >= 0

		    return (isSignedInteger(s, secondArg)
		         && ( (isEmpty(s) && secondArg)  || (parseInt (s) >= 0) ) );
		}

	/*==================================================================================*/

		// isNegativeInteger (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is an integer < 0.

		function isNegativeInteger (s)
		{   var secondArg = defaultEmptyOK;

		    if (isNegativeInteger.arguments.length > 1)
		        secondArg = isNegativeInteger.arguments[1];

		    // The next line is a bit strange.  What it means is:
		    // a) s must be a signed integer, AND
		    // b) one of the following must be true:
		    //    i)  s is empty and we are supposed to return true for
		    //        empty strings
		    //    ii) this is a negative, not positive, number

		    return (isSignedInteger(s, secondArg)
		         && ( (isEmpty(s) && secondArg)  || (parseInt (s) < 0) ) );
		}

	/*==================================================================================*/

		// isNonpositiveInteger (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is an integer <= 0.

		function isNonpositiveInteger (s)
		{   var secondArg = defaultEmptyOK;

		    if (isNonpositiveInteger.arguments.length > 1)
		        secondArg = isNonpositiveInteger.arguments[1];

		    // The next line is a bit strange.  What it means is:
		    // a) s must be a signed integer, AND
		    // b) one of the following must be true:
		    //    i)  s is empty and we are supposed to return true for
		    //        empty strings
		    //    ii) this is a number <= 0

		    return (isSignedInteger(s, secondArg)
		         && ( (isEmpty(s) && secondArg)  || (parseInt (s) <= 0) ) );
		}

	/*==================================================================================*/

		// isFloat (STRING s [, BOOLEAN emptyOK])
		// 
		// True if string s is an unsigned floating point (real) number. 
		//
		// Also returns true for unsigned integers. If you wish
		// to distinguish between integers and floating point numbers,
		// first call isInteger, then call isFloat.
		//
		// Does not accept exponential notation.

		function isFloat (s)

		{   var i;
		    var seenDecimalPoint = false;

		    if (isEmpty(s)) 
		       if (isFloat.arguments.length == 1) return defaultEmptyOK;
		       else return (isFloat.arguments[1] == true);

		    if (s == decimalPointDelimiter) return false;

		    // 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 ((c == decimalPointDelimiter) && !seenDecimalPoint) seenDecimalPoint = true;
		        else if (!isDigit(c)) return false;
		    }

		    // All characters are numbers.
		    return true;
		}

	/*==================================================================================*/

		// isSignedFloat (STRING s [, BOOLEAN emptyOK])
		// 
		// True if string s is a signed or unsigned floating point 
		// (real) number. First character is allowed to be + or -.
		// Also returns true for unsigned integers. If you wish
		// to distinguish between integers and floating point numbers,
		// first call isSignedInteger, then call isSignedFloat.
		// Does not accept exponential notation.

		function isSignedFloat (s)

		{   if (isEmpty(s)) 
		       if (isSignedFloat.arguments.length == 1) return defaultEmptyOK;
		       else return (isSignedFloat.arguments[1] == true);

		    else {
		        var startPos = 0;
		        var secondArg = defaultEmptyOK;

		        if (isSignedFloat.arguments.length > 1)
		            secondArg = isSignedFloat.arguments[1];

		        // skip leading + or -
		        if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
		           startPos = 1;    
		        return (isFloat(s.substring(startPos, s.length), secondArg))
		    }
		}

	/*==================================================================================*/

		// isAlphabetic (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is English letters (A .. Z, a..z) only.

		function isAlphabetic (s)

		{   var i;

		    if (isEmpty(s)) 
		       if (isAlphabetic.arguments.length == 1) return defaultEmptyOK;
		       else return (isAlphabetic.arguments[1] == true);

		    // Search through string's characters one by one
		    // until we find a non-alphabetic character.
		    // If we do, return false; if we don't, return true.

		    for (i = 0; i < s.length; i++)
		    {   
		        // Check that current character is letter.
		        var c = s.charAt(i);

		        if (!isLetter(c))
		        return false;
		    }

		    // All characters are letters.
		    return true;
		}

	/*==================================================================================*/

		// isName (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is English letters (A .. Z, a..z) or
		// is among the "nameDelimiters"

		function isName (s)

		{   var i;

		    if (isEmpty(s)) 
		       if (isName.arguments.length == 1) return defaultEmptyOK;
		       else return (isName.arguments[1] == true);

		    // Search through string's characters one by one
		    // until we find a non-alphabetic character or a non-name delimiter.
		    // If we do, return false; if we don't, return true.

		    for (i = 0; i < s.length; i++)
		    {   
		        // Check that current character is letter.
		        var c = s.charAt(i);

		        //if (!isLetter(c))
		        if ((!isLetter(c)) && (!isNameDelimiter(c)))
		        return false;
		    }

		    // All characters are letters or name delimiters.
		    return true;
		}


	/*==================================================================================*/

		// isAlphanumeric (STRING s [, BOOLEAN emptyOK])
		// 
		// Returns true if string s is English letters (A .. Z, a..z) and numbers only.

		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.
		    // If 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 (! (isLetter(c) || isDigit(c) ) )
		        return false;
		    }

		    // All characters are numbers or letters.
		    return true;
		}

	/*==================================================================================*/

		/* 
		   reformat (TARGETSTRING, STRING, INTEGER, STRING, INTEGER ... )       
		  
		   reformat takes one named argument, a string s, and any number
		   of other arguments.  The other arguments must be integers or
		   strings.  These other arguments specify how string s is to be
		   reformatted and how and where other strings are to be inserted
		   into it.
		  
		   reformat processes the other arguments in order one by one.
		   * If the argument is an integer, reformat appends that number 
		     of sequential characters from s to the resultString.
		   * If the argument is a string, reformat appends the string
		     to the resultString.
		  
		   NOTE: The first argument after TARGETSTRING must be a string.
		   (It can be empty.)  The second argument must be an integer.
		   Thereafter, integers and strings must alternate.
		  
		   It is the caller's responsibility to make sure that we do not
		   try to copy more characters from s than s.length.
		  
		   EXAMPLES:
		  
		   - To reformat a 10-digit U.S. phone number from "1234567890"
		     to "123-456-7890" make this function call:
		     reformat("1234567890", "", 3, "-", 3, "-", 4)
		  
		   - To reformat a 9-digit U.S. Social Security number from
		     "123456789" to "123-45-6789" make this function call:
		     reformat("123456789", "", 3, "-", 2, "-", 4)
		  
		   If you have a string which is already delimited in one way
		   (example: a phone number delimited with spaces as "123 456 7890")
		   and you want to delimit it in another way using function reformat,
		   call function stripCharsNotInBag to remove the unwanted 
		   characters, THEN call function reformat to delimit as desired.
		  
		   EXAMPLE:
		  
		   reformat (stripCharsNotInBag ("123 456 7890", digits),
		             "", 3, "-", 3, "-", 4)
		*/

		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;
		}

	/*==================================================================================*/

		// isSSN (STRING s [, BOOLEAN emptyOK])
		// 
		// isSSN returns true if string s is a valid U.S. Social
		// Security Number.  Must be 9 digits.
		//
		// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
		// from string s before calling this function.

		function isSSN (s)
		{   if (isEmpty(s)) 
		       if (isSSN.arguments.length == 1) return defaultEmptyOK;
		       else return (isSSN.arguments[1] == true);
		    return (isInteger(s) && s.length == digitsInSocialSecurityNumber)
		}



		// isPhoneNumber (STRING s [, BOOLEAN emptyOK])
		// 
		// isPhoneNumber 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.

		function isPhoneNumber (s)
		{   if (isEmpty(s)) 
		       if (isPhoneNumber.arguments.length == 1) return defaultEmptyOK;
		       else return (isPhoneNumber.arguments[1] == true);
		    return (isInteger(s) && s.length == digitsInPhoneNumber)
		}

	/*==================================================================================*/

		// isIntPhoneNumber (STRING s [, BOOLEAN emptyOK])
		// 
		// isIntPhoneNumber returns true if string s is a valid 
		// international phone number.  Must be digits only; any length OK.
		// May be prefixed by + character.
		//
		// NOTE: Strip out any delimiters (spaces, hyphens, parentheses, etc.)
		// from string s before calling this function.  You may leave in 
		// leading + character if you wish.

		function isIntPhoneNumber (s)
		{   if (isEmpty(s)) 
		       if (isIntPhoneNumber.arguments.length == 1) return defaultEmptyOK;
		       else return (isIntPhoneNumber.arguments[1] == true);
		    return (isPositiveInteger(s))
		}

	/*==================================================================================*/

		// isZIPCode (STRING s [, BOOLEAN emptyOK])
		// 
		// isZIPCode returns true if string s is a valid 
		// U.S. ZIP code.  Must be 5 or 9 digits only.
		//
		// NOTE: Strip out any delimiters (spaces, hyphens, etc.)
		// from string s before calling this function.  

		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)))
		}

	/*==================================================================================*/

		// 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

		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;
		}

	/*==================================================================================*/

		// isYear (STRING s [, BOOLEAN emptyOK])
		// 
		// isYear returns true if string s is a valid year number.

		function isYear (s)
		{   if (isEmpty(s)) 
		       if (isYear.arguments.length == 1) return defaultEmptyOK;
		       else return (isYear.arguments[1] == true);
		    if (!isNonnegativeInteger(s)) return false;
		    return (s.length == 4);
		}

	/*==================================================================================*/

		// isIntegerInRange (STRING s, INTEGER a, INTEGER b [, BOOLEAN emptyOK])
		// 
		// isIntegerInRange returns true if string s is an integer 
		// within the range of integer arguments a and b, inclusive.

		function isIntegerInRange (s, a, b)
		{   if (isEmpty(s)) 
		       if (isIntegerInRange.arguments.length == 1) return defaultEmptyOK;
		       else return (isIntegerInRange.arguments[1] == true);

		    if (!isInteger(s, false)) return false;
		    var num = parseInt (s);
		    return ((num >= a) && (num <= b));
		}

	/*==================================================================================*/

		// isMonth (STRING s [, BOOLEAN emptyOK])
		// 
		// isMonth returns true if string s is a valid month number between 1 and 12.

		function isMonth (s)
		{   if (isEmpty(s)) 
		       if (isMonth.arguments.length == 1) return defaultEmptyOK;
		       else return (isMonth.arguments[1] == true);
		    return isIntegerInRange (s, 1, 12);
		}

	/*==================================================================================*/

		// isDay (STRING s [, BOOLEAN emptyOK])
		// 
		// isDay returns true if string s is a valid day number between 1 and 31.

		function isDay (s)
		{   if (isEmpty(s)) 
		       if (isDay.arguments.length == 1) return defaultEmptyOK;
		       else return (isDay.arguments[1] == true);   
		    return isIntegerInRange (s, 1, 31);
		}

	/*==================================================================================*/

		// daysInFebruary (INTEGER year)
		// 
		// Given integer argument year, returns number of days in February of that year.

		function daysInFebruary (year)
		{   // February has 29 days in any year evenly divisible by four,
		    // EXCEPT for centurial years which are not also divisible by 400.
		    return (  ((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0) ) ) ? 29 : 28 );
		}

	/*==================================================================================*/

		// isDate (STRING year, STRING month, STRING day)
		//
		// isDate returns true if string arguments year, month, and day 
		// form a valid date.

		function isDate (year, month, day)
		{   // catch invalid years (not 4-digit) and invalid months and days.
		    if (! (isYear(year, false) && isMonth(month, false) && isDay(day, false))) return false;

		    var intYear = parseInt(year);
		    var intMonth = parseInt(month);
		    var intDay = parseInt(day);

		    // catch invalid days, NOT for February
		    if (intDay > daysInMonth[intMonth]) return false; 
			
			// catch invalid days, for February
		    if ((intMonth == 2) && (intDay > daysInFebruary(intYear))) return false;

		    return true;
		}

/*
==================================================================================
FUNCTIONS TO NOTIFY USER OF INPUT REQUIREMENTS OR MISTAKES.
==================================================================================
*/

		// Display prompt string s in status bar.

		function prompt (s)
		{   window.status = s
		}

	/*==================================================================================*/

		// Display data entry prompt string s in status bar.

		function promptEntry (s)
		{   window.status = pEntryPrompt + s
		}


	/*==================================================================================*/

		// 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()
		    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, put focus in it, and return false.

		function warnInvalid (theField, s)
		{	theField.focus()
		    theField.select()
		    alert(s)
		    return false
		}

/*
==================================================================================
FUNCTIONS TO INTERACTIVELY CHECK OR MODIFY VARIOUS FIELDS
==================================================================================
*/

		// checkName (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
		// Check that string theField.value can be a valid Name; letters only.
		// isAlphabetic (s [,eok])

		function checkName (theField, s, emptyOK)
		{   if (checkName.arguments.length == 2) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isEmpty(theField.value))
				return warnEmpty (theField, s);
		    if (!isName(theField.value))
				return warnInvalid (theField, "This is not a valid " + s);
		    else return true;
		}

	/*==================================================================================*/

		// checkString (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
		// Check that string theField.value is not all whitespace.

		function checkString (theField, s, emptyOK)
		{   if (checkString.arguments.length == 2) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isWhitespace(theField.value))
		       return warnEmpty (theField, s);
		    else return true;
		}

	/*==================================================================================*/

		// checkCaseNumber (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
		// Check that string theField.value is not all whitespace and is a number.

		function checkCaseNumber (theField, s, emptyOK)
		{   if (checkCaseNumber.arguments.length == 2) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isWhitespace(theField.value))
		       return warnEmpty (theField, s);
		    if (!isInteger(theField.value))
				return warnInvalid (theField, "This is not a valid " + s);
		    if (theField.value.length < 3)
				return warnInvalid (theField, "This is not a valid " + s + ". This should be the 3-digit case number, not the number of cases you have completed.");
		    else return true;
		}

	/*==================================================================================*/

		// takes ZIPString, a string of 5 or 9 digits;
		// if 9 digits, inserts separator hyphen

		function reformatZIPCode (ZIPString)
		{   if (ZIPString.length == 5) return ZIPString;
		    else return (reformat (ZIPString, "", 5, "-", 4));
		}

	/*==================================================================================*/

		// checkZIPCode (TEXTFIELD theField [, BOOLEAN emptyOK==false])
		//
		// Check that string theField.value is a valid ZIP code.

		function checkZIPCode (theField, emptyOK)
		{   if (checkZIPCode.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isEmpty(theField.value))
				return warnEmpty (theField, sZIPCode);
		    else
		    { var normalizedZIP = stripCharsInBag(theField.value, ZIPCodeDelimiters)
		      if (!isZIPCode(normalizedZIP, false)) 
		         return warnInvalid (theField, iZIPCode);
		      else 
		      {  
		         theField.value = reformatZIPCode(normalizedZIP)
		         return true;
		      }
		    }
		}

	/*==================================================================================*/

		// reformatPhone (s)
		// takes Phone, a string of 10 digits
		// and reformats as 123-456-789

		function reformatPhone (Phone)
		{   return (reformat (Phone, "", 3, "-", 3, "-", 4))
		}

	/*==================================================================================*/

		// checkPhone (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
		//
		// Check that string theField.value is a valid US Phone.

		function checkPhone (theField, s, emptyOK)
		{   if (checkPhone.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isWhitespace(theField.value))
				return warnEmpty (theField, s);
		    else
		    {  var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters)
		       if (!isPhoneNumber(normalizedPhone, false)) 
		          return warnInvalid (theField, iPhone);
		       else 
		       {  theField.value = reformatPhone(normalizedPhone)
		          return true;
		       }
		    }
		}

	/*==================================================================================*/

		// checkInternationalPhone (TEXTFIELD theField [, BOOLEAN emptyOK==false])
		//
		// Check that string theField.value is a valid International Phone.

		function checkInternationalPhone (theField, emptyOK)
		{   if (checkInternationalPhone.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isEmpty(theField.value))
				return warnEmpty (theField, sPhone);
		    else
		    {  if (!isIntPhoneNumber(theField.value, false)) 
		          return warnInvalid (theField, iWorldPhone);
		       else return true;
		    }
		}

	/*==================================================================================*/

		// checkEmail (TEXTFIELD theField, STRING s, [, BOOLEAN emptyOK==false])
		//
		// Check that string theField.value is a valid Email.

		function checkEmail (theField, s, emptyOK)
		{   if (checkEmail.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isWhitespace(theField.value))
				return warnEmpty (theField, sEmail);
		    else if (!isEmail(theField.value, false)) 
		       return warnInvalid (theField, iEmail);
		    else return true;
		}


	/*==================================================================================*/

		// reformatSSN (s)
		// takes SSN, a string of 9 digits
		// and reformats as 123-45-6789

		function reformatSSN (SSN)
		{   return (reformat (SSN, "", 3, "-", 2, "-", 4))
		}

	/*==================================================================================*/

		// Check that string theField.value is a valid SSN.

		function checkSSN (theField, emptyOK)
		{   if (checkSSN.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (isEmpty(theField.value))
				return warnEmpty (theField);
		    else
		    {  var normalizedSSN = stripCharsInBag(theField.value, SSNDelimiters)
		       if (!isSSN(normalizedSSN, false)) 
		          return warnInvalid (theField, iSSN);
		       else 
		       {  theField.value = reformatSSN(normalizedSSN)
		          return true;
		       }
		    }
		}

	/*==================================================================================*/

		// checkYear (theField, emptyOK)
		// Check that string theField.value is a valid Year.

		function checkYear (theField, emptyOK)
		{   if (checkYear.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (!isYear(theField.value, false)) 
		       return warnInvalid (theField, iYear);
		    else return true;
		}

	/*==================================================================================*/

		// checkMonth (theField, emptyOK)
		// Check that string theField.value is a valid Month.

		function checkMonth (theField, emptyOK)
		{   if (checkMonth.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (!isMonth(theField.value, false)) 
		       return warnInvalid (theField, iMonth);
		    else return true;
		}

	/*==================================================================================*/

		// checkDay (theField, emptyOK)
		// Check that string theField.value is a valid Day.

		function checkDay (theField, emptyOK)
		{   if (checkDay.arguments.length == 1) emptyOK = defaultEmptyOK;
		    if ((emptyOK == true) && (isEmpty(theField.value))) return true;
		    if (!isDay(theField.value, false)) 
		       return warnInvalid (theField, iDay);
		    else return true;
		}

	/*==================================================================================*/

		// checkDate (yearField, monthField, dayField, STRING labelString [, OKtoOmitDay==false])
		//
		// Check that yearField.value, monthField.value, and dayField.value 
		// form a valid date.

		function checkDate (yearField, monthField, dayField, labelString, OKtoOmitDay)
		{   if (checkDate.arguments.length == 4) OKtoOmitDay = false;
		    if (!isYear(yearField.value)) return warnInvalid (yearField, iYear);
		    if (!isMonth(monthField.value)) return warnInvalid (monthField, iMonth);
		    if ( (OKtoOmitDay == true) && isEmpty(dayField.value) ) return true;
		    else if (!isDay(dayField.value)) 
		       return warnInvalid (dayField, iDay);
		    if (isDate (yearField.value, monthField.value, dayField.value))
		       return true;
		    alert (iDatePrefix + labelString + iDateSuffix)
		    return false
		}

	/*==================================================================================*/

		// Get checked value from radio button.

		function getRadioButtonValue (radio)
		{   for (var i = 0; i < radio.length; i++)
		    {   if (radio[i].checked) { break }
		    }
		    return radio[i].value
		}

	/*==================================================================================*/

		// Validate credit card info.

		function checkCreditCard (radio, theField)
		{   var cardType = getRadioButtonValue (radio)
		    var normalizedCCN = stripCharsInBag(theField.value, creditCardDelimiters)
		    if (!isCardMatch(cardType, normalizedCCN)) 
		       return warnInvalid (theField, iCreditCardPrefix + cardType + iCreditCardSuffix);
		    else 
		    {  theField.value = normalizedCCN
		       return true
		    }
		}

/*
==================================================================================
CREDIT CARD VERIFICATION FUNCTIONS
==================================================================================
*/

		// FUNCTION:	isCreditCard(st)
		// INPUT:		st - a string representing a credit card number
		// RETURNS:		true, if the credit card number passes the 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--;
		  }

		  if ((sum % 10) == 0)
		    return (true);
		  else
		    return (false);

		}

	/*==================================================================================*/

		// FUNCTION:	isVisa(cc)
		// 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;
		}

	/*==================================================================================*/

		// FUNCTION:	isMasterCard(cc)
		// 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;

		}

	/*==================================================================================*/

		// FUNCTION:	isAmericanExpress(css)
		// INPUT:		cc - a string representing a credit card number
		// RETURNS:		true, if the credit card number is a valid American Express number.
		//				false, otherwise
		//
		//				Sample number: 340 6450932 01019 (15 digits)
		    
		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:	isAnyCard(cc)
		// INPUT:		cc - a string representing a credit card number
		// RETURNS:		true, if the credit card number is any valid credit
		//				card number for any of the accepted card types.
		//				false, otherwise

		function isAnyCard(cc)
		{
		  if (!isCreditCard(cc))
		    return false;
		  if (!isMasterCard(cc) && !isVisa(cc) && !isAmericanExpress(cc)) {
		    return false;
		  }
		  return true;

		}

	/*==================================================================================*/

		// 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 == "AMERICANEXPRESS") || (cardType == "AMEX") )
		        && (!isAmericanExpress(cardNumber))) doesMatch = false;
			return doesMatch;

		}

	/*==================================================================================*/
