//-----------------------------------------------------------------------------
// validate.js 
//
// This is a set of JavaScript functions for validating input on an HTML form.
//
// Based on FormChek.js
// 18 Feb 97 created Eric Krock
// (c) 1997 Netscape Communications Corporation
//-----------------------------------------------------------------------------

// GLOBAL VARIABLES

// "Bags" of characters found in input strings.  See stripCharsInBag().
var whitespace = " \t\n\r";
var phoneNumberDelimiters = "+()- ";
var ZIPCodeDelimiters = "-";
var creditCardDelimiters = " ";
var USStateCodeDelimiter = "|";

// Valid US state abbreviations
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";

// warning message strings
var mPrefix = "You did not enter valid information for ";
var mSuffix = ".";
var iCode = "You did not enter a valid 'Code'.";
var iCodeExample = "You entered the example code.";
var iEmail = "You did not enter a valid 'E-mail'.";
var iCreditCardPrefix = "You did not enter a valid ";
var iCreditCardSuffix = " 'Card Number'.";
var iCreditCardType = "You did not choose a valid 'Card Type'.";
var iMonth = "You did not enter a valid 'Card Expires Month'.";
var iYear = "You did not enter a valid 'Card Expires Year'.";

// Is it OK for an input string to be empty by default.  Can be overidden by
// the calling subroutine.
var defaultEmptyOK = false;

// INPUT PARSING ROUTINES

//-----------------------------------------------------------------------------
// Verifies input string has a value
//-----------------------------------------------------------------------------
function isEmpty(s) {
	return ( (s == null) || (s.length == 0) );
}

//-----------------------------------------------------------------------------
// Determines whether input string contains whitespace
//-----------------------------------------------------------------------------
function isWhitespace(s) {
	var i;

	if ( isEmpty(s) ) 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;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Removes characters contained in bag from input string
//-----------------------------------------------------------------------------
function stripCharsInBag(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;
}

//-----------------------------------------------------------------------------
// Removes whitespace from input string
//-----------------------------------------------------------------------------
function stripWhitespace(s) {
	return stripCharsInBag(s, whitespace);
}

//-----------------------------------------------------------------------------
// Determines whether input string contains a character
//-----------------------------------------------------------------------------
function charInString(c, s) {   
	for ( i = 0; i < s.length; i++ ) { 
		if (s.charAt(i) == c) return true;
	}
	return false
}

//-----------------------------------------------------------------------------
// Removes initial whitespace from input string
//-----------------------------------------------------------------------------
function stripInitialWhitespace(s) {
	var i = 0;
	while ( (i < s.length) && charInString(s.charAt(i), whitespace) ) i++;
    return s.substring (i, s.length);
}

//-----------------------------------------------------------------------------
// Determines whether character can be classified as a character
//-----------------------------------------------------------------------------
function isLetter(c) {
	return ( ((c >= "a") && (c <= "z")) || ((c >= "A") && (c <= "Z")) );
}

//-----------------------------------------------------------------------------
// Determines whether character is a digit
//-----------------------------------------------------------------------------
function isDigit(c) {   
	return ( (c >= "0") && (c <= "9") );
}

//-----------------------------------------------------------------------------
// Determines whether character is a letter or a digit
//-----------------------------------------------------------------------------
function isLetterOrDigit(c) {
	return ( isLetter(c) || isDigit(c) );
}

//-----------------------------------------------------------------------------
// Determines whether input string is an integer
//-----------------------------------------------------------------------------
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;
}

//-----------------------------------------------------------------------------
// Determines whether input string is a signed integer
//-----------------------------------------------------------------------------
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 sign (+ or -)
		if ( (s.charAt(0) == "-") || (s.charAt(0) == "+") )
			startPos = 1;    
		return (isInteger(s.substring(startPos, s.length), secondArg))
	}
}

//-----------------------------------------------------------------------------
// Determines whether input string is a positive integer
//-----------------------------------------------------------------------------
function isPositiveInteger(s) {
	var secondArg = defaultEmptyOK;

	if ( isPositiveInteger.arguments.length > 1 )
		secondArg = isPositiveInteger.arguments[1];

	return ( isSignedInteger(s, secondArg) && ( (isEmpty(s) && secondArg)  || (parseInt (s) > 0) ) );
}

//-----------------------------------------------------------------------------
// Determines whether input string is a non-negative integer
//-----------------------------------------------------------------------------
function isNonnegativeInteger(s) {
	var secondArg = defaultEmptyOK;

	if ( isNonnegativeInteger.arguments.length > 1 )
		secondArg = isNonnegativeInteger.arguments[1];

	return ( isSignedInteger(s, secondArg) && ( (isEmpty(s) && secondArg)  || (parseInt (s) >= 0) ) );
}

//-----------------------------------------------------------------------------
// Determines whether input string an integer in the range [a,b]
//-----------------------------------------------------------------------------
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) );
}

