// GENERIC CODE FOR CLIENT_SIDE ERROR VALIDATION
//  Core error checking functions taken from Netscape developer site

// ********************************************************
// CONSTANT STRING DECLARATIONS
// ********************************************************

// Strings for field identifiers
var fieldID = new Array();
fieldID["default"] = "";
fieldID["lastName"] = "Last Name";
fieldID["lastname"] = "Last Name";
fieldID["firstName"] = "First Name";
fieldID["firstname"] = "First Name";
fieldID["shipName"] = "Ship-to Name";
fieldID["s_name"] = "Ship-to Name";
fieldID["billLastName"] = "Billing Last Name";
fieldID["card_Lname"] = "Billing Last Name";
fieldID["billFirstName"] = "Billing First Name";
fieldID["card_Fname"] = "Billing First Name";
fieldID["billMiddleName"] = "Billing Middle Name";
fieldID["card_Mname"] = "Billing Middle Name";
fieldID["addr1"] = "Address (line 1)";
fieldID["addr2"] = "Address (line 2)";
fieldID["billAddr1"] = "Billing Address (line 1)";
fieldID["address"] = "Billing Address (line 1)";
fieldID["billAddr2"] = "Billing Address (line 2)";
fieldID["suppadr"] = "Billing Address (line 2)";
fieldID["shipAddr1"] = "Shipping Address (line 1)";
fieldID["s_address"] = "Shipping Address (line 1)";
fieldID["shipAddr2"] = "Shipping Address (line 2)";
fieldID["s_suppadr"] = "Shipping Address (line 2)";
fieldID["city"] = "City";
fieldID["billCity"] = "Billing City";
fieldID["shipCity"] = "Shipping City";
fieldID["s_city"] = "Shipping City";
fieldID["state"] = "State";
fieldID["billState"] = "Billing State";
fieldID["shipState"] = "Shipping State";
fieldID["s_state"] = "Shipping State";
fieldID["zip"] = "ZIP Code";
fieldID["billZip"] = "Billing ZIP Code";
fieldID["shipZip"] = "Shipping ZIP Code";
fieldID["s_zip"] = "Shipping ZIP Code";
fieldID["phone"] = "Phone Number";
fieldID["s_phone"] = "Phone Number";
fieldID["dayPhone"] = "Daytime Phone Number";
fieldID["form_dphone"] = "Daytime Phone Number";
fieldID["evePhone"] = "Evening Phone Number";
fieldID["form_ephone"] = "Evening Phone Number";
fieldID["formEmail"] = "Email Address";
fieldID["form_email"] = "Email Address";
fieldID["emailMatch"] = "Re-typed Email address";
fieldID["confirm_email"] = "Re-typed Email address";
fieldID["ccNum"] = "Credit Card Number";
fieldID["card_number"] = "Credit Card Number";
fieldID["ccExpYr"] = "Credit Card Expiration Year";
fieldID["card_year"] = "Credit Card Expiration Year";
fieldID["ccExpMo"] = "Credit Card Expiration Month";
fieldID["card_month"] = "Credit Card Expiration Month";
fieldID["ccExpDate"] = "Credt Card Expiration Date";
fieldID["ccType"] = "Credit Card Type";
fieldID["userName"] = "User name";
fieldID["matchUname"] = "Re-typed username";
fieldID["login"] = "Login name";
fieldID["formPwd"] = "Password";
fieldID["formPassword"] = "Password";
fieldID["formNewPwd"] = "New Password";
fieldID["pwdMatch"] = "Re-typed password";
fieldID["acctNo"] = "Account Number";
fieldID["orderNo"] = "Order Number";
fieldID["name"] = "Name";
fieldID["email"] = "Email address";
fieldID["friendName1"] = "Friend Name 1";
fieldID["friendEmail1"] = "Friend email address 1";
fieldID["useMessage"] = "Use message";
fieldID["createMessage"] = "Create personal message";
fieldID["questionTxt"] = "Question text box";
fieldID["gender"] = "Gender";
fieldID["storyTxt"] = "Story Text";
fieldID["age"] = "Age";
fieldID["formVBGR"] = "Enter Secure Code";