//-----------------------------------------------------------------------------
// Determines whether input string contains only alphabetic characters
//-----------------------------------------------------------------------------
function isAlphabetic(s) {
	var i;

	if ( isEmpty(s) ) 
		if ( isAlphabetic.arguments.length == 1 ) return defaultEmptyOK;
		else return ( isAlphabetic.arguments[1] == true );

	for ( i = 0; i < s.length; i++ ) {   
		var c = s.charAt(i);
		if ( !isLetter(c) )	return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Determines whether input string contains only alphabetic characters and 
// digits
//-----------------------------------------------------------------------------
function isAlphanumeric(s) {
	var i;

	if ( isEmpty(s) ) 
		if ( isAlphanumeric.arguments.length == 1 ) return defaultEmptyOK;
		else return ( isAlphanumeric.arguments[1] == true );

	for ( i = 0; i < s.length; i++ ) {   
		var c = s.charAt(i);
		if ( ! (isLetter(c) || isDigit(c) ) ) return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Determines whether input string contains only alphabetic characters and 
// digits
//-----------------------------------------------------------------------------
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;
}

//-----------------------------------------------------------------------------
// Determines whether input string is formatted as an international phone 
// number
//-----------------------------------------------------------------------------
function isInternationalPhoneNumber(s) {
	if ( isEmpty(s) ) 
		if ( isInternationalPhoneNumber.arguments.length == 1 ) return defaultEmptyOK;
		else return ( isInternationalPhoneNumber.arguments[1] == true );
	return (isPositiveInteger(s));
}

//-----------------------------------------------------------------------------
// Determines whether input string is formatted as an US Postal Code
//-----------------------------------------------------------------------------
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)) );
}

//-----------------------------------------------------------------------------
// Determines whether input string is formatted as a US state abbreviation
//-----------------------------------------------------------------------------
function isStateCode(s) {
	if ( isEmpty(s) ) 
		if ( isStateCode.arguments.length == 1 ) return defaultEmptyOK;
		else return ( isStateCode.arguments[1] == true );
	return ( (USStateCodes.indexOf(s) != -1) && (s.indexOf(USStateCodeDelimiter) == -1) );
}

//-----------------------------------------------------------------------------
// Determines whether input string is formatted as an email address
//-----------------------------------------------------------------------------
function isEmail(s) {
	var sSplitArray1, sSplitArray2;

	if ( isEmpty(s) ) {
		if ( isEmail.arguments.length == 1 ) return defaultEmptyOK;
		else return ( isEmail.arguments[1] == true );
	}
   
	if ( isWhitespace(s) ) return false;

	if ( (s.indexOf("@") != s.lastIndexOf("@")) || (s.indexOf(".") == 0) || (s.indexOf("@") == 0) ||
		(s.indexOf("..") != -1 ) || (s.indexOf("@.") != -1) || (s.indexOf(".@") != -1) ) {
		return false;
	}

	sSplitArray1 = s.split("@");
	if ( sSplitArray1.length != 2 ) return false;

	sSplitArray2 = sSplitArray1[1].split(".");
	if ( sSplitArray2.length < 2 ) return false;

	if ( sSplitArray2[sSplitArray2.length-1].length > 3 ) return false;

	return true;
}

//-----------------------------------------------------------------------------
// Determines whether input string is formatted as two or four digit year
//-----------------------------------------------------------------------------
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 == 2) || (s.length == 4) );
}

//-----------------------------------------------------------------------------
// Determines whether input string is formatted as a calendar month
//-----------------------------------------------------------------------------
function isMonth(s) {
	if ( isEmpty(s) ) 
		if ( isMonth.arguments.length == 1 ) return defaultEmptyOK;
		else return ( isMonth.arguments[1] == true );
	return isIntegerInRange(s, 1, 12);
}

// CREDIT CARD VERIFICATION ROUTINES

//-----------------------------------------------------------------------------
// Determines whether input string is a credit card number
//-----------------------------------------------------------------------------
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);
}

//-----------------------------------------------------------------------------
// Determines whether input string is a visa number
//-----------------------------------------------------------------------------
function isVisa(cc) {
	if ( ((cc.length == 16) || (cc.length == 13) ) && (cc.substring(0,1) == 4) ) return isCreditCard(cc);
	return false;
}

//-----------------------------------------------------------------------------
// Determines whether input string is a master card number
//-----------------------------------------------------------------------------
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;
} 

//-----------------------------------------------------------------------------
// Determines whether input string is an american express number
//-----------------------------------------------------------------------------
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;
}

//-----------------------------------------------------------------------------
// Determines whether the given number matches the given card type
//-----------------------------------------------------------------------------
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;
}

// NON-INPUT FIELD ROUTINES

//-----------------------------------------------------------------------------
// Gets value of radio button selection
//-----------------------------------------------------------------------------
function getRadioButtonValue(radio) {
	if ( radio.length == null ) { 
		if ( radio.checked == true ) return radio.value;
		else return null;
	}
	for ( var i = 0; i < radio.length; i++ ) {
		if ( radio[i].checked ) break;
    }
    if ( i == radio.length ) return null;
	if ( radio[i] == null ) return null;
	
	return radio[i].value;
}

// WARNING DISPLAY ROUTINES