// Error message strings indexed by error check
var errMsgs = new Array();
errMsgs["prefix"] = "The following errors were encountered when processing your information:\n";
errMsgs["linePrefix"] = "   >  ";
errMsgs["default"] = " - Please review the data in the indicated field for accuracy";
//errMsgs["isZIPCode"] = " - Please enter a 5- or 9-digit U.S. ZIP Code (like 94111).";
errMsgs["isZIPCode"] = " - Please enter a Canadian Zipcode like X9X 9X9.";
errMsgs["isUSPhoneNumber"] = " - Please enter a valid 10 digit U.S. phone number (like 415 555 1212).";
errMsgs["isEmail"] = " - Please enter a valid email address (like username@domain.com).";
errMsgs["checkCreditCard"] = " - Please check your credit card number and type for errors.";
errMsgs["isCreditCard"] = " - Your credit card number does not appear to be valid. Please check for errors.";
errMsgs["badMatch"] = " does not match ";
errMsgs["missingPwdMatch"] = " - Please re-type your password for verification.";
errMsgs["missingUnameMatch"] = " - Please re-type your username for verification.";
errMsgs["isNotSelected"] = " has not been selected. Please select a value.";
errMsgs["missingReqdField"] = " - Required field is empty";
errMsgs["missingField"] = " - Please enter data in the indicated field";
errMsgs["badExp"] = " - Your credit card appears to have expired. Check the expiration date you have entered";

// VARIABLE DECLARATIONS
var digits = "0123456789";
var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz";
var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var whitespace = " \t\n\r";
var decimalPointDelimiter = ".";
var phoneNumberDelimiters = ".()- ";
var validUSPhoneChars = digits + phoneNumberDelimiters;
var digitsInUSPhoneNumber = 10;
var ZIPCodeDelimiters = "-";
var validZIPCodeChars = digits + ZIPCodeDelimiters
var digitsInZIPCode1 = 5;
var digitsInZIPCode2 = 9;
var creditCardDelimiters = "- ";

// Styles for text and other elements
var errColor = "#ff0000";
var normalColor = "#737373";


// ERROR OBJECT
//  A generic object to hold information about client-side form errors
//   form: HTML form being validated (object)
//   reqVars: A string array of required variables
//   validateVars: An associative array of variables and the required validation checks
function CSErrorObj(form,checkVars,valVars,varList) {
	this.formObj = form;
	this.errorVars = new Array();
	this.varFlagged = new Array();
	this.errorStrings = new Array();
	this.numErrors = 0;
	this.numVarErrs = 0;
	
	// Initialize array of all variables in form
	for (var i = 0; i < this.formObj.elements.length; i++) {
		var name = this.formObj.elements[i].name;
		if (name != "") this.varFlagged[name] = false;
	}	
	
	// Array of required form variables and array to hold those missing
	if (arguments.length > 1) {
		this.checkVars = checkVars;
	} else {
		this.checkVars = new Array();
	}
	
	// Associative array containing required validation checks
	if (arguments.length > 2) {
		this.valVars = valVars;
	} else {
		this.valVars = new Array();
	}
	
	// Associative array containing variables to be tagged/untagged
	if (arguments.length > 3) {
		this.varList = varList;
	} else {
		this.varList = new Array();
		var count = 0;
		for (var i = 0; i < this.checkVars.length; i++) {
			this.varList[count] = this.checkVars[i];
			count++;
		}
		for (var i = 0; i < this.valVars.length; i++) {
			var varName = this.valVars[i];
			if (!this.checkVars[varName]) {
				this.varList[count] = this.checkVars[i];
				count++;
			}
		}
	}
	
	// Define methods to validate form data
	this.checkRequired = CheckRequiredFields;
	this.checkValues = CheckDataValues;
	this.checkAll = CheckAllData;
	this.errorsExist = ErrorsExist;
	this.flagError = FlagError;
	this.buildErrorString = BuildErrString;
	this.displayErrs = DisplayErrors;
}


// ***********************************************************************************
// BEGIN METHODS FOR CSERROROBJ

// Small wrapper function to determine if any errors were found
function ErrorsExist() {
	if (this.errorStrings.length > 0) {
		return true;
	} else {
		return false;
	}
}

// Function to display an error message
function DisplayErrors() {
	// Change styles of variable text for those variables containing errors
	// Initialize by reverting all text to normal color, and then add error
	//  color to all variables for which there are errors
	revertElms(this.varList);
	highlightElms(this.errorVars);
	
	// Alert box containing error string for all errors
	if (this.errorsExist()) {
		var errorString = this.buildErrorString();
		alert(errMsgs["prefix"] + errorString);
		return false;
	} else {
		return true;
	}	
}



// Method to flag a given variable name as having an error
function FlagError(varName,string) {
	if (!this.varFlagged[varName]) {
		this.errorVars[this.numVarErrs] = varName;
		this.varFlagged[varName] = true;
		this.numVarErrs++;
	}
	
	if (arguments.length > 1) {
		this.errorStrings[this.numErrors] = string;
		this.numErrors++;
	}
	return true;
}

// Method to check all required variables for omissions
//  Returns true if any required form variables missing
function CheckRequiredFields() {
	// Loop over all reqd variables and determine if they are empty
	for (var i = 0; i < this.checkVars.length; i++) {
		var varName = this.checkVars[i];
		var elmObj = this.formObj.elements[varName];
		if (elmObj) {
			var varValue = elmObj.value;
			var errString = "";		
			if (isWhitespace(varValue)) {			
				// Map form variable name to text and store in errors array			
				if (fieldID[varName] == null) {
					 errString = fieldID["default"] + errMsgs["missingReqdField"];
				} else {
					errString = fieldID[varName] + errMsgs["missingReqdField"];
				}					
				this.flagError(varName,errString);
			}
		}
	}
}

// Method to validate form data based on a list of validation functions
//  Returns true if there were any validation errors
function CheckDataValues() {
	// Loop over all variables requiring validation
	var counter = 0;
	for (var i in this.valVars) {
		var checks = this.valVars[i];
		var varName = i;
		var varValue = this.formObj.elements[varName].value;
		
		// Check to see whether the variable is empty - abort checks if so...
		if (isWhitespace(varValue)) continue;
		
		// Loop over all validation checks for a given variable		
		for (var j = 0; j < checks.length; j++) {
			var checkFnName = checks[j];
			var testResult = eval(checkFnName + "(\"" + varValue + "\")");
			if (!testResult) {		
				// Use default error message if none available for this function
				if (errMsgs[checkFnName] == null) errMsgs[checkFnName] = errMsgs["default"];
				var msg = fieldID[varName] + errMsgs[checkFnName];
				this.flagError(varName,msg);			
			}
		}
	}
}

// Wrapper method to perform both required field and validation checking
//  Return a string containing error messages
function CheckAllData() {
	// Check for required field errors
	this.checkRequired();	
		
	// Check for data validation errors
	this.checkValues();		
	return this.errorsExist();
}

// Method to build a simple bulleted list of variable errors
function BuildErrString() {
	var errString = "";	
	for (var i = 0; i < this.errorStrings.length; i++) {
		errString += errMsgs["linePrefix"] + this.errorStrings[i] + "\n";
	}	
	return errString;
}

// END METHODS FOR CSERROROBJ
// ************************************************************************