//-----------------------------------------------------------------------------
// Display empty input string warning to client 
//-----------------------------------------------------------------------------
function warnEmpty(theField, s) {
	alert(mPrefix + s + mSuffix);
	return false;
}

//-----------------------------------------------------------------------------
// Display invalid input string warning to client
//-----------------------------------------------------------------------------
function warnInvalid(theField, s) {
	alert(s);
	return false;
}

// INPUT FIELD VALIDATION ROUTINES

//-----------------------------------------------------------------------------
// Verifies input field is a valid string
//-----------------------------------------------------------------------------
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;
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid state abbreviation 
//-----------------------------------------------------------------------------
function checkStateCode(theField, emptyOK) {
	if ( checkStateCode.arguments.length == 1 ) emptyOK = defaultEmptyOK;
	if ( (emptyOK == true) && (isEmpty(theField.value)) ) {
		return true;
	} else {
		theField.value = theField.value.toUpperCase();
		if ( ! isStateCode(theField.value, false) ) return warnInvalid(theField, iStateCode);
		else return true;
	}
}

//-----------------------------------------------------------------------------
// Reformats zip code into a consistent format
//-----------------------------------------------------------------------------
function reformatZIPCode(ZIPString) {
	if ( ZIPString.length == 5 ) return ZIPString;
	else return ( reformat (ZIPString, "", 5, "-", 4) );
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid US postal code 
//-----------------------------------------------------------------------------
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 warnInvalid (theField, iZIPCode);
		} else {
			// if you don't want to insert a hyphen, comment next line out
			theField.value = reformatZIPCode(normalizedZIP);
			return true;
		}
	}
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid international phone number
//-----------------------------------------------------------------------------
function checkInternationalPhone(theField, emptyOK) {
	if ( checkInternationalPhone.arguments.length == 1 ) emptyOK = defaultEmptyOK;
	if ( (emptyOK == true) && (isEmpty(theField.value)) ) { 
		return true;
	} else {
		var normalizedPhone = stripCharsInBag(theField.value, phoneNumberDelimiters);
		if ( !isInternationalPhoneNumber(normalizedPhone, false) ) return warnInvalid (theField, iWorldPhone);
		else return true;
	}
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid email address
//-----------------------------------------------------------------------------
function checkEmail(theField, emptyOK) {
	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;
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid two or four digit 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;
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid calendar year
//-----------------------------------------------------------------------------
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;
}

//-----------------------------------------------------------------------------
// Verifies input field is a valid credit card selection
//-----------------------------------------------------------------------------
function checkCreditCard(radio, theField) {
	var cardType = getRadioButtonValue(radio);
	
	if ( ! cardType ) {
		return warnInvalid(theField, iCreditCardType);
	}

	var normalizedCCN = stripCharsInBag(theField.value, creditCardDelimiters);
	if ( ! isCardMatch(cardType, normalizedCCN) ) 
		return warnInvalid(theField, iCreditCardPrefix + cardType + iCreditCardSuffix);
	else {
		theField.value = normalizedCCN;
		return true;
	}
}

//-----------------------------------------------------------------------------
// Validates format of the access code or offer code.  The access code should 
// consist of four alphabetic characters, followed by four digits, followed by 
// four alphabetic characters, followed by four digits.  The offer code should 
// consist of six alphanumeric characters.
//-----------------------------------------------------------------------------
function checkCode(code) {
	
	// verify example code has not been used
	if ( code.value == 'aaaa2222cccc4444' || code.value == 'a2cc4a' ) return warnInvalid(code, iCodeExample);
	
	// verify field length
	if ( ! (code.value.length == 16 || code.value.length == 6) ) return warnInvalid(code, iCode);

	// verify field format
	if ( ! ( isAlphabetic(code.value.substr(0,4)) && isPositiveInteger(code.value.substr(4,4)) && 
			 isAlphabetic(code.value.substr(8,4)) && isPositiveInteger(code.value.substr(12,4)) ) 
		&& ! ( isAlphanumeric(code.value) )	) {
		return warnInvalid(code, iCode);
	}
	
	return true;
}

//-----------------------------------------------------------------------------
// Validates HTML form and submits it to the server with the proper navigation
// parameter.
//-----------------------------------------------------------------------------
function checkForm(formObject, direction, isCode) {
	if ( direction == 'previous' || validate(formObject, isCode) ) {
		if ( direction == 'next' ) {
			formObject.direction.value = "Next Page";
		} else {
			if ( direction == 'previous' ) {
				formObject.direction.value = "Previous Page";
			}
		}
		formObject.submit();
	}
}

//-----------------------------------------------------------------------------
// Sets country select list on the given form to "United States" if a US state 
// is selected from the state select list.
//-----------------------------------------------------------------------------
function setCountryToUS(stateListObject, countryListObject) {
	var selectedIndex;

	if ( stateListObject && stateListObject.options ) {
		selectedIndex = stateListObject.selectedIndex;
		if ( stateListObject.options[selectedIndex].value &&
			USStateCodes.indexOf(stateListObject.options[selectedIndex].value) != -1 ) {
			countryListObject.options[1].selected = true;
		}
	}
}