// ********************************************************
// BEGIN CORE ERROR CHECKING 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;
    if (isEmpty(s)) return true;

    for (i = 0; i < s.length; i++) {   
        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 = "";
    for (i = 0; i < s.length; i++) {   
        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 = "";
    for (i = 0; i < s.length; i++) {   
        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)
}

// 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 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.
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;
    }

    // All characters are numbers.
    return true;
}


// 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);

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

    // All characters are letters.
    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);

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

    // All characters are numbers or letters.
    return true;
}

// isUSPhoneNumber (STRING s [, BOOLEAN emptyOK])
function isUSPhoneNumber (s) {
	if (isEmpty(s)) 
       if (isUSPhoneNumber.arguments.length == 1) return defaultEmptyOK;
       else return (isUSPhoneNumber.arguments[1] == true);
	   var s2 = stripCharsInBag(s,phoneNumberDelimiters);
    return (isInteger(s2) && s2.length == digitsInUSPhoneNumber);
}

// isZIPCode (STRING s [, BOOLEAN emptyOK])
/*
function isZIPCode (s) {
	s = stripWhitespace(s);
	if (isEmpty(s)) 
       if (isZIPCode.arguments.length == 1) return defaultEmptyOK;
       else return (isZIPCode.arguments[1] == true);
	var s2 = stripCharsInBag(s, ZIPCodeDelimiters);
	return (isInteger(s2) && 
            ((s2.length == digitsInZIPCode1) ||
             (s2.length == digitsInZIPCode2)));
}
*/

function isZIPCode(strValue) {
/************************************************
 * DESCRIPTION: Validates US & Canadian zipcoces
 * US Format:  99999 or 99999-9999
 * Canadian Format:  X9X #X#
 *
 * PARAMETERS:
 *    strValue - String to be tested for validity
 *
 *    RETURNS:
 *       True if valid, otherwise false.
 *
 *       *************************************************/
  var objRegExp  = /(^\d{5}(-\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$)/;

  //check for valid US Zipcode
  return objRegExp.test(strValue);
  }


// isEmail (STRING s [, BOOLEAN emptyOK])
function isEmail2 (s) {
	if (isEmpty(s)) 
       if (isEmail.arguments.length == 1) return defaultEmptyOK;
       else return (isEmail.arguments[1] == true);
   
    if (isWhitespace(s)) return false;
    
	var i = 1;
    var sLength = s.length;

    // look for @
    while ((i < sLength) && (s.charAt(i) != "@")) {
		i++;
    }

    if ((i >= sLength) || (s.charAt(i) != "@")) return false;
    else i += 2;

    // look for .
    while ((i < sLength) && (s.charAt(i) != ".")) {
		i++;
    }

    // there must be at least one character after the .
    if ((i >= sLength - 1) || (s.charAt(i) != ".")) return false;
    else return true;
}

function wasEmail(str) {
  // are regular expressions supported?
  var supported = 0;
  if (window.RegExp) {
    var tempStr = "a";
    var tempReg = new RegExp(tempStr);
    if (tempReg.test(tempStr)) supported = 1;
  }
  if (!supported) 
    return (str.indexOf(".") > 2) && (str.indexOf("@") > 0);
  var r1 = new RegExp("(@.*@)|(\\.\\.)|(@\\.)|(^\\.)");
  var r2 = new RegExp("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$");
  return (!r1.test(str) && r2.test(str));
}

function isEmail(emailStr) {

emailStr = emailStr.toLowerCase(); // lowercase email, so comp with domain will match

var checkTLD=1;
var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/;
var emailPat=/^(.+)@(.+)$/;
var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
var validChars="\[^\\s" + specialChars + "\]";
var quotedUser="(\"[^\"]*\")";
var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
var atom=validChars + '+';
var word="(" + atom + "|" + quotedUser + ")";
var userPat=new RegExp("^" + word + "(\\." + word + ")*$");
var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");
var matchArray=emailStr.match(emailPat);

if (matchArray==null) {
	return false;
	}
var user=matchArray[1];
var domain=matchArray[2];

for (i=0; i<user.length; i++) {
	if (user.charCodeAt(i)>127) {
		return false;
		}
	}
for (i=0; i<domain.length; i++) {
	if (domain.charCodeAt(i)>127) {
		return false;
		}
	}
if (user.match(userPat)==null) {
	return false;
	}

var IPArray=domain.match(ipDomainPat);
if (IPArray!=null) {
	for (var i=1;i<=4;i++) {
		if (IPArray[i]>255) {
			return false;
			}
		}
	return true;
	}

var atomPat=new RegExp("^" + atom + "$");
var domArr=domain.split(".");
var len=domArr.length;
for (i=0;i<len;i++) {
	if (domArr[i].search(atomPat)==-1) {
		return false;
		}
	}

if (checkTLD && domArr[domArr.length-1].length!=2 && domArr[domArr.length-1].search(knownDomsPat)==-1) {
	return false;
	}

if (len<2) {
	return false;
	}

return true;
}

// 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
}

// Determine if credit card passes Lunh mod-10 test 
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);

}

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

// Determine if CC is a MasterCard
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;
}

// Determine if CC is an AMEX
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;
}


//  Determine if CC matches a certain 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;
}

// Function to check a credit card number
function checkCreditCard(selectObj, ccNum) {
	var cardType = getSelectedValue(selectObj);
    var normalizedCCN = stripCharsInBag(ccNum, creditCardDelimiters);
    if (!isCardMatch(cardType, normalizedCCN)) {
		return false;
	} else {
		return true;
    }
}

// Determine if two form fields match
//   Used for elements that must be re-typed
function fieldsMatch(val1,val2) {
	if (isWhitespace(val1) || isWhitespace(val2)) {
		return false;
	} else {
		if (!stringsMatch(val1,val2)) {
			return false;
		} else {
			return true;
		}
	}
}

// Determine if two strings match identically
function stringsMatch(s1,s2) {
	if (s1 === s2) {
		return true;
	} else {
		return false;
	}
}

// Determines if a SELECT menu has a value
//  Requires a menu structured as follows:
//    	<SELECT name="foobar">
//			<OPTION selected="selected">Choose a state...</OPTION>
//			<OPTION value="val1">Value 1</OPTION>
//				...
//		</SELECT>
function isSelected(selectObj) {
	if (selectObj.selectedIndex != 0) {
		return true;
	} else {
		return false;
	}
}

// Determines if radio button is selected
function radioIsSelected(radioObj) {
	for (var i = 0; i < radioObj.length; i++) {
		if (radioObj[i].checked) {
			return true;
		}
	} 
	return false;
}

// Utility function to get the selected value within a select menu
function getSelectedValue(selectObj) {
	var idx = selectObj.selectedIndex;
	return selectObj.options[idx].value;
}

// Function to revert form element text to default color
//  Text associated with form elements must have an id
//  tag that corresponds to the error raised via flagErrors(id,msg)
function revertElms(elmList) {
	if (document.getElementById) {
		for (var i = 0; i < elmList.length; i++) {
			var txtObj = document.getElementById(elmList[i]);
			txtObj.style.color = normalColor;
		}
	}
}

// Function to change form element text to "error" color
function highlightElms(elmList) {
	if (document.getElementById) {
		for (var i = 0; i < elmList.length; i++) {		
			var txtObj = document.getElementById(elmList[i]);
			txtObj.style.color = errColor;
		}
	}
}

// Function to format a date string
function format_date(numMonth,numYear) {
	monthadv = "JanFebMarAprMayJunJulAugSepOctNovDec";
	maxdays = "312831303130313130313031";
	dtOffset = (numMonth - 1) * 3;
	strDate = monthadv.substr(dtOffset,3) + " ";
	
	dyOffset = (numMonth - 1) * 2;
	strDate += maxdays.substr(dyOffset,2) + ", " + numYear;
	return strDate;
}

